<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
ServletWebServerApplicationContext實現了父類別AbstractApplicationContext的onRefresh模板方法,在這裡進行了拓展建立了Web容器。
@Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
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(); }
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); } } } }
@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(); } } }
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); }
(註釋均已在原始碼中標註好,小夥伴們對哪一步感興趣可以藉助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 < 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); }
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); } } }
建立和初始化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) && !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 && 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(); } } // 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() && !AprStatus.isInstanceCreated()) { throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener", getProtocolHandlerClassName())); } if (protocolHandler.isAprRequired() && !AprStatus.isAprAvailable()) { throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary", getProtocolHandlerClassName())); } if (AprStatus.isAprAvailable() && AprStatus.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 { //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 的元件初始化步驟順序如下:
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 < services.length; i++) { // 2.2.1.2.2 Service啟動 services[i].start(); } } if (periodicEventDelay > 0) { monitorFuture = getUtilityExecutor().scheduleWithFixedDelay( new Runnable() { @Override public void run() { startPeriodicLifecycleEvent(); } }, 0, 60, TimeUnit.SECONDS); } }
只是釋出事件和設定狀態而已
protected void startInternal() throws LifecycleException { fireLifecycleEvent(CONFIGURE_START_EVENT, null); setState(LifecycleState.STARTING); }
依次啟動 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(); } } } }
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<Future<Void>> results = new ArrayList<>(); for (int i = 0; i < children.length; i++) { //非同步初始化Host results.add(startStopExecutor.submit(new StartChild(children[i]))); } MultiThrowable multiThrowable = null; for (Future<Void> 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 > 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) && (!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有如下元件被呼叫了 start 方法:
但由於 Executor
沒有實現 startInternal
方法,所以不會啟動
synchronized (executors) { for (Executor executor: executors) { executor.start(); } }
接下來啟動 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
。
// 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(); } } }
啟動過程依次啟動了如下元件:
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 && 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()); } } }
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() < 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建立的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45