ServletWebServerApplicationContext创建Web容器Tomcat示例

目录
  • 正文
  • 创建Web服务
  • 一、获取Web服务器工厂
    • 1.1 选择导入Web工厂
  • 二、getWebServer:获取Web服务
    • 2.1 创建TomcatEmbeddedContext
    • 2.2. 创建TomcatWebServer
      • 2.2.1 启动Tomcat
    • 初始化小结
      • startInternal:启动Internal
      • NamingResources启动
      • Service启动
      • 启动Engine
      • 启动TomcatEmbeddedContext
      • 启动Executor
      • 启动MapperListener
      • 启动Connector
    • 启动总结
  • 三、注册Bean生命周期
    • 3.1 WebServerStartStopLifecycle(Web服务器启动-停止生命周期)
      • 3.1.1 addPreviouslyRemovedConnectors:启动Connector
  • 四、初始化上下文环境

正文

ServletWebServerApplicationContext实现了父类AbstractApplicationContext的onRefresh模板方法,在这里进行了拓展创建了Web容器。

@Override
protected void onRefresh() {
   super.onRefresh();
   try {
      createWebServer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
   }
}

创建Web服务

private void createWebServer() {
   WebServer webServer = this.webServer;
   ServletContext servletContext = getServletContext();
   if (webServer == null && servletContext == null) {
      //一、获取Web服务器工厂
      ServletWebServerFactory factory = getWebServerFactory();
      //二、获取Web服务
      this.webServer = factory.getWebServer(getSelfInitializer());
      //三、注册Bean生命周期(在容器启动和销毁时调用)
      getBeanFactory().registerSingleton("webServerGracefulShutdown",
            new WebServerGracefulShutdownLifecycle(this.webServer));
      getBeanFactory().registerSingleton("webServerStartStop",
            new WebServerStartStopLifecycle(this, this.webServer));
   }
   else if (servletContext != null) {
      try {
         getSelfInitializer().onStartup(servletContext);
      }
      catch (ServletException ex) {
         throw new ApplicationContextException("Cannot initialize servlet context", ex);
      }
   }
   //四、初始化上下文环境
   initPropertySources();
}

一、获取Web服务器工厂

protected ServletWebServerFactory getWebServerFactory() {
   // Use bean names so that we don't consider the hierarchy
   //获取Web服务器工厂名称
   String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
   if (beanNames.length == 0) {
      throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
            + "ServletWebServerFactory bean.");
   }
   if (beanNames.length > 1) {
      throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
            + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
   }
   //从容器中获取Web服务器工厂实例
   return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

这里的Web服务器工厂是通过ServletWebServerFactoryAutoConfiguration自动配置类导入进来的。

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
//Web启动环境
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
//2.1导入Web工厂
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
      ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
      ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
      ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
   //导入Web服务器工厂自定义程序
   @Bean
   public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
      return new ServletWebServerFactoryCustomizer(serverProperties);
   }
   //如果是Tomcat则导入Tomcat自定义程序
   @Bean
   @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
   public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
         ServerProperties serverProperties) {
      return new TomcatServletWebServerFactoryCustomizer(serverProperties);
   }
   @Bean
   @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
   @ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
   public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
      ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
      FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
      registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
      registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
      return registration;
   }
   /**
    * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
    * {@link ImportBeanDefinitionRegistrar} for early registration.
    */
   public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
      private ConfigurableListableBeanFactory beanFactory;
      @Override
      public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
         if (beanFactory instanceof ConfigurableListableBeanFactory) {
            this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
         }
      }
      @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
            BeanDefinitionRegistry registry) {
         if (this.beanFactory == null) {
            return;
         }
         registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
               WebServerFactoryCustomizerBeanPostProcessor.class);
         registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
               ErrorPageRegistrarBeanPostProcessor.class);
      }
      private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
         if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
            beanDefinition.setSynthetic(true);
            registry.registerBeanDefinition(name, beanDefinition);
         }
      }
   }
}

1.1 选择导入Web工厂

@Configuration
class ServletWebServerFactoryConfiguration {
    ServletWebServerFactoryConfiguration() {
    }
    //1.如果容器中有Servlet,Undertow,SslClientAuthMode就会创建Undertow工厂
    @Configuration
    @ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedUndertow {
        public EmbeddedUndertow() {
        }
        @Bean
        public UndertowServletWebServerFactory undertowServletWebServerFactory() {
            return new UndertowServletWebServerFactory();
        }
    }
    //2.如果容器中有Servlet,Server,Loader就会创建Jetty工厂
    @Configuration
    @ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedJetty {
        public EmbeddedJetty() {
        }
        @Bean
        public JettyServletWebServerFactory JettyServletWebServerFactory() {
            return new JettyServletWebServerFactory();
        }
    }
    //3.如果容器中有Servlet,Tomcat,UpgradeProtocol就会创建Tomcat工厂
    @Configuration
    @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedTomcat {
        public EmbeddedTomcat() {
        }
        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }
    }
}

二、getWebServer:获取Web服务

public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
private String protocol = DEFAULT_PROTOCOL;
public WebServer getWebServer(ServletContextInitializer... initializers) {
    Tomcat tomcat = new Tomcat();
    // 给嵌入式Tomcat创建一个临时文件夹,用于存放Tomcat运行中需要的文件
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    // Tomcat核心概念:Connector,默认放入的protocol为NIO模式
    Connector connector = new Connector(this.protocol);
    // 给Service添加Connector
    tomcat.getService().addConnector(connector);
    // 执行定制器,修改即将设置到Tomcat中的Connector
    customizeConnector(connector);
    tomcat.setConnector(connector);
    // 关闭热部署(嵌入式Tomcat不存在修改web.xml、war包等情况)
    tomcat.getHost().setAutoDeploy(false);
    // 设置backgroundProcessorDelay机制
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    // 2.1 创建TomcatEmbeddedContext
    prepareContext(tomcat.getHost(), initializers);
    // 2.2. 创建TomcatWebServer
    return getTomcatWebServer(tomcat);
}

2.1 创建TomcatEmbeddedContext

(注释均已在源码中标注好,小伙伴们对哪一步感兴趣可以借助IDE自己动手Debug体会一下实现)

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
    File documentRoot = getValidDocumentRoot();
    // 创建TomcatEmbeddedContext
    TomcatEmbeddedContext context = new TomcatEmbeddedContext();
    if (documentRoot != null) {
        context.setResources(new LoaderHidingResourceRoot(context));
    }
    context.setName(getContextPath());
    context.setDisplayName(getDisplayName());
    // 设置contextPath,很熟悉了
    context.setPath(getContextPath());
    // 给嵌入式Tomcat创建docbase的临时文件夹
    File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
    context.setDocBase(docBase.getAbsolutePath());
    // 注册监听器
    context.addLifecycleListener(new FixContextListener());
    context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
            : ClassUtils.getDefaultClassLoader());
    // 设置默认编码映射
    resetDefaultLocaleMapping(context);
    addLocaleMappings(context);
    context.setUseRelativeRedirects(false);
    try {
        context.setCreateUploadTargets(true);
    }
    catch (NoSuchMethodError ex) {
        // Tomcat is &lt; 8.5.39. Continue.
    }
    configureTldSkipPatterns(context);
    // 自定义的类加载器,可以加载web应用的jar包
    WebappLoader loader = new WebappLoader(context.getParentClassLoader());
    loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
    // 指定类加载器遵循双亲委派机制
    loader.setDelegate(true);
    context.setLoader(loader);
    // 注册默认的Servlet
    if (isRegisterDefaultServlet()) {
        addDefaultServlet(context);
    }
    // 如果需要jsp支持,注册jsp的Servlet和Initializer
    if (shouldRegisterJspServlet()) {
        addJspServlet(context);
        addJasperInitializer(context);
    }
    // 注册监听器
    context.addLifecycleListener(new StaticResourceConfigurer(context));
    ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
    host.addChild(context);
    configureContext(context, initializersToUse);
    postProcessContext(context);
}

2.2. 创建TomcatWebServer

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
   return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}

进入TomcatWebServer构造方法中:

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    //初始化服务
    initialize();
}

初始化TomcatWebServer

private void initialize() throws WebServerException {
   logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
   synchronized (this.monitor) {
      try {
         //设置Engine的id
         addInstanceIdToEngineName();
         //获取Context(TomcatEmbeddedContext  2.1中创建出来的)
         Context context = findContext();
         //添加监听器 TomcatEmbeddedContext
         //在服务启动时如果有连接进来先删除连接,以便在启动服务时不会发生协议绑定。
         context.addLifecycleListener((event) -> {
            if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
               // Remove service connectors so that protocol binding doesn't
               // happen when the service is started.
               //删除ServiceConnectors,以便在启动服务时不会发生协议绑定。
               removeServiceConnectors();
            }
         });
         // Start the server to trigger initialization listeners
         //2.2.1 启动Tomcat
         this.tomcat.start();
         // We can re-throw failure exception directly in the main thread
         //Tomcat启动有异常需要在主线程中抛出
         rethrowDeferredStartupExceptions();
         try {
            ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
         }
         catch (NamingException ex) {
            // Naming is not enabled. Continue
         }
         // Unlike Jetty, all Tomcat threads are daemon threads. We create a
         // blocking non-daemon to stop immediate shutdown
         //开启阻塞非守护线程停止web容器
         startDaemonAwaitThread();
      }
      catch (Exception ex) {
         stopSilently();
         destroySilently();
         throw new WebServerException("Unable to start embedded Tomcat", ex);
      }
   }
}

2.2.1 启动Tomcat

创建和初始化Server和Service

public void start() throws LifecycleException {
    //创建服务(Server和Service)
    getServer();
    server.start();
}

启动服务

public final synchronized void start() throws LifecycleException {
    //如果是正在启动或启动状态
    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
            LifecycleState.STARTED.equals(state)) {
        if (log.isDebugEnabled()) {
            Exception e = new LifecycleException();
            log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
        } else if (log.isInfoEnabled()) {
            log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
        }
        return;
    }
    //如果是新建状态
    if (state.equals(LifecycleState.NEW)) {
        //2.2.1.1 初始化服务
        init();
    //如果是失败状态
    } else if (state.equals(LifecycleState.FAILED)) {
        //停止服务
        stop();
    //如果不是初始化也不是停止状态
    } else if (!state.equals(LifecycleState.INITIALIZED) &amp;&amp;
            !state.equals(LifecycleState.STOPPED)) {
        //修改状态
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }
    try {
        //修改状态为准备启动
        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        //2.2.1.2 启动Internal
        startInternal();
        if (state.equals(LifecycleState.FAILED)) {
            // This is a 'controlled' failure. The component put itself into the
            // FAILED state so call stop() to complete the clean-up.
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        // This is an 'uncontrolled' failure so put the component into the
        // FAILED state and throw an exception.
        handleSubClassException(t, "lifecycleBase.startFail", toString());
    }
}

2.2.1.1 初始化Server

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());
    }
}

初始化 Server

protected void initInternal() throws LifecycleException {
    //调用父类初始化(设置名称:Tomcat,类型:Server)
    super.initInternal();
    // Initialize utility executor
    reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
    //注册线程池
    register(utilityExecutor, "type=UtilityExecutor");
    // Register global String cache
    // Note although the cache is global, if there are multiple Servers
    // present in the JVM (may happen when embedding) then the same cache
    // will be registered under multiple names
    //注册字符串缓存
    onameStringCache = register(new StringCache(), "type=StringCache");
    // Register the MBeanFactory
    MBeanFactory factory = new MBeanFactory();
    factory.setContainer(this);
    //注册Bean工厂
    onameMBeanFactory = register(factory, "type=MBeanFactory");
    // Register the naming resources
    //注册命名资源
    globalNamingResources.init();
    // Populate the extension validator with JARs from common and shared
    // class loaders
    if (getCatalina() != null) {
        ClassLoader cl = getCatalina().getParentClassLoader();
        // Walk the class loader hierarchy. Stop at the system class loader.
        // This will add the shared (if present) and common class loaders
        while (cl != null &amp;&amp; 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() &amp;&amp;
                                    f.getName().endsWith(".jar")) {
                                ExtensionValidator.addSystemResource(f);
                            }
                        } catch (URISyntaxException e) {
                            // Ignore
                        } catch (IOException e) {
                            // Ignore
                        }
                    }
                }
            }
            cl = cl.getParent();
        }
    }
    // Initialize our defined Services
    //2.2.1.1.1 初始化service(2.2.1最开始时创建)
    for (Service service : services) {
        service.init();
    }
}

初始化Service

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());
    }
}

初始化Service

protected void initInternal() throws LifecycleException {
    //调用父类初始化(设置名称:Tomcat,类型:Server)
    super.initInternal();
    //2.2.1.1.1.1 初始化engine
    if (engine != null) {
        engine.init();
    }
    // Initialize any Executors
    //2.2.1.1.1.2 初始化executor
    for (Executor executor : findExecutors()) {
        if (executor instanceof JmxEnabled) {
            ((JmxEnabled) executor).setDomain(getDomain());
        }
        executor.init();
    }
    // Initialize mapper listener
    //2.2.1.1.1.3 初始化mapperListener
    mapperListener.init();
    // Initialize our defined Connectors
    //2.2.1.1.1.4 初始化connector
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            connector.init();
        }
    }
}

初始化engine

protected void initInternal() throws LifecycleException {
    // Ensure that a Realm is present before any attempt is made to start
    // one. This will create the default NullRealm if necessary.
    // 在尝试启动一个Realm之前,请确保存在一个Realm。如有必要,这将创建默认的NullRealm
    getRealm();
    super.initInternal();
}
public Realm getRealm() {
    Realm configured = super.getRealm();
    // If no set realm has been called - default to NullRealm
    // This can be overridden at engine, context and host level
    if (configured == null) {
        configured = new NullRealm();
        this.setRealm(configured);
    }
    return configured;
}

初始化executor

它还是调的父类 LifecycleMBeanBase 的方法

protected void initInternal() throws LifecycleException {
    super.initInternal();
}

初始化mapperListener

protected void initInternal() throws LifecycleException {
    // If oname is not null then registration has already happened via preRegister().
    // 如果oname不为null,则已经通过preRegister()进行了注册
    if (oname == null) {
        mserver = Registry.getRegistry(null, null).getMBeanServer();
        oname = register(this, getObjectNameKeyProperties());
    }
}

初始化connector

protected void initInternal() throws LifecycleException {
    super.initInternal();
    if (protocolHandler == null) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
    }
    // Initialize adapter
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);
    if (service != null) {
        protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
    }
    // Make sure parseBodyMethodsSet has a default
    if (null == parseBodyMethodsSet) {
        setParseBodyMethods(getParseBodyMethods());
    }
    if (protocolHandler.isAprRequired() &amp;&amp; !AprStatus.isInstanceCreated()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",
                getProtocolHandlerClassName()));
    }
    if (protocolHandler.isAprRequired() &amp;&amp; !AprStatus.isAprAvailable()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",
                getProtocolHandlerClassName()));
    }
    if (AprStatus.isAprAvailable() &amp;&amp; AprStatus.getUseOpenSSL() &amp;&amp;
            protocolHandler instanceof AbstractHttp11JsseProtocol) {
        AbstractHttp11JsseProtocol&lt;?&gt; jsseProtocolHandler =
                (AbstractHttp11JsseProtocol&lt;?&gt;) protocolHandler;
        if (jsseProtocolHandler.isSSLEnabled() &amp;&amp;
                jsseProtocolHandler.getSslImplementationName() == null) {
            // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
            jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
        }
    }
    try {
        //2.2.1.1.1.5 初始化protocolHandler
        protocolHandler.init();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
    }
}

初始化protocolHandler

public void init() throws Exception {
    // Upgrade protocols have to be configured first since the endpoint
    // init (triggered via super.init() below) uses this list to configure
    // the list of ALPN protocols to advertise
    // 必须先配置升级协议,因为端点初始化(通过下面的super.init()触发)使用此列表来配置要发布的ALPN协议列表
    for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
        configureUpgradeProtocol(upgradeProtocol);
    }
    super.init();
}

Debug发现这个 upgradeProtocols 为空,直接走下面父类(AbstractProtocol)的 init 方法:

public void init() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
        logPortOffset();
    }
    if (oname == null) {
        // Component not pre-registered so register it
        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);
    }
    String endpointName = getName();
    endpoint.setName(endpointName.substring(1, endpointName.length()-1));
    endpoint.setDomain(domain);
    //2.2.1.1.1.6 初始化endpoint
    endpoint.init();
}

上面又是一堆初始化,这个咱暂且不关注,注意最底下有一个 endpoint.init :

初始化endpoint

来到 AbstractEndPoint :

public final void init() throws Exception {
    // Debug为false
    if (bindOnInit) {
        bindWithCleanup();
        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);
        }
    }
}

这里面又是初始化 oname ,又是配置 socketProperties 的,但这里面再也没见到 init 方法,证明这部分初始化过程已经结束了。

初始化小结

嵌入式 Tomcat 的组件初始化步骤顺序如下:

  • Server
  • Service
  • Engine
  • Executor
  • MapperListener
  • Connector
  • Protocol
  • EndPoint

startInternal:启动Internal

startInternal 方法中有两部分启动:globalNamingResources 启动,services 启动。分别来看:

protected void startInternal() throws LifecycleException {
    // 发布启动事件
    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    setState(LifecycleState.STARTING);
    // 2.2.1.2.1 NamingResources启动
    globalNamingResources.start();
    // Start our defined Services
    synchronized (servicesLock) {
        for (int i = 0; i &lt; services.length; i++) {
            // 2.2.1.2.2 Service启动
            services[i].start();
        }
    }
    if (periodicEventDelay &gt; 0) {
        monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
                new Runnable() {
                    @Override
                    public void run() {
                        startPeriodicLifecycleEvent();
                    }
                }, 0, 60, TimeUnit.SECONDS);
    }
}

NamingResources启动

只是发布事件和设置状态而已

protected void startInternal() throws LifecycleException {
    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    setState(LifecycleState.STARTING);
}

Service启动

依次启动 Engine 、Executor 、MapperListener 、Connector

protected void startInternal() throws LifecycleException {
    if(log.isInfoEnabled())
        log.info(sm.getString("standardService.start.name", this.name));
    setState(LifecycleState.STARTING);
    // Start our defined Container first
    if (engine != null) {
        synchronized (engine) {
            // 2.2.1.2.2.1 启动Engine
            engine.start();
        }
    }
    synchronized (executors) {
        for (Executor executor: executors) {
            // 2.2.1.2.2.3 启动Executor
            executor.start();
        }
    }
    // 2.2.1.2.2.4 启动MapperListener
    mapperListener.start();
    // Start our defined Connectors second
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            // If it has already failed, don't try and start it
            if (connector.getState() != LifecycleState.FAILED) {
                // 2.2.1.2.2.5 启动connector
                connector.start();
            }
        }
    }
}

启动Engine

protected synchronized void startInternal() throws LifecycleException {
    // Log our server identification information
    if (log.isInfoEnabled()) {
        log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));
    }
    // Standard container startup
    super.startInternal();
}

它直接调的父类 ContainerBase 的 startInternal 方法:

protected synchronized void startInternal() throws LifecycleException {
    // Start our subordinate components, if any
    logger = null;
    getLogger();
    // Cluster与集群相关,SpringBoot项目中使用嵌入式Tomcat,不存在集群
    Cluster cluster = getClusterInternal();
    if (cluster instanceof Lifecycle) {
        ((Lifecycle) cluster).start();
    }
    // Realm与授权相关
    Realm realm = getRealmInternal();
    if (realm instanceof Lifecycle) {
        ((Lifecycle) realm).start();
    }
    // Start our child containers, if any
    // Container的类型是StandardHost
    Container children[] = findChildren();
    List&lt;Future&lt;Void&gt;&gt; results = new ArrayList&lt;&gt;();
    for (int i = 0; i &lt; children.length; i++) {
        //异步初始化Host
        results.add(startStopExecutor.submit(new StartChild(children[i])));
    }
    MultiThrowable multiThrowable = null;
    for (Future&lt;Void&gt; result : results) {
        try {
            result.get();
        } catch (Throwable e) {
            log.error(sm.getString("containerBase.threadedStartFailed"), e);
            if (multiThrowable == null) {
                multiThrowable = new MultiThrowable();
            }
            multiThrowable.add(e);
        }
    }
    if (multiThrowable != null) {
        throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
                multiThrowable.getThrowable());
    }
    // Start the Valves in our pipeline (including the basic), if any
    if (pipeline instanceof Lifecycle) {
        ((Lifecycle) pipeline).start();
    }
    setState(LifecycleState.STARTING);
    // Start our thread
    if (backgroundProcessorDelay &gt; 0) {
        monitorFuture = Container.getService(ContainerBase.this).getServer()
                .getUtilityExecutor().scheduleWithFixedDelay(
                        new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
    }
}

StartChild 实现了带返回值的异步多线程接口 Callable 核心方法就是在 call

private static class StartChild implements Callable<Void>

它实现了带返回值的异步多线程接口 Callable !那里面的核心方法就是 call :

public Void call() throws LifecycleException {
    child.start();
    return null;
}

它在这里初始化 child,而通过Debug得知 child 的类型是 StandardHost,故来到 StandardHost 的 start 方法:

protected synchronized void startInternal() throws LifecycleException {
    // Set error report valve
    String errorValve = getErrorReportValveClass();
    if ((errorValve != null) &amp;&amp; (!errorValve.equals(""))) {
        try {
            boolean found = false;
            Valve[] valves = getPipeline().getValves();
            for (Valve valve : valves) {
                if (errorValve.equals(valve.getClass().getName())) {
                    found = true;
                    break;
                }
            }
            if(!found) {
                Valve valve =
                    (Valve) Class.forName(errorValve).getConstructor().newInstance();
                getPipeline().addValve(valve);
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error(sm.getString(
                    "standardHost.invalidErrorReportValveClass",
                    errorValve), t);
        }
    }
    super.startInternal();
}

上面的一个大if结构是设置错误提示页面的,下面又调父类的 startInternal :

protected synchronized void startInternal() throws LifecycleException {
    // ......
    // Start our child containers, if any
    Container children[] = findChildren();
    List<Future<Void>> results = new ArrayList<>();
    for (int i = 0; i < children.length; i++) {
        results.add(startStopExecutor.submit(new StartChild(children[i])));
    }

又回来了。。。因为一个 Host 包含一个 Context 。

Host 搜索children就会搜到它下面的 Context ,之后又是下面的初始化过程,进入 Context 的初始化:

启动TomcatEmbeddedContext

在TomcatEmbeddedContext有如下组件被调用了 start 方法:

  • StandardRoot
  • DirResourceSet
  • WebappLoader
  • JarResourceSet
  • StandardWrapper
  • StandardPineline
  • StandardWrapperValve
  • NonLoginAuthenticator
  • StandardContextValve
  • StandardManager
  • LazySessionIdGenerator

启动Executor

但由于 Executor 没有实现 startInternal 方法,所以不会启动

    synchronized (executors) {
        for (Executor executor: executors) {
            executor.start();
        }
    }

启动MapperListener

接下来启动 MapperListener :

public void startInternal() throws LifecycleException {
    setState(LifecycleState.STARTING);
    Engine engine = service.getContainer();
    if (engine == null) {
        return;
    }
    // 获取当前部署的主机名(本地调试为localhost)
    findDefaultHost();
    // 把当前自身注册到Engine、Host、Context、Wrapper中
    addListeners(engine);
    // 取出的Container的类型为Host
    Container[] conHosts = engine.findChildren();
    for (Container conHost : conHosts) {
        Host host = (Host) conHost;
        if (!LifecycleState.NEW.equals(host.getState())) {
            // Registering the host will register the context and wrappers
            //将Host、Context、Wrapper注册到当前监听器中
            registerHost(host);
        }
    }
}

启动Connector

最后一步是启动 Connector 。

    // Start our defined Connectors second
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            // If it has already failed, don't try and start it
            if (connector.getState() != LifecycleState.FAILED) {
                connector.start();
            }
        }
    }

启动总结

启动过程依次启动了如下组件:

  • NamingResources
  • Service
  • Engine
  • Host
  • Context
  • Wrapper
  • Executor
  • MapperListener

三、注册Bean生命周期

3.1 WebServerStartStopLifecycle(Web服务器启动-停止生命周期)

WebServerStartStopLifecycle实现了Lifecycle,在容器刷新完成时会调用finishRefresh()

@Override
public void start() {
   //启动Tomcat 容器
   this.webServer.start();
   this.running = true;
   this.applicationContext
         .publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
}
public void start() throws WebServerException {
    synchronized (this.monitor) {
        if (this.started) {
            return;
        }
        try {
            // 3.1.1 还原、启动Connector
            addPreviouslyRemovedConnectors();
            // 只拿一个Connector
            Connector connector = this.tomcat.getConnector();
            if (connector != null &amp;&amp; this.autoStart) {
                // 3.1.2 延迟启动
                performDeferredLoadOnStartup();
            }
            // 检查Connector是否正常启动
            checkThatConnectorsHaveStarted();
            this.started = true;
            logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
                    + getContextPath() + "'");
        }
        // catch ......
        finally {
            // 解除ClassLoader与TomcatEmbeddedContext的绑定关系
            Context context = findContext();
            ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
        }
    }
}

3.1.1 addPreviouslyRemovedConnectors:启动Connector

private void addPreviouslyRemovedConnectors() {
    Service[] services = this.tomcat.getServer().findServices();
    for (Service service : services) {
        Connector[] connectors = this.serviceConnectors.get(service);
        if (connectors != null) {
            for (Connector connector : connectors) {
                // 添加并启动
                service.addConnector(connector);
                if (!this.autoStart) {
                    stopProtocolHandler(connector);
                }
            }
            this.serviceConnectors.remove(service);
        }
    }
}

可以发现它将一个缓存区的 Connector 一个一个取出放入 Service 中。注意在 service.addConnector 中有顺便启动的部分:

public void addConnector(Connector connector) {
    synchronized (connectorsLock) {
        connector.setService(this);
        Connector results[] = new Connector[connectors.length + 1];
        System.arraycopy(connectors, 0, results, 0, connectors.length);
        results[connectors.length] = connector;
        connectors = results;
    }
    try {
        if (getState().isAvailable()) {
            // 启动Connector
            connector.start();
        }
    } catch (LifecycleException e) {
        throw new IllegalArgumentException(
                sm.getString("standardService.connector.startFailed", connector), e);
    }
    // Report this property change to interested listeners
    support.firePropertyChange("connector", null, connector);
}

前面的部分是取出 Connector ,并与 Service 绑定,之后中间部分的try块,会启动 Connector :

protected void startInternal() throws LifecycleException {
    // Validate settings before starting
    if (getPortWithOffset() &lt; 0) {
        throw new LifecycleException(sm.getString(
                "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
    }
    setState(LifecycleState.STARTING);
    try {
        // 启动ProtocolHandler
        protocolHandler.start();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
    }
}

Connector 的启动会引发 ProtocolHandler 的启动:

public void start() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
        logPortOffset();
    }
    // 启动EndPoint
    endpoint.start();
    monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
            new Runnable() {
                @Override
                public void run() {
                    if (!isPaused()) {
                        startAsyncTimeout();
                    }
                }
            }, 0, 60, TimeUnit.SECONDS);
}

ProtocolHandler 的启动会引发 EndPoint 的启动,至此所有组件均已启动完毕。

performDeferredLoadOnStartup:延迟启动

这里面会延迟启动 TomcatEmbeddedContext

private void performDeferredLoadOnStartup() {
    try {
        for (Container child : this.tomcat.getHost().findChildren()) {
            if (child instanceof TomcatEmbeddedContext) {
                // 延迟启动Context
                ((TomcatEmbeddedContext) child).deferredLoadOnStartup();
            }
        }
    }
    catch (Exception ex) {
        if (ex instanceof WebServerException) {
            throw (WebServerException) ex;
        }
        throw new WebServerException("Unable to start embedded Tomcat connectors", ex);
    }
}

四、初始化上下文环境

这里在Spring中已经刷新过一次,详情:在文章 https://www.jb51.net/article/277946.htm 的 prepareRefresh:初始化前的预处理中

以上就是ServletWebServerApplicationContext创建Web容器Tomcat示例的详细内容,更多关于Web容器Tomcat创建的资料请关注我们其它相关文章!

(0)

相关推荐

  • Tomcat打破双亲委派机制实现隔离Web应用的方法

    目录 Tomcat类加载器的层次结构 WebAppClassLoader SharedClassLoader CatalinaClassLoader CommonClassLoader Spring的加载问题 线程上下文加载器 总结 Tomcat通过自定义类加载器WebAppClassLoader打破双亲委派,即重写了JVM的类加载器ClassLoader的findClass方法和loadClass方法,以优先加载Web应用目录下的类. Tomcat负责加载我们的Servlet类.加载Servl

  • 删除 Tomcat webapps 目录自带项目方式详解

    目录 1.webapps目录中的项目 2.修改配置文件 1.webapps目录中的项目 本文将 %CATALINA_HOME% 目录称为“tomcat”目录. 在 Tomcat 8.0 的 tomcat/webapps 目录中,含有 5 个 Tomcat 自带的 Web 项目,如下所示: docs有关于 Tomcat 的介绍和操作文档等examples小程序示例:如 websocket 等host-manager进行 Host 管理manager进行 Server Status 和 Applic

  • Eclipse开发JavaWeb项目配置Tomcat的方法步骤

    以下都经过本人自学时一一自己动手配置实验. 首先介绍eclipse开发JavaWeb项目需要配置的相关环境,使用tomcat软件在本地搭建服务器,然后再在eclipse环境下配置tomcat: 第一步:使用tomcat软件在本地搭建服务器 这个本地的tomcat服务器与eclipse环境下配置tomcat服务器都可以使用,但是只能启动一个,否则会报端口冲突,到时安装好环境会介绍 tomcat软件是apache旗下的一个开源项目.软件下载链接:http://tomcat.apache.org/,如

  • tomcat的webapps目录下的应用删除部署详解

    目录 1.tomcat9的webapps下应用的访问 2. 进一步:删除webapps下所有目录,只留自己应用 2.1 删除说明 2.2 需要删除的内容 3. 小结 1.tomcat9的webapps下应用的访问 tomcat9的server.xml文件中 <Host>下需要加一段: <Context docBase="/xxx/tomcat9/webapps/xxx" path="/" reloadable="true">

  • 使用IDEA创建servlet JavaWeb 应用及使用Tomcat本地部署的实现

    目录 需要安装好的软件 背景知识-Servlet是什么? Servlet 是一种规范 Servlet 接口 JSP Web 容器 背景知识-JavaWeb应用的目录结构 1.新建一个java项目 2.将普通java项目转换成JavaWeb项目 3.进行项目目录结构的设置 4.引入Tomcat的jar包 5.简单写一些代码用来测试 6.设置Tomcat 7.开启Tomcat 总结 记录一下使用IDEA创建servlet并使用Tomcat本地部署的过程. 需要安装好的软件 首先IDEA社区版不支持J

  • ServletWebServerApplicationContext创建Web容器Tomcat示例

    目录 正文 创建Web服务 一.获取Web服务器工厂 1.1 选择导入Web工厂 二.getWebServer:获取Web服务 2.1 创建TomcatEmbeddedContext 2.2. 创建TomcatWebServer 2.2.1 启动Tomcat 初始化小结 startInternal:启动Internal NamingResources启动 Service启动 启动Engine 启动TomcatEmbeddedContext 启动Executor 启动MapperListener

  • spring boot 加载web容器tomcat流程源码分析

    我本地的springboot版本是2.5.1,后面的分析都是基于这个版本 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.1</version> <relativePath/> <!-- lookup parent fr

  • 详解eclipse创建maven项目实现动态web工程完整示例

    本文介绍了eclipse创建maven项目实现动态web工程完整示例,分享给大家.具体如下: 需求表均同springmvc案例 此处只是使用maven 注意,以下所有需要建立在你的eclipse等已经集成配置好了maven了,说白了就是新建项目的时候已经可以找到maven了 没有的话需要安装maven 1.新建maven项目,如果不在上面,请到other里面去找一下 2,进入maven项目之后,点击next 选择webapp之后 next 输入两个id package可以不写,是它默认帮你新建一

  • 使用Spring Boot创建Web应用程序的示例代码

    在这篇文章中,我们将探讨使用Spring Boot创建Web应用程序的细节. 我们将探索Spring Boot如何帮助你加速应用程序开发. 我们将使用Spring Boot构建一个简单的Web应用程序,并为其添加一些有用的服务. 1. 介绍 启动一个新项目的主要挑战之一是该项目的初始设置. 我们需要对不同的目录结构进行调用,并且需要确保我们遵循所有行业标准.对于使用Spring Boot创建Web应用程序,我们需要以下工具: 我们自己喜欢的IDE (我将使用IntelliJ) Maven JDK

  • IDEA2020.1.2创建web项目配置Tomcat的详细教程

    本文章是一篇IDEA创建web项目配置Tomcat的整合文章,并非原创,原文链接 https://blog.csdn.net/qq_45738810/article/details/107842532 https://www.cnblogs.com/shindo/p/7272646.html 作为初学者,先看了第一个链接里大佬的文章又看其他的文章表示很懵,为了避免各位和我一样,所以在此整合一下,希望能帮到各位! 第一步,先创建一个普通的Java项目 第二步,创建web项目.右键项目名–>Add

  • 使用IDEA创建Web项目并发布到tomcat的操作方法

    Web开发 1.web开发概述 •学习web开发,需要先安装一台web服务器,将开发好的web项目部署在web服务器中供外界访问. WEB服务器有很多,流行的WEB服务器有Tomcat. WebSphere .WebLogic.Jboss等. 在小型的应用系统或者有特殊需要的系统中,可以使用一个免费的Web服务器: Apache 的Tomcat,该服务器支持全部JSP以及Servlet规范. Tomcat安装配置 Tomcat官方站点:http://tomcat.apache.org 获取Tom

  • IDEA2022创建Web项目配置Tomcat的详细图文说明

    下面是在idea上面配置一个Tomcat的项目环境. 1.首先创建普通的一个Java项目,不要选择JavaEE  2.创建完成后按照下图所示,依次选择 File -> Add Framework Support -> Web Application -> 在WEB-INF文件夹下创建classes和lib文件夹  3.下面配置环境: File-> Project Structure-> Modules-> Paths-> 选择第二个“Use”,修改两个路径均为cl

  • NodeJS http模块用法示例【创建web服务器/客户端】

    本文实例讲述了NodeJS http模块用法.分享给大家供大家参考,具体如下: Node.js提供了http模块,用于搭建HTTP服务端和客户端. 创建Web服务器 /** * node-http 服务端 */ let http = require('http'); let url = require('url'); let fs = require('fs'); // 创建服务器 let server = http.createServer((req, res) => { // 解析请求 le

  • SpringBoot 嵌入式web容器的启动原理详解

    目录 SpringBoot应用启动run方法 SpringApplication.java 中执行的代码 ServletWebServerApplicationContext.java执行的方法 SpringBoot 2.x 版本 嵌入式Servlet容器自动配置原理以及启动原理 一.版本说明 二.总结 三.嵌入式Servlet容器自动配置原理(以Tomcat为例) 四.嵌入式Servlet容器启动原理(以Tomcat为例) SpringBoot应用启动run方法 SpringApplicati

  • SpringBoot嵌入式Web容器原理与使用介绍

    目录 原理 应用 1. 切换Web服务器 2. 定制服务器规则 嵌入式 Web 容器:应用中内置服务器(Tomcat),不用在外部配置服务器了 原理 SpringBoot 项目启动,发现是 web 应用,引入 web 场景包 ----- 如:Tomcat web 应用创建一个 web 版的 IOC 容器 ServletWebServerApplicationContext ServletWebServerApplicationContext 启动的时候寻找 ServletWebServerFac

随机推荐