经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Servlet » 查看文章
ServletWebServerApplicationContext创建Web容器Tomcat示例
来源:jb51  时间:2023/3/15 8:52:51  对本文有异议

正文

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

  1. @Override
  2. protected void onRefresh() {
  3. super.onRefresh();
  4. try {
  5. createWebServer();
  6. }
  7. catch (Throwable ex) {
  8. throw new ApplicationContextException("Unable to start web server", ex);
  9. }
  10. }

创建Web服务

  1. private void createWebServer() {
  2. WebServer webServer = this.webServer;
  3. ServletContext servletContext = getServletContext();
  4. if (webServer == null && servletContext == null) {
  5. //一、获取Web服务器工厂
  6. ServletWebServerFactory factory = getWebServerFactory();
  7. //二、获取Web服务
  8. this.webServer = factory.getWebServer(getSelfInitializer());
  9. //三、注册Bean生命周期(在容器启动和销毁时调用)
  10. getBeanFactory().registerSingleton("webServerGracefulShutdown",
  11. new WebServerGracefulShutdownLifecycle(this.webServer));
  12. getBeanFactory().registerSingleton("webServerStartStop",
  13. new WebServerStartStopLifecycle(this, this.webServer));
  14. }
  15. else if (servletContext != null) {
  16. try {
  17. getSelfInitializer().onStartup(servletContext);
  18. }
  19. catch (ServletException ex) {
  20. throw new ApplicationContextException("Cannot initialize servlet context", ex);
  21. }
  22. }
  23. //四、初始化上下文环境
  24. initPropertySources();
  25. }

一、获取Web服务器工厂

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

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

  1. @Configuration(proxyBeanMethods = false)
  2. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  3. @ConditionalOnClass(ServletRequest.class)
  4. //Web启动环境
  5. @ConditionalOnWebApplication(type = Type.SERVLET)
  6. @EnableConfigurationProperties(ServerProperties.class)
  7. //2.1导入Web工厂
  8. @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
  9. ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
  10. ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
  11. ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
  12. public class ServletWebServerFactoryAutoConfiguration {
  13. //导入Web服务器工厂自定义程序
  14. @Bean
  15. public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
  16. return new ServletWebServerFactoryCustomizer(serverProperties);
  17. }
  18. //如果是Tomcat则导入Tomcat自定义程序
  19. @Bean
  20. @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
  21. public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
  22. ServerProperties serverProperties) {
  23. return new TomcatServletWebServerFactoryCustomizer(serverProperties);
  24. }
  25. @Bean
  26. @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
  27. @ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
  28. public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
  29. ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
  30. FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
  31. registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
  32. registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
  33. return registration;
  34. }
  35. /**
  36. * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
  37. * {@link ImportBeanDefinitionRegistrar} for early registration.
  38. */
  39. public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
  40. private ConfigurableListableBeanFactory beanFactory;
  41. @Override
  42. public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  43. if (beanFactory instanceof ConfigurableListableBeanFactory) {
  44. this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
  45. }
  46. }
  47. @Override
  48. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
  49. BeanDefinitionRegistry registry) {
  50. if (this.beanFactory == null) {
  51. return;
  52. }
  53. registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
  54. WebServerFactoryCustomizerBeanPostProcessor.class);
  55. registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
  56. ErrorPageRegistrarBeanPostProcessor.class);
  57. }
  58. private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
  59. if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
  60. RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
  61. beanDefinition.setSynthetic(true);
  62. registry.registerBeanDefinition(name, beanDefinition);
  63. }
  64. }
  65. }
  66. }

1.1 选择导入Web工厂

  1. @Configuration
  2. class ServletWebServerFactoryConfiguration {
  3. ServletWebServerFactoryConfiguration() {
  4. }
  5. //1.如果容器中有Servlet,Undertow,SslClientAuthMode就会创建Undertow工厂
  6. @Configuration
  7. @ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})
  8. @ConditionalOnMissingBean(
  9. value = {ServletWebServerFactory.class},
  10. search = SearchStrategy.CURRENT
  11. )
  12. public static class EmbeddedUndertow {
  13. public EmbeddedUndertow() {
  14. }
  15. @Bean
  16. public UndertowServletWebServerFactory undertowServletWebServerFactory() {
  17. return new UndertowServletWebServerFactory();
  18. }
  19. }
  20. //2.如果容器中有Servlet,Server,Loader就会创建Jetty工厂
  21. @Configuration
  22. @ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class})
  23. @ConditionalOnMissingBean(
  24. value = {ServletWebServerFactory.class},
  25. search = SearchStrategy.CURRENT
  26. )
  27. public static class EmbeddedJetty {
  28. public EmbeddedJetty() {
  29. }
  30. @Bean
  31. public JettyServletWebServerFactory JettyServletWebServerFactory() {
  32. return new JettyServletWebServerFactory();
  33. }
  34. }
  35. //3.如果容器中有Servlet,Tomcat,UpgradeProtocol就会创建Tomcat工厂
  36. @Configuration
  37. @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
  38. @ConditionalOnMissingBean(
  39. value = {ServletWebServerFactory.class},
  40. search = SearchStrategy.CURRENT
  41. )
  42. public static class EmbeddedTomcat {
  43. public EmbeddedTomcat() {
  44. }
  45. @Bean
  46. public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
  47. return new TomcatServletWebServerFactory();
  48. }
  49. }
  50. }

二、getWebServer:获取Web服务

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

2.1 创建TomcatEmbeddedContext

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

  1. protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
  2. File documentRoot = getValidDocumentRoot();
  3. // 创建TomcatEmbeddedContext
  4. TomcatEmbeddedContext context = new TomcatEmbeddedContext();
  5. if (documentRoot != null) {
  6. context.setResources(new LoaderHidingResourceRoot(context));
  7. }
  8. context.setName(getContextPath());
  9. context.setDisplayName(getDisplayName());
  10. // 设置contextPath,很熟悉了
  11. context.setPath(getContextPath());
  12. // 给嵌入式Tomcat创建docbase的临时文件夹
  13. File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
  14. context.setDocBase(docBase.getAbsolutePath());
  15. // 注册监听器
  16. context.addLifecycleListener(new FixContextListener());
  17. context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
  18. : ClassUtils.getDefaultClassLoader());
  19. // 设置默认编码映射
  20. resetDefaultLocaleMapping(context);
  21. addLocaleMappings(context);
  22. context.setUseRelativeRedirects(false);
  23. try {
  24. context.setCreateUploadTargets(true);
  25. }
  26. catch (NoSuchMethodError ex) {
  27. // Tomcat is &lt; 8.5.39. Continue.
  28. }
  29. configureTldSkipPatterns(context);
  30. // 自定义的类加载器,可以加载web应用的jar包
  31. WebappLoader loader = new WebappLoader(context.getParentClassLoader());
  32. loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
  33. // 指定类加载器遵循双亲委派机制
  34. loader.setDelegate(true);
  35. context.setLoader(loader);
  36. // 注册默认的Servlet
  37. if (isRegisterDefaultServlet()) {
  38. addDefaultServlet(context);
  39. }
  40. // 如果需要jsp支持,注册jsp的Servlet和Initializer
  41. if (shouldRegisterJspServlet()) {
  42. addJspServlet(context);
  43. addJasperInitializer(context);
  44. }
  45. // 注册监听器
  46. context.addLifecycleListener(new StaticResourceConfigurer(context));
  47. ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
  48. host.addChild(context);
  49. configureContext(context, initializersToUse);
  50. postProcessContext(context);
  51. }

2.2. 创建TomcatWebServer

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

进入TomcatWebServer构造方法中:

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

初始化TomcatWebServer

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

2.2.1 启动Tomcat

创建和初始化Server和Service

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

启动服务

  1. public final synchronized void start() throws LifecycleException {
  2. //如果是正在启动或启动状态
  3. if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
  4. LifecycleState.STARTED.equals(state)) {
  5. if (log.isDebugEnabled()) {
  6. Exception e = new LifecycleException();
  7. log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
  8. } else if (log.isInfoEnabled()) {
  9. log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
  10. }
  11. return;
  12. }
  13. //如果是新建状态
  14. if (state.equals(LifecycleState.NEW)) {
  15. //2.2.1.1 初始化服务
  16. init();
  17. //如果是失败状态
  18. } else if (state.equals(LifecycleState.FAILED)) {
  19. //停止服务
  20. stop();
  21. //如果不是初始化也不是停止状态
  22. } else if (!state.equals(LifecycleState.INITIALIZED) &amp;&amp;
  23. !state.equals(LifecycleState.STOPPED)) {
  24. //修改状态
  25. invalidTransition(Lifecycle.BEFORE_START_EVENT);
  26. }
  27. try {
  28. //修改状态为准备启动
  29. setStateInternal(LifecycleState.STARTING_PREP, null, false);
  30. //2.2.1.2 启动Internal
  31. startInternal();
  32. if (state.equals(LifecycleState.FAILED)) {
  33. // This is a 'controlled' failure. The component put itself into the
  34. // FAILED state so call stop() to complete the clean-up.
  35. stop();
  36. } else if (!state.equals(LifecycleState.STARTING)) {
  37. // Shouldn't be necessary but acts as a check that sub-classes are
  38. // doing what they are supposed to.
  39. invalidTransition(Lifecycle.AFTER_START_EVENT);
  40. } else {
  41. setStateInternal(LifecycleState.STARTED, null, false);
  42. }
  43. } catch (Throwable t) {
  44. // This is an 'uncontrolled' failure so put the component into the
  45. // FAILED state and throw an exception.
  46. handleSubClassException(t, "lifecycleBase.startFail", toString());
  47. }
  48. }

2.2.1.1 初始化Server

  1. public final synchronized void init() throws LifecycleException {
  2. if (!state.equals(LifecycleState.NEW)) {
  3. invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
  4. }
  5. try {
  6. //设置状态为初始化
  7. setStateInternal(LifecycleState.INITIALIZING, null, false);
  8. //初始化
  9. initInternal();
  10. //设置状态为初始化完成
  11. setStateInternal(LifecycleState.INITIALIZED, null, false);
  12. } catch (Throwable t) {
  13. handleSubClassException(t, "lifecycleBase.initFail", toString());
  14. }
  15. }

初始化 Server

  1. protected void initInternal() throws LifecycleException {
  2. //调用父类初始化(设置名称:Tomcat,类型:Server)
  3. super.initInternal();
  4. // Initialize utility executor
  5. reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
  6. //注册线程池
  7. register(utilityExecutor, "type=UtilityExecutor");
  8. // Register global String cache
  9. // Note although the cache is global, if there are multiple Servers
  10. // present in the JVM (may happen when embedding) then the same cache
  11. // will be registered under multiple names
  12. //注册字符串缓存
  13. onameStringCache = register(new StringCache(), "type=StringCache");
  14. // Register the MBeanFactory
  15. MBeanFactory factory = new MBeanFactory();
  16. factory.setContainer(this);
  17. //注册Bean工厂
  18. onameMBeanFactory = register(factory, "type=MBeanFactory");
  19. // Register the naming resources
  20. //注册命名资源
  21. globalNamingResources.init();
  22. // Populate the extension validator with JARs from common and shared
  23. // class loaders
  24. if (getCatalina() != null) {
  25. ClassLoader cl = getCatalina().getParentClassLoader();
  26. // Walk the class loader hierarchy. Stop at the system class loader.
  27. // This will add the shared (if present) and common class loaders
  28. while (cl != null &amp;&amp; cl != ClassLoader.getSystemClassLoader()) {
  29. if (cl instanceof URLClassLoader) {
  30. URL[] urls = ((URLClassLoader) cl).getURLs();
  31. for (URL url : urls) {
  32. if (url.getProtocol().equals("file")) {
  33. try {
  34. File f = new File (url.toURI());
  35. if (f.isFile() &amp;&amp;
  36. f.getName().endsWith(".jar")) {
  37. ExtensionValidator.addSystemResource(f);
  38. }
  39. } catch (URISyntaxException e) {
  40. // Ignore
  41. } catch (IOException e) {
  42. // Ignore
  43. }
  44. }
  45. }
  46. }
  47. cl = cl.getParent();
  48. }
  49. }
  50. // Initialize our defined Services
  51. //2.2.1.1.1 初始化service(2.2.1最开始时创建)
  52. for (Service service : services) {
  53. service.init();
  54. }
  55. }

初始化Service

  1. public final synchronized void init() throws LifecycleException {
  2. if (!state.equals(LifecycleState.NEW)) {
  3. invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
  4. }
  5. try {
  6. //设置状态为初始化
  7. setStateInternal(LifecycleState.INITIALIZING, null, false);
  8. //初始化
  9. initInternal();
  10. //设置状态为初始化完成
  11. setStateInternal(LifecycleState.INITIALIZED, null, false);
  12. } catch (Throwable t) {
  13. handleSubClassException(t, "lifecycleBase.initFail", toString());
  14. }
  15. }

初始化Service

  1. protected void initInternal() throws LifecycleException {
  2. //调用父类初始化(设置名称:Tomcat,类型:Server)
  3. super.initInternal();
  4. //2.2.1.1.1.1 初始化engine
  5. if (engine != null) {
  6. engine.init();
  7. }
  8. // Initialize any Executors
  9. //2.2.1.1.1.2 初始化executor
  10. for (Executor executor : findExecutors()) {
  11. if (executor instanceof JmxEnabled) {
  12. ((JmxEnabled) executor).setDomain(getDomain());
  13. }
  14. executor.init();
  15. }
  16. // Initialize mapper listener
  17. //2.2.1.1.1.3 初始化mapperListener
  18. mapperListener.init();
  19. // Initialize our defined Connectors
  20. //2.2.1.1.1.4 初始化connector
  21. synchronized (connectorsLock) {
  22. for (Connector connector : connectors) {
  23. connector.init();
  24. }
  25. }
  26. }

初始化engine

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

初始化executor

它还是调的父类 LifecycleMBeanBase 的方法

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

初始化mapperListener

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

初始化connector

  1. protected void initInternal() throws LifecycleException {
  2. super.initInternal();
  3. if (protocolHandler == null) {
  4. throw new LifecycleException(
  5. sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
  6. }
  7. // Initialize adapter
  8. adapter = new CoyoteAdapter(this);
  9. protocolHandler.setAdapter(adapter);
  10. if (service != null) {
  11. protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
  12. }
  13. // Make sure parseBodyMethodsSet has a default
  14. if (null == parseBodyMethodsSet) {
  15. setParseBodyMethods(getParseBodyMethods());
  16. }
  17. if (protocolHandler.isAprRequired() &amp;&amp; !AprStatus.isInstanceCreated()) {
  18. throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",
  19. getProtocolHandlerClassName()));
  20. }
  21. if (protocolHandler.isAprRequired() &amp;&amp; !AprStatus.isAprAvailable()) {
  22. throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",
  23. getProtocolHandlerClassName()));
  24. }
  25. if (AprStatus.isAprAvailable() &amp;&amp; AprStatus.getUseOpenSSL() &amp;&amp;
  26. protocolHandler instanceof AbstractHttp11JsseProtocol) {
  27. AbstractHttp11JsseProtocol&lt;?&gt; jsseProtocolHandler =
  28. (AbstractHttp11JsseProtocol&lt;?&gt;) protocolHandler;
  29. if (jsseProtocolHandler.isSSLEnabled() &amp;&amp;
  30. jsseProtocolHandler.getSslImplementationName() == null) {
  31. // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
  32. jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
  33. }
  34. }
  35. try {
  36. //2.2.1.1.1.5 初始化protocolHandler
  37. protocolHandler.init();
  38. } catch (Exception e) {
  39. throw new LifecycleException(
  40. sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
  41. }
  42. }

初始化protocolHandler

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

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

  1. public void init() throws Exception {
  2. if (getLog().isInfoEnabled()) {
  3. getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
  4. logPortOffset();
  5. }
  6. if (oname == null) {
  7. // Component not pre-registered so register it
  8. oname = createObjectName();
  9. if (oname != null) {
  10. Registry.getRegistry(null, null).registerComponent(this, oname, null);
  11. }
  12. }
  13. if (this.domain != null) {
  14. rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
  15. Registry.getRegistry(null, null).registerComponent(
  16. getHandler().getGlobal(), rgOname, null);
  17. }
  18. String endpointName = getName();
  19. endpoint.setName(endpointName.substring(1, endpointName.length()-1));
  20. endpoint.setDomain(domain);
  21. //2.2.1.1.1.6 初始化endpoint
  22. endpoint.init();
  23. }

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

初始化endpoint

来到 AbstractEndPoint :

  1. public final void init() throws Exception {
  2. // Debug为false
  3. if (bindOnInit) {
  4. bindWithCleanup();
  5. bindState = BindState.BOUND_ON_INIT;
  6. }
  7. if (this.domain != null) {
  8. // Register endpoint (as ThreadPool - historical name)
  9. oname = new ObjectName(domain + ":type=ThreadPool,name="" + getName() + """);
  10. Registry.getRegistry(null, null).registerComponent(this, oname, null);
  11. ObjectName socketPropertiesOname = new ObjectName(domain +
  12. ":type=ThreadPool,name="" + getName() + "",subType=SocketProperties");
  13. socketProperties.setObjectName(socketPropertiesOname);
  14. Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);
  15. for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
  16. registerJmx(sslHostConfig);
  17. }
  18. }
  19. }

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

 初始化小结

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

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

startInternal:启动Internal

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

  1. protected void startInternal() throws LifecycleException {
  2. // 发布启动事件
  3. fireLifecycleEvent(CONFIGURE_START_EVENT, null);
  4. setState(LifecycleState.STARTING);
  5. // 2.2.1.2.1 NamingResources启动
  6. globalNamingResources.start();
  7. // Start our defined Services
  8. synchronized (servicesLock) {
  9. for (int i = 0; i &lt; services.length; i++) {
  10. // 2.2.1.2.2 Service启动
  11. services[i].start();
  12. }
  13. }
  14. if (periodicEventDelay &gt; 0) {
  15. monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
  16. new Runnable() {
  17. @Override
  18. public void run() {
  19. startPeriodicLifecycleEvent();
  20. }
  21. }, 0, 60, TimeUnit.SECONDS);
  22. }
  23. }

NamingResources启动

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

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

Service启动

依次启动 Engine 、Executor 、MapperListener 、Connector 

  1. protected void startInternal() throws LifecycleException {
  2. if(log.isInfoEnabled())
  3. log.info(sm.getString("standardService.start.name", this.name));
  4. setState(LifecycleState.STARTING);
  5. // Start our defined Container first
  6. if (engine != null) {
  7. synchronized (engine) {
  8. // 2.2.1.2.2.1 启动Engine
  9. engine.start();
  10. }
  11. }
  12. synchronized (executors) {
  13. for (Executor executor: executors) {
  14. // 2.2.1.2.2.3 启动Executor
  15. executor.start();
  16. }
  17. }
  18. // 2.2.1.2.2.4 启动MapperListener
  19. mapperListener.start();
  20. // Start our defined Connectors second
  21. synchronized (connectorsLock) {
  22. for (Connector connector: connectors) {
  23. // If it has already failed, don't try and start it
  24. if (connector.getState() != LifecycleState.FAILED) {
  25. // 2.2.1.2.2.5 启动connector
  26. connector.start();
  27. }
  28. }
  29. }
  30. }

启动Engine

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

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

  1. protected synchronized void startInternal() throws LifecycleException {
  2. // Start our subordinate components, if any
  3. logger = null;
  4. getLogger();
  5. // Cluster与集群相关,SpringBoot项目中使用嵌入式Tomcat,不存在集群
  6. Cluster cluster = getClusterInternal();
  7. if (cluster instanceof Lifecycle) {
  8. ((Lifecycle) cluster).start();
  9. }
  10. // Realm与授权相关
  11. Realm realm = getRealmInternal();
  12. if (realm instanceof Lifecycle) {
  13. ((Lifecycle) realm).start();
  14. }
  15. // Start our child containers, if any
  16. // Container的类型是StandardHost
  17. Container children[] = findChildren();
  18. List&lt;Future&lt;Void&gt;&gt; results = new ArrayList&lt;&gt;();
  19. for (int i = 0; i &lt; children.length; i++) {
  20. //异步初始化Host
  21. results.add(startStopExecutor.submit(new StartChild(children[i])));
  22. }
  23. MultiThrowable multiThrowable = null;
  24. for (Future&lt;Void&gt; result : results) {
  25. try {
  26. result.get();
  27. } catch (Throwable e) {
  28. log.error(sm.getString("containerBase.threadedStartFailed"), e);
  29. if (multiThrowable == null) {
  30. multiThrowable = new MultiThrowable();
  31. }
  32. multiThrowable.add(e);
  33. }
  34. }
  35. if (multiThrowable != null) {
  36. throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
  37. multiThrowable.getThrowable());
  38. }
  39. // Start the Valves in our pipeline (including the basic), if any
  40. if (pipeline instanceof Lifecycle) {
  41. ((Lifecycle) pipeline).start();
  42. }
  43. setState(LifecycleState.STARTING);
  44. // Start our thread
  45. if (backgroundProcessorDelay &gt; 0) {
  46. monitorFuture = Container.getService(ContainerBase.this).getServer()
  47. .getUtilityExecutor().scheduleWithFixedDelay(
  48. new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
  49. }
  50. }

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

  1. private static class StartChild implements Callable<Void>

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

  1. public Void call() throws LifecycleException {
  2. child.start();
  3. return null;
  4. }

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

  1. protected synchronized void startInternal() throws LifecycleException {
  2. // Set error report valve
  3. String errorValve = getErrorReportValveClass();
  4. if ((errorValve != null) &amp;&amp; (!errorValve.equals(""))) {
  5. try {
  6. boolean found = false;
  7. Valve[] valves = getPipeline().getValves();
  8. for (Valve valve : valves) {
  9. if (errorValve.equals(valve.getClass().getName())) {
  10. found = true;
  11. break;
  12. }
  13. }
  14. if(!found) {
  15. Valve valve =
  16. (Valve) Class.forName(errorValve).getConstructor().newInstance();
  17. getPipeline().addValve(valve);
  18. }
  19. } catch (Throwable t) {
  20. ExceptionUtils.handleThrowable(t);
  21. log.error(sm.getString(
  22. "standardHost.invalidErrorReportValveClass",
  23. errorValve), t);
  24. }
  25. }
  26. super.startInternal();
  27. }

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

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

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

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

 启动TomcatEmbeddedContext

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

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

启动Executor

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

  1. synchronized (executors) {
  2. for (Executor executor: executors) {
  3. executor.start();
  4. }
  5. }

启动MapperListener

接下来启动 MapperListener :

  1. public void startInternal() throws LifecycleException {
  2. setState(LifecycleState.STARTING);
  3. Engine engine = service.getContainer();
  4. if (engine == null) {
  5. return;
  6. }
  7. // 获取当前部署的主机名(本地调试为localhost)
  8. findDefaultHost();
  9. // 把当前自身注册到Engine、Host、Context、Wrapper中
  10. addListeners(engine);
  11. // 取出的Container的类型为Host
  12. Container[] conHosts = engine.findChildren();
  13. for (Container conHost : conHosts) {
  14. Host host = (Host) conHost;
  15. if (!LifecycleState.NEW.equals(host.getState())) {
  16. // Registering the host will register the context and wrappers
  17. //将Host、Context、Wrapper注册到当前监听器中
  18. registerHost(host);
  19. }
  20. }
  21. }

 启动Connector

最后一步是启动 Connector 。

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

 启动总结

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

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

三、注册Bean生命周期

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

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

  1. @Override
  2. public void start() {
  3. //启动Tomcat 容器
  4. this.webServer.start();
  5. this.running = true;
  6. this.applicationContext
  7. .publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
  8. }
  1. public void start() throws WebServerException {
  2. synchronized (this.monitor) {
  3. if (this.started) {
  4. return;
  5. }
  6. try {
  7. // 3.1.1 还原、启动Connector
  8. addPreviouslyRemovedConnectors();
  9. // 只拿一个Connector
  10. Connector connector = this.tomcat.getConnector();
  11. if (connector != null &amp;&amp; this.autoStart) {
  12. // 3.1.2 延迟启动
  13. performDeferredLoadOnStartup();
  14. }
  15. // 检查Connector是否正常启动
  16. checkThatConnectorsHaveStarted();
  17. this.started = true;
  18. logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
  19. + getContextPath() + "'");
  20. }
  21. // catch ......
  22. finally {
  23. // 解除ClassLoader与TomcatEmbeddedContext的绑定关系
  24. Context context = findContext();
  25. ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
  26. }
  27. }
  28. }

3.1.1 addPreviouslyRemovedConnectors:启动Connector

  1. private void addPreviouslyRemovedConnectors() {
  2. Service[] services = this.tomcat.getServer().findServices();
  3. for (Service service : services) {
  4. Connector[] connectors = this.serviceConnectors.get(service);
  5. if (connectors != null) {
  6. for (Connector connector : connectors) {
  7. // 添加并启动
  8. service.addConnector(connector);
  9. if (!this.autoStart) {
  10. stopProtocolHandler(connector);
  11. }
  12. }
  13. this.serviceConnectors.remove(service);
  14. }
  15. }
  16. }

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

  1. public void addConnector(Connector connector) {
  2. synchronized (connectorsLock) {
  3. connector.setService(this);
  4. Connector results[] = new Connector[connectors.length + 1];
  5. System.arraycopy(connectors, 0, results, 0, connectors.length);
  6. results[connectors.length] = connector;
  7. connectors = results;
  8. }
  9. try {
  10. if (getState().isAvailable()) {
  11. // 启动Connector
  12. connector.start();
  13. }
  14. } catch (LifecycleException e) {
  15. throw new IllegalArgumentException(
  16. sm.getString("standardService.connector.startFailed", connector), e);
  17. }
  18. // Report this property change to interested listeners
  19. support.firePropertyChange("connector", null, connector);
  20. }

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

  1. protected void startInternal() throws LifecycleException {
  2. // Validate settings before starting
  3. if (getPortWithOffset() &lt; 0) {
  4. throw new LifecycleException(sm.getString(
  5. "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
  6. }
  7. setState(LifecycleState.STARTING);
  8. try {
  9. // 启动ProtocolHandler
  10. protocolHandler.start();
  11. } catch (Exception e) {
  12. throw new LifecycleException(
  13. sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
  14. }
  15. }

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

  1. public void start() throws Exception {
  2. if (getLog().isInfoEnabled()) {
  3. getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
  4. logPortOffset();
  5. }
  6. // 启动EndPoint
  7. endpoint.start();
  8. monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
  9. new Runnable() {
  10. @Override
  11. public void run() {
  12. if (!isPaused()) {
  13. startAsyncTimeout();
  14. }
  15. }
  16. }, 0, 60, TimeUnit.SECONDS);
  17. }

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

 performDeferredLoadOnStartup:延迟启动

这里面会延迟启动 TomcatEmbeddedContext

  1. private void performDeferredLoadOnStartup() {
  2. try {
  3. for (Container child : this.tomcat.getHost().findChildren()) {
  4. if (child instanceof TomcatEmbeddedContext) {
  5. // 延迟启动Context
  6. ((TomcatEmbeddedContext) child).deferredLoadOnStartup();
  7. }
  8. }
  9. }
  10. catch (Exception ex) {
  11. if (ex instanceof WebServerException) {
  12. throw (WebServerException) ex;
  13. }
  14. throw new WebServerException("Unable to start embedded Tomcat connectors", ex);
  15. }
  16. }

四、初始化上下文环境

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

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

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号