【Tomcat】二.Tomcat启动初始化Boostrap

作者: Weidan 分类: Tomcat 发布时间: 2020-04-19

零.Bootstrap初始化

Bootstrap 可以看成是一个 TomcatServer 运行时需要的环境的准备,这里通过加载配置文件,设置全局数据以便让后面的 组件 初始化的时候可以使用到。

一.获取Tomcat运行的配置目录

那么首先是在 static代码块 中获取到运行环境的目录基础:

public final class Bootstrap {
  private static final Log log = LogFactory.getLog(Bootstrap.class);

    /**
     * Daemon object used by main.
     */
    private static final Object daemonLock = new Object();
    // 声明一个Bootstrap静默线程的对象引用,main方法将会用到
    private static volatile Bootstrap daemon = null;
    private static final File catalinaBaseFile;
    private static final File catalinaHomeFile;
    private static final Pattern PATH_PATTERN = Pattern.compile("(\".*?\")|(([^,])*)");

    static {
        // 获取当前运行的绝对路径
        String userDir = System.getProperty("user.dir");
        // 获取catalina.home的值
        String home = System.getProperty(Globals.CATALINA_HOME_PROP);
        File homeFile = null;
        if (home != null) {
            File f = new File(home);
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }
        // 两次尝试获取homeFile的值,不过刚开始我们就配置了,所以这两个判断直接跳过
        if (homeFile == null) {
            File bootstrapJar = new File(userDir, "bootstrap.jar");
            if (bootstrapJar.exists()) {
                File f = new File(userDir, "..");
                try {
                    homeFile = f.getCanonicalFile();
                } catch (IOException ioe) {
                    homeFile = f.getAbsoluteFile();
                }
            }
        }
        if (homeFile == null) {
            // Second fall-back. Use current directory
            File f = new File(userDir);
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }

        // 存储到Boostrap的静态变量中.
        catalinaHomeFile = homeFile;
        System.setProperty(
                Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());

        // 然后获取catalina.base的值,也同样存储到System属性中
        String base = System.getProperty(Globals.CATALINA_BASE_PROP);
        if (base == null) {
            catalinaBaseFile = catalinaHomeFile;
        } else {
            File baseFile = new File(base);
            try {
                baseFile = baseFile.getCanonicalFile();
            } catch (IOException ioe) {
                baseFile = baseFile.getAbsoluteFile();
            }
            catalinaBaseFile = baseFile;
        }
        System.setProperty(
                Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
    }
  // .......省略其他代码
}

二.读取运行参数

既然第一步已经初始化好了运行时所需要使用的 文件路径,那么接下来肯定就是读取配置并且根据配置来初始化项目了。这一步就直接在 main 方法中进行调用:

public final class Bootstrap {
      public static void main(String args[]) {
        // 加锁初始化 Boostrap 的静默引用
        synchronized (daemonLock) {
            if (daemon == null) {
                // Don't set daemon until init() has completed
                Bootstrap bootstrap = new Bootstrap();
                try {
                    bootstrap.init();
                } catch (Throwable t) {
                    handleThrowable(t);
                    t.printStackTrace();
                    return;
                }
                // 持有引用
                daemon = bootstrap;
            } else {
                Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
            }
        }

        // 根据运行的命令做相对应的操作
        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }

            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();
            } else if (command.equals("start")) {
                // init初始化以后就需要进入到这里来启动Tomcat服务
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else if (command.equals("configtest")) {
                daemon.load(args);
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
                System.exit(0);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t) {
            // Unwrap the Exception for clearer error reporting
            if (t instanceof InvocationTargetException &&
                    t.getCause() != null) {
                t = t.getCause();
            }
            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }
    }
}

2.1 Bootstrap初始化

包括初始化自定义的 ClassLoader 以及 Catalina 的准备工作。

public void init() throws Exception {
  // 初始化三个ClassLoader
  initClassLoaders();
  // 将当前线程的ClassLoader设置成Catalina的Loader
  Thread.currentThread().setContextClassLoader(catalinaLoader);

  SecurityClassLoad.securityClassLoad(catalinaLoader);

  // 通过反射,做 Catalina 的准备工作
  if (log.isDebugEnabled())
    log.debug("Loading startup class");
  Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
  Object startupInstance = startupClass.getConstructor().newInstance();

  // 将 sharedLoader 设置成 Catalina 的父级 ClassLoader
  if (log.isDebugEnabled())
    log.debug("Setting startup class properties");
  String methodName = "setParentClassLoader";
  Class<?> paramTypes[] = new Class[1];
  paramTypes[0] = Class.forName("java.lang.ClassLoader");
  Object paramValues[] = new Object[1];
  paramValues[0] = sharedLoader;
  Method method =
    startupInstance.getClass().getMethod(methodName, paramTypes);
  method.invoke(startupInstance, paramValues);

  catalinaDaemon = startupInstance;
}

首先需要初始化三个 ClassLoader(但其实到最后三个都指向 commonLoader):

private void initClassLoaders() {
  try {
    commonLoader = createClassLoader("common", null);
    if (commonLoader == null) {
      // no config file, default to this loader - we might be in a 'single' env.
      commonLoader = this.getClass().getClassLoader();
    }
    catalinaLoader = createClassLoader("server", commonLoader);
    sharedLoader = createClassLoader("shared", commonLoader);
  } catch (Throwable t) {
    handleThrowable(t);
    log.error("Class loader creation threw exception", t);
    System.exit(1);
  }
}

那么我们现在就有这些 ClassLoader

每一层加载 Class 的时候, Loader 就都会说:你去找你爷去,找到最后没有找到,才退下来一层,如果找到了就返回,到最后都没有找到就会抛出 ClassNotFoundException

所以这种方式可以很好的防止我们使用一个同样的类,对 Tomcat 造成破坏。

根据 catalina-home/conf/catalina.properties 的配置创建初始化 ClassLoader。我们使用的是默认配置:

common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
server.loader=
shared.loader=

由于只有 common.loader 配置了规则,所以下面两个在创建的时候由于没有配置,直接将 commonClassLoader 返回了。

private ClassLoader createClassLoader(String name, ClassLoader parent)
  throws Exception {
  // 通过读取 catalina-home/conf/catalina.properties 的配置,
  // 如果没有配置,则返回直接返回父级的ClassLoader
  String value = CatalinaProperties.getProperty(name + ".loader");
  // 默认情况下,shared和Catalina的ClassLoader都没有配置,直接返回了CommonLoader实例
  if ((value == null) || (value.equals("")))
    return parent;

  value = replace(value);

  // 在加载CommonLoader的时候加载了 lib 文件夹下的所有jar包.
  List<Repository> repositories = new ArrayList<>();
  String[] repositoryPaths = getPaths(value);
  // 循环 catalina-home/catalina.properties 配置的规则的路径,
  // 使用 URL 加载 jar 包
  for (String repository : repositoryPaths) {
    // Check for a JAR URL repository
    try {
      @SuppressWarnings("unused")
      URL url = new URL(repository);
      repositories.add(new Repository(repository, RepositoryType.URL));
      continue;
    } catch (MalformedURLException e) {
      // Ignore
    }

    // Local repository
    if (repository.endsWith("*.jar")) {
      repository = repository.substring
        (0, repository.length() - "*.jar".length());
      repositories.add(new Repository(repository, RepositoryType.GLOB));
    } else if (repository.endsWith(".jar")) {
      repositories.add(new Repository(repository, RepositoryType.JAR));
    } else {
      repositories.add(new Repository(repository, RepositoryType.DIR));
    }
  }

  return ClassLoaderFactory.createClassLoader(repositories, parent);
}

更进一步的就不打算走下去了,只要知道目前 commonClassLoader 加载了 lib 下的所有包就可以了。

2.2 Bootstrap启动

加载完 jar 包目录,接下来就是启动了,在 main 方法里面我们可以直接看这几句话:

else if (command.equals("start")) {
  // init初始化以后就需要进入到这里来启动Tomcat服务
  daemon.setAwait(true);
  daemon.load(args);
  daemon.start();
  if (null == daemon.getServer()) {
    System.exit(1);
  }
}

首先,设置 Catalina 进行等候:

public void setAwait(boolean await)
        throws Exception {

  Class<?> paramTypes[] = new Class[1];
  paramTypes[0] = Boolean.TYPE;
  Object paramValues[] = new Object[1];
  paramValues[0] = Boolean.valueOf(await);
  Method method =
    catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
  method.invoke(catalinaDaemon, paramValues);
}

我们可以看到 Bootstrap 设置 Catalina 的时候,一直都是通过反射的形式调用,这是因为 Boostrap 要保证 Catalina 类和 XML读取的相关工具 在不同的 ClassLoader 中,从而可以保证 Catalina 加载的我们 war 项目不可以访问到 Tomcat的关键类

Catalina:蛮好听的名字,查了一下百度资料,Catalina 是美国西海岸靠近洛杉矶22英里的一个小岛,因为其风景秀丽而著名。 Servlet 运行模块的最早开发者 Craig McClanahan 因为喜欢 Catalina岛 故以 Catalina 命名他所开这个模块,尽管他从来也没有去过那里。

另外在开发的早期阶段,Tomcat是被搭建在一个叫 Avalon 的服务器框架上,而 Avalon 则是 Catalina 岛上的一个小镇的名字,于是想一个与小镇名字相关联的单词也是自然而然。还有一个原因来自于 Craig McClanahan 养的猫,他养的猫在他写程序的时候喜欢在电脑周围闲逛。

那就想象成 Bootstrap 把这个岛建立起来了!。

那第二句 daemon.load(args); 也是通过反射调用,代码就不贴了,直接进入 Catalina.class#load()

三.Catalina加载配置

public class Catalina {

  public void load() {
    // 装配 server.xml 的配置
    if (loaded) {
      return;
    }
    loaded = true;
    long t1 = System.nanoTime();
    initDirs();
    initNaming();
    Digester digester = createStartDigester();
    InputSource inputSource = null;
    InputStream inputStream = null;
    File file = null;
    try {
      try {
        file = configFile();
        inputStream = new FileInputStream(file);
        inputSource = new InputSource(file.toURI().toURL().toString());
      } catch (Exception e) {
        if (log.isDebugEnabled()) {
          log.debug(sm.getString("catalina.configFail", file), e);
        }
      }
      if (inputStream == null) {
        try {
          inputStream = getClass().getClassLoader()
            .getResourceAsStream(getConfigFile());
          inputSource = new InputSource
            (getClass().getClassLoader()
             .getResource(getConfigFile()).toString());
        } catch (Exception e) {
          if (log.isDebugEnabled()) {
            log.debug(sm.getString("catalina.configFail",
                                   getConfigFile()), e);
          }
        }
      }

      // This should be included in catalina.jar
      // Alternative: don't bother with xml, just create it manually.
      if (inputStream == null) {
        try {
          inputStream = getClass().getClassLoader()
            .getResourceAsStream("server-embed.xml");
          inputSource = new InputSource
            (getClass().getClassLoader()
             .getResource("server-embed.xml").toString());
        } catch (Exception e) {
          if (log.isDebugEnabled()) {
            log.debug(sm.getString("catalina.configFail",
                                   "server-embed.xml"), e);
          }
        }
      }


      if (inputStream == null || inputSource == null) {
        if  (file == null) {
          log.warn(sm.getString("catalina.configFail",
                                getConfigFile() + "] or [server-embed.xml]"));
        } else {
          log.warn(sm.getString("catalina.configFail",
                                file.getAbsolutePath()));
          if (file.exists() && !file.canRead()) {
            log.warn("Permissions incorrect, read permission is not allowed on the file.");
          }
        }
        return;
      }

      try {
        inputSource.setByteStream(inputStream);
        digester.push(this);
        digester.parse(inputSource);
      } catch (SAXParseException spe) {
        log.warn("Catalina.start using " + getConfigFile() + ": " +
                 spe.getMessage());
        return;
      } catch (Exception e) {
        log.warn("Catalina.start using " + getConfigFile() + ": " , e);
        return;
      }
    } finally {
      if (inputStream != null) {
        try {
          inputStream.close();
        } catch (IOException e) {
          // Ignore
        }
      }
    }
    // 保存Server的一些信息:
    getServer().setCatalina(this);
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

    // 将日志输出流从默认的 System.out 替换成 Tomcat 自己的实现.
    initStreams();

    // 开始初始化 Server,也就是最大的一层。
    try {
      getServer().init();
    } catch (LifecycleException e) {
      if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
        throw new java.lang.Error(e);
      } else {
        log.error("Catalina.start", e);
      }
    }

    long t2 = System.nanoTime();
    if(log.isInfoEnabled()) {
      log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
    }
  }

}

看人家配置解析,Emm还是蛮无聊的,直接跳过去了。只要知道是根据 server.xml 解析创建对象就可以了,在这个过程中,实例化了 server 对象。

那么接下来就需要初始化里面的内容了,我们大概还记得这个图:

getServer().init(); 相当于初始化 TomcatServer

我们知道一个 Service 包含了 ConnectorEngine,所以初始化肯定涉及这两个鬼:

public class StandardService extends LifecycleMBeanBase implements Service {

  @Override
  protected void initInternal() throws LifecycleException {

    super.initInternal();

    // 初始化Engine
    if (engine != null) {
      engine.init();
    }

    // 默认是空的执行器
    for (Executor executor : findExecutors()) {
      if (executor instanceof JmxEnabled) {
        ((JmxEnabled) executor).setDomain(getDomain());
      }
      executor.init();
    }

    // 初始化监听器
    mapperListener.init();

    // 初始化Connector
    synchronized (connectorsLock) {
      for (Connector connector : connectors) {
        try {
          connector.init();
        } catch (Exception e) {
          String message = sm.getString(
            "standardService.connector.initFailed", connector);
          log.error(message, e);

          if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
            throw new LifecycleException(message);
        }
      }
    }
  }

}

四.StandardServer初始化

4.1 lifecycle生命周期转换

StandardServer 就需要先看看 Lifecycle 接口了:

这个接口规范了在 Tomcat 运行期间的 生命周期函数。只要跟 Tomcat 一起运行的,就会有这些 生命周期

与此同时还有个 LifecycleBase 实现了基本的生命周期转换:

public abstract class LifecycleBase implements Lifecycle {

  @Override
  public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
      invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }

    try {
      setStateInternal(LifecycleState.INITIALIZING, null, false);
      initInternal();
      setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
      handleSubClassException(t, "lifecycleBase.initFail", toString());
    }
  }

  // 子类实现这个即可实现转换
  protected abstract void initInternal() throws LifecycleException;

}

4.2 StandardServer初始化

public final class StandardServer extends LifecycleMBeanBase implements Server {


  @Override
  protected void initInternal() throws LifecycleException {

    super.initInternal();

    // String注册
    onameStringCache = register(new StringCache(), "type=StringCache");

    // 注册MBeanFactory,用来创建
    MBeanFactory factory = new MBeanFactory();
    factory.setContainer(this);
    onameMBeanFactory = register(factory, "type=MBeanFactory");

    // 初始化全局组件名字的组件
    globalNamingResources.init();

    if (getCatalina() != null) {
      ClassLoader cl = getCatalina().getParentClassLoader();
      // 加载 shared 和 common ClassLoader 的Jar包,一直到 SystemClassLoader
      while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
        if (cl instanceof URLClassLoader) {
          URL[] urls = ((URLClassLoader) cl).getURLs();
          for (URL url : urls) {
            if (url.getProtocol().equals("file")) {
              try {
                File f = new File (url.toURI());
                if (f.isFile() &&
                    f.getName().endsWith(".jar")) {
                  ExtensionValidator.addSystemResource(f);
                }
              } catch (URISyntaxException e) {
                // Ignore
              } catch (IOException e) {
                // Ignore
              }
            }
          }
        }
        cl = cl.getParent();
      }
    }
    // 初始化Service
    for (int i = 0; i < services.length; i++) {
      services[i].init();
    }
  }

}

加载了一些 Jar 包,然后就初始化我们最熟悉的 Service

五.StandardService初始化

同样的实现了 Lifecycle 接口,所以我们只需要关注 initInternal 即可。那我们知道 Service 是包含一个 Engine接收数据的 Connector 的。所以他的初始化主要是关注这两者:

public class StandardService extends LifecycleMBeanBase implements Service {
  @Override
  protected void initInternal() throws LifecycleException {

    super.initInternal();

    // 初始化Engine
    if (engine != null) {
      engine.init();
    }

    // 默认是空的执行器
    for (Executor executor : findExecutors()) {
      if (executor instanceof JmxEnabled) {
        ((JmxEnabled) executor).setDomain(getDomain());
      }
      executor.init();
    }

    // 初始化监听器
    mapperListener.init();

    // 初始化Connector
    synchronized (connectorsLock) {
      for (Connector connector : connectors) {
        try {
          connector.init();
        } catch (Exception e) {
          String message = sm.getString(
            "standardService.connector.initFailed", connector);
          log.error(message, e);

          if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
            throw new LifecycleException(message);
        }
      }
    }
  }
}

六.Engine初始化

Engine 默认实现是 StandardEngine

public class StandardEngine extends ContainerBase implements Engine {

  @Override
  protected void initInternal() throws LifecycleException {
    // Realm,一个认证的东西,这里保证加载成功
    getRealm();
    super.initInternal();
  }

}

来瞄一下配置文件:

<Server>  
  <!-- 省略 -->
    <Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" secretRequired=""/>
    <Engine name="Catalina" defaultHost="localhost">
        <!-- 这是一个混合的Realm认证,可以包含其他的 Realm -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t "%r" %s %b" />
      </Host>
    </Engine>
  </Service>
</Server>

LockOutRealm 是一个可以组合子 Realm 的类,规定了在一定时间内 用户鉴权出错次数,超出次数则会返回错误。不过项目中一般不用这个。

接下来 super.initInternal() ,父类不是 LifecycleBase 了,而是另外一个类:ContainerBase

public abstract class ContainerBase extends LifecycleMBeanBase
        implements Container {
  @Override
  protected void initInternal() throws LifecycleException {
    BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
    startStopExecutor = new ThreadPoolExecutor(
      getStartStopThreadsInternal(),
      getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
      startStopQueue,
      new StartStopThreadFactory(getName() + "-startStop-"));
    startStopExecutor.allowCoreThreadTimeOut(true);
    super.initInternal();
  }
}

这就是初始化线程池实例以便后续可以让项目可以并行执行部署。

七.MapperListener初始化

这个容器主要处理 HOST 以及 URI 映射的 Servlet。初始化没做什么事情,只是注册了个名字,先跳过不看。

八.Connector初始化

public class Connector extends LifecycleMBeanBase  { 
  @Override
  protected void initInternal() throws LifecycleException {

    super.initInternal();

    // Initialize adapter
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);

    // Make sure parseBodyMethodsSet has a default
    if (null == parseBodyMethodsSet) {
      // 设置需要解析消息体的HTTPMethod
      setParseBodyMethods(getParseBodyMethods());
    }

    if (protocolHandler.isAprRequired() && !AprLifecycleListener.isInstanceCreated()) {
      throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",
                                                getProtocolHandlerClassName()));
    }
    if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
      throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",
                                                getProtocolHandlerClassName()));
    }
    if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
        protocolHandler instanceof AbstractHttp11JsseProtocol) {
      AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
        (AbstractHttp11JsseProtocol<?>) protocolHandler;
      if (jsseProtocolHandler.isSSLEnabled() &&
          jsseProtocolHandler.getSslImplementationName() == null) {
        // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
        jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
      }
    }

    try {
      // 初始化协议处理器:Http11NIOProtocol
      protocolHandler.init();
    } catch (Exception e) {
      throw new LifecycleException(
        sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
    }
  }

  public void setParseBodyMethods(String methods) {

    HashSet<String> methodSet = new HashSet<>();

    if (null != methods) {
      methodSet.addAll(Arrays.asList(methods.split("\\s*,\\s*")));
    }

    if (methodSet.contains("TRACE")) {
      throw new IllegalArgumentException(sm.getString("coyoteConnector.parseBodyMethodNoTrace"));
    }

    this.parseBodyMethods = methods;
    this.parseBodyMethodsSet = methodSet;
    setProperty("parseBodyMethods", methods);
  }
}

九.Http11NIOProtocol初始化

Http11NIOProtocol 是一个 HTTP 1.1 的解析器,所以他需要负责 RequestResponse 的解析,所以,这里又通过一个 Endpoint 组件来做,所以需要初始化 Endpont

public abstract class AbstractProtocol<S> implements ProtocolHandler, MBeanRegistration {
  @Override
  public void init() throws Exception {
    if (getLog().isInfoEnabled()) {
      getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
    }

    if (oname == null) {
      oname = createObjectName();
      if (oname != null) {
        Registry.getRegistry(null, null).registerComponent(this, oname, null);
      }
    }

    if (this.domain != null) {
      // 与域名绑定解析器
      rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
      Registry.getRegistry(null, null).registerComponent(
        getHandler().getGlobal(), rgOname, null);
    }

    // 获取接口名字并初始化,接口在这里的意思是管理Socket数据进出的意思
    String endpointName = getName();
    // Endpoint在解析server.xml被初始化,现在是设置一些数据
    endpoint.setName(endpointName.substring(1, endpointName.length()-1));
    endpoint.setDomain(domain);

    endpoint.init();
  }
}
public abstract class AbstractJsseEndpoint<S> extends AbstractEndpoint<S> {

  @Override
  public void init() throws Exception {
    testServerCipherSuitesOrderSupport();
    super.init();
  }

  private void testServerCipherSuitesOrderSupport() {
    // JDK8下不支持使用SSLImplementation实现类,估计是修复Bug时需要hhh
    if(!JreCompat.isJre8Available() &&
       !OpenSSLImplementation.class.getName().equals(getSslImplementationName())) {
      for (SSLHostConfig sslHostConfig : sslHostConfigs.values()) {
        if (sslHostConfig.getHonorCipherOrder() != null) {
          throw new UnsupportedOperationException(
            sm.getString("endpoint.jsse.cannotHonorServerCipherOrder"));
        }
      }
    }
  }

}
public abstract class AbstractEndpoint<S> {


  public void init() throws Exception {
    if (bindOnInit) {
      bind();
      bindState = BindState.BOUND_ON_INIT;
    }
    if (this.domain != null) {
      // Register endpoint (as ThreadPool - historical name)
      oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
      Registry.getRegistry(null, null).registerComponent(this, oname, null);

      ObjectName socketPropertiesOname = new ObjectName(domain +
                                                        ":type=ThreadPool,name=\"" + getName() + "\",subType=SocketProperties");
      socketProperties.setObjectName(socketPropertiesOname);
      Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);

      for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
        registerJmx(sslHostConfig);
      }
    }
  }


}
public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {

  /**
    * Initialize the endpoint.
    */
  @Override
  public void bind() throws Exception {

    // 使用NIO的类库打开绑定端口
    if (!getUseInheritedChannel()) {
      serverSock = ServerSocketChannel.open();
      socketProperties.setProperties(serverSock.socket());
      InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
      serverSock.socket().bind(addr,getAcceptCount());
    } else {
      // ...
    }
    serverSock.configureBlocking(true); //mimic APR behavior

    // Initialize thread count defaults for acceptor, poller
    if (acceptorThreadCount == 0) {
      // FIXME: Doesn't seem to work that well with multiple accept threads
      acceptorThreadCount = 1;
    }
    if (pollerThreadCount <= 0) {
      //minimum one poller thread
      pollerThreadCount = 1;
    }
    setStopLatch(new CountDownLatch(pollerThreadCount));

    // Initialize SSL if needed
    initialiseSsl();

    selectorPool.open();
  }
}

关于 NIO内核 后面再详细了解了。

一路跳出来,Catalinaload() 方法就走完了,至于 start() 丢下一篇。