一、springboot的启动原理
springboot的启动通过在main方法的SpringApplication.run( )方法启动
@SpringBootApplication public class ShuaApplication { public static void main(String[] args) { SpringApplication.run(ShuaApplication.class, args); } }
首先创建SpringApplication对象,在其构造方法中做了几件事
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //1、推断并设置当前web应用类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //2、设置初始化器、监听器。从spring.factories文件中分别读取key为ApplicationContextInitializer、ApplicationListener两种接口类型的实现类。 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //3、找到main方法执行的启动类 this.mainApplicationClass = deduceMainApplicationClass(); }
SpringApplication.run(...)方法
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); /* * 1、配置属性 * 设置系统属性 java.awt.headless,为 true 则启用headless模式 * headless模式是应用的一种配置模式,在服务器缺少显示设备、键盘、鼠标等外设的情况下可以使用该模式 * 比如我们使用的Linux服务器就是缺少前述的这些设备,但是又需要使用这些设备提供的能力 */ configureHeadlessProperty(); /* * 2、获取监听器,发布应用开始启动事件 * 通过SpringFactoriesLoader检索META-INF/spring.factories, * 找到声明的所有SpringApplicationRunListener的实现类并将其实例化, * 之后逐个调用其starting()方法,广播SpringBoot要开始执行了 */ SpringApplicationRunListeners listeners = getRunListeners(args); //发布ApplicationStartingEvent事件 listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //3、加载配置信息:环境变量、配置文件(application.properties或application.yml) //发布ApplicationEnvironmentPreparedEvent事件 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); //4、创建spring上下文:ApplicationContext context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); /** * 5、预处理上下文 * 逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext * 发布ApplicationContextInitializedEvent事件 * load(context,sources)将启动类作为配置类,注册为BeanDefinition * 发布ApplicationPreparedEvent事件 **/ prepareContext(context, environment, listeners, applicationArguments, printedBanner); /*6、刷新spring上下文:调用spring的refresh()方法 * 包括@EnableAutoConfiguration功能,其就是通过@Import导入自动配置类,将spring.factories中的自动配置类注册为BeanDefinition * 另外springboot也是重写其中的onRefresh()方法创建启动Tomcat的 */ refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //发布ApplicationStartedEvent事件 listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //发布ApplicationReadyEvent事件 listeners.running(context); } catch (Throwable ex) { //发布ApplicationFailedEvent事件 handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
总结
1、运行man方法:应用程序启动始于main方法的执行。首先创建一个SpringApplication实例,在其构造方法中,从spring.factires文件中加载并注册监听器(ApplicationListener)、初始化器(ApplicationInitializer)等扩展接口。
2、运行run方法:初始化spring容器并启动内置Tomcat
(1)加载配置信息:读取和解析环境变量、配置文件(application.properties或application.yml)
(2)创建spring上下文:根据应用程序类型创建相应的ApplicationContext,对于web应用,通常创建的是ServletWebServerApplicationContext。
(3)预处理上下文:将启动类作为配置类,读取并注册为BeanDefinition,这样就可以识别应用程序的配置。
(4)刷新上下文:即调用spring的refresh()方法,包括扫描解析类文件注册为BeanDefinition——>实例化bean——>依赖注入——>执行初始化方法等流程。
这里有两点要注意:
-
springboot的自动配置类是在这一步处理的,@EnableAutoConfiguration是利用@Import来实现自动装配功能(即自动加载spring.factories文件中的定义的类)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.coohua.caf.core.web.FastJsonAutoConfiguration,\ com.coohua.caf.core.base.CustomizedPropertiesBinderAutoConfiguration,\ com.coohua.caf.core.base.FrameworkConfigAutoConfiguration,\ com.coohua.caf.core.base.SpringApplicationEventListenerAutoConfiguration,\ com.coohua.caf.core.web.WebAutoConfiguration,\ com.coohua.caf.core.logging.LoggerLevelRefresherAutoConfiguration,\ com.coohua.caf.core.apollo.ApolloConfigAutoChangeAutoConfiguration
-
springboot创建和启动Tomcat是onRefresh()方法,实际是ServletWebServerApplicationContext重写了该方法。
(5)创建内置的Tomcat:ServletWebServerApplicationContext重写onRefresh()方法,在其中创建和启动Tomcat
事件机制
springboot启动过程的各个阶段发布很多事件,一个是springboot自身通过这些事件监听机制对整个流程进行一定程度的解耦,另一个就是可以供外部进行相应的扩展。
二、springboot自动装配原理
1、在主配置配类上@SpringBootApplication注解中包含@EnableAutoConfiguration注解,该注解负责自动装配功能。
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(****) public @interface SpringBootApplication {
2、@EnableAutoConfiguration利用@Import注入了处理自动装配逻辑的类AutoConfigurationImportSelector
AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration {
它会加载spring.factories文件中所有key为EnableAutoConfiguration的类。
注意:所有springboot通过EnableAutoConfiguration自动装配的都会在最后加载,为了确保能够方便扩展和覆盖(原理是AutoConfigurationImportSelector实现了DeferredImportSelector延迟加载)
3、spring.factories文件还有两个用处,ApplicationContextInitializer、ApplicationListener两种接口类型的实现类。这两个类是在new SpringApplication对象的构造方法中加载为实例对象的。
其中实现了ApplicationContextInitializer的类是在上面启动过程第5步的预处理上下文中执行initialize()方法,ApplicationListener则可以自己注册监听器来监听对应的事件。
org.springframework.context.ApplicationContextInitializer=\ com.coohua.caf.core.logging.Log4j2ContextInitializer org.springframework.context.ApplicationListener=\ com.coohua.bp.config.api.spring.ConfigContainerBootListener
4、spring中@Import注解的处理时机
参考文章:https://blog.51cto.com/u_15287666/4970846#SpringImportImport_49
@Import注解的处理是利用后置处理器BeanFactoryPostProcessor,ConfigurationClassPostProcessor实现了该后置处理器,在refresh()方法中执行
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //1、调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. //2、告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入 // 创建bean工厂供后面使用——DefaultListableBeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. //3、为BeanFactory配置容器特性,例如类加载器、事件处理器等 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. //4、为容器的某些子类指定特殊的BeanPost事件处理器 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //5、调用所有注册的BeanFactoryPostProcessor的Bean invokeBeanFactoryPostProcessors(beanFactory);
ConfigurationClassPostProcessor类负责扫描和处理@Configuration注解和@Import注解。
三、springboot如何启动内置Tomcat
springboot的web容器ServletWebServerApplicationContext重写了onRefresh()方法,在其中创建并启动Tomcat。
public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext { @Override protected void onRefresh() { super.onRefresh(); try { //创建并启动Tomcat 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) { //Servlet工厂,web环境下默认是TomcatServletWebServerFactory ServletWebServerFactory factory = getWebServerFactory(); //在这一步获取一个webServer,在其中创建Tomcat对象并启动 this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }
TomcatServletWebServerFactory.getWebServer(...)创建Tomcat对象。
@Override public WebServer getWebServer(ServletContextInitializer... initializers) { //创建Tomcat对象 Tomcat tomcat = new Tomcat(); File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); //在这一步启动Tomcat return getTomcatWebServer(tomcat); }
TomcatWebServer在构造方法中调用initialize(),在这个方法中启动Tomcat。
public TomcatWebServer(Tomcat tomcat, boolean autoStart) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; initialize(); } private void initialize() throws WebServerException { synchronized (this.monitor) { try { 。。。。 //这一步就是启动Tomcat了 this.tomcat.start(); 。。。。。 } catch (Exception ex) { stopSilently(); throw new WebServerException("Unable to start embedded Tomcat", ex); } } }
标签:springboot,Tomcat,启动,spring,tomcat,context,new,原理 From: https://www.cnblogs.com/jing-yi/p/18239338