spring boot中如何启动tomcat或jetty的

您所在的位置:网站首页 bannermode spring boot中如何启动tomcat或jetty的

spring boot中如何启动tomcat或jetty的

2023-04-02 17:49| 来源: 网络整理| 查看: 265

最近面试中会聊到这个话题,说实话之前已经做过大量spring boot源码级的分享,如果你在之前看过Spring Boot 容器启动原理揭秘,想必已经有了一个大概的认识。启动tomcat或jetty其实也跳不出这个套路,或者讲它仅仅是其中的一个环节,通过AbstractApplicationContext.onRefresh()完成的。

知识回顾

Spring Boot的一个及其吸引人的特性是fat-jar技术,解下jar解压包的MANIFEST.MF文件我们会发现Main-Class及Start-Class,而Start-Class就是我们程序中的main函数类DemoApplication。

spring boot中如何启动tomcat或jetty的_bc

SpringApplication

而这个启动类中重点关注SpringApplication类,有两个非常重要的方法:

private void initialize(Object[] sources)public static ConfigurableApplicationContext run(Object source, String... args)public class SpringApplication { public SpringApplication(Object... sources) { this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; this.addCommandLineProperties = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = new HashSet(); this.initialize(sources);//关注点 } //初始化启动参数变量 private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = this.deduceWebEnvironment(); this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); } //启动ioc容器与tomcat容器 public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[]{source}, args); } public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return (new SpringApplication(sources)).run(args); } //run真正执行的方法 public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; this.configureHeadlessProperty(); SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); Banner printedBanner = this.printBanner(environment); //创建ioc容器 context = this.createApplicationContext(); new FailureAnalyzers(context); this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新ioc容器上下文 this.refreshContext(context); this.afterRefresh(context, applicationArguments); listeners.finished(context, (Throwable)null); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } return context; } catch (Throwable var9) { this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9); throw new IllegalStateException(var9); } } private void refreshContext(ConfigurableApplicationContext context) { this.refresh(context);//此次很关键 if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException var3) { ; } } } protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); //这个一定不陌生吧 ((AbstractApplicationContext)applicationContext).refresh(); } //会创建ioc为AnnotationConfigEmbeddedWebApplicationContext protected ConfigurableApplicationContext createApplicationContext() { Class contextClass = this.applicationContextClass; if (contextClass == null) { try { contextClass = Class.forName(this.webEnvironment ? "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext" : "org.springframework.context.annotation.AnnotationConfigApplicationContext"); } catch (ClassNotFoundException var3) { throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3); } } return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass); } }

要解答本文的疑问,问题有回到了AbstractApplicationContext.refresh(),如果你对它里面干的事情比较熟悉的话,后续可以不用继续看下去了。

AbstractApplicationContext.refresh()

它主要是通过工厂类EmbeddedServletContainerFactory生成一个EmbeddedServletContainer实例,再调用start方法进行启动,最终完成tomcat、jetty的启动。

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean { //ioc的最重要的一些组件 @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh();//重点关注这里 // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh();//重点关注这里 } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } } protected void onRefresh() throws BeansException {//没错,实现在子类中 // For subclasses: do nothing by default. } } public class AnnotationConfigEmbeddedWebApplicationContext extends EmbeddedWebApplicationContext { } public class EmbeddedWebApplicationContext extends GenericWebApplicationContext { //重写了AbstractApplicationContext的onRefresh protected void onRefresh() { super.onRefresh(); try { this.createEmbeddedServletContainer();//看名字是不是有种柳暗花明的感觉 } catch (Throwable var2) { throw new ApplicationContextException("Unable to start embedded container", var2); } } //没错就是创建tomcat的核心类EmbeddedServletContainer private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; ServletContext localServletContext = this.getServletContext(); if (localContainer == null && localServletContext == null) { EmbeddedServletContainerFactory containerFactory = this.getEmbeddedServletContainerFactory(); this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(new ServletContextInitializer[]{this.getSelfInitializer()}); } else if (localServletContext != null) { try { this.getSelfInitializer().onStartup(localServletContext); } catch (ServletException var4) { throw new ApplicationContextException("Cannot initialize servlet context", var4); } } this.initPropertySources(); } //获取Sevlet工程中获取web容器 protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() { String[] beanNames = this.getBeanFactory().getBeanNamesForType(EmbeddedServletContainerFactory.class); if (beanNames.length == 0) { throw new ApplicationContextException("Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean."); } else if (beanNames.length > 1) { throw new ApplicationContextException("Unable to start EmbeddedWebApplicationContext due to multiple EmbeddedServletContainerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } else { return (EmbeddedServletContainerFactory)this.getBeanFactory().getBean(beanNames[0], EmbeddedServletContainerFactory.class); } } //重写了AbstractApplicationContext的finishRefresh protected void finishRefresh() { super.finishRefresh(); //关注点 EmbeddedServletContainer localContainer = this.startEmbeddedServletContainer(); if (localContainer != null) { this.publishEvent(new EmbeddedServletContainerInitializedEvent(this, localContainer)); } } //启动tomcat private EmbeddedServletContainer startEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; if (localContainer != null) { localContainer.start();//start方法 } return localContainer; } }

EmbeddedServletContainerFactory

此时你觉得EmbeddedServletContainerFactory有点突兀,它从哪里来,又是做什么的呢?首先看它的实现:

TomcatEmbeddedServletContainerFactory,tomcat容器JettyEmbeddedServletContainerFactory,Jetty容器UndertowEmbeddedServletContainerFactory,Undertow容器

它来自spring的SPI(spring.factories),Auto Configure的配置key=EnableAutoConfiguration

spring boot中如何启动tomcat或jetty的_tomcat_02

spring boot中如何启动tomcat或jetty的_bc_03

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication @Import(BeanPostProcessorsRegistrar.class) public class EmbeddedServletContainerAutoConfiguration { /** * Nested configuration if Tomcat is being used. */ @Configuration @ConditionalOnClass({ Servlet.class, Tomcat.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { @Bean public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {//这就是为什么可以在ioc中可以获取了 return new TomcatEmbeddedServletContainerFactory(); } } /** * Nested configuration if Jetty is being used. */ @Configuration @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedJetty { @Bean public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() { return new JettyEmbeddedServletContainerFactory(); } } /** * Nested configuration if Undertow is being used. */ @Configuration @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedUndertow { @Bean public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() { return new UndertowEmbeddedServletContainerFactory(); } } } public class TomcatEmbeddedServletContainerFactory extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware { private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private static final Set


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3