spring boot启动概述 spring boot 框架凭借极简配置,一键运行和强大的第三方框架集成等特点,受到广大开发者的青睐,基本成为java开发中必不可少的基础的框架。spirng boot带给我们这么多便利的背后,它都做了些什么,让我们就跟随spirng boot的整个启动流程一探究竟。
上图可见spring boot的整个启动流程及各组件的相互调用关系。
java程序由启动主类调用main()方法开始。
调用 SpringApplication的构造方法,实例一个Spirng应用对象。在构造方法里主要完成启动环境初始化工作,如,推断主类,spring应用类型,加载配置文件,读取spring.factories文件等。
调用run方法,所有的启动工作在该方法内完成,主要完成加载配置资源,准备上下文,创建上下文,刷新上下文,过程事件发布等。 整个启动流程细节我们跟着源码分析。
1.启动入口 (SrpingApplication) 大家熟悉的springboot的启动类,@SpringBootApplication + psvm(main方法)+ new SpringApplication().run(XXXX.class, args)
1 2 3 4 5 6 7 8 java 代码解读复制代码@SpringBootApplication public class SummaryApplication { public static void main (String[] args) { SpringApplication application = new SpringApplication(); application.run(SummaryApplication.class , args ) ; } }
1.1 @SpringBootApplication 注解 通过源码发现该注解只是@Configuration,@EnableAutoConfiguration,@ComponentScan 三个注解的组合,这是在springboot 1.5以后为这三个注解做的一个简写。接下来简单说下这三个注解的功能:
1 2 3 4 5 6 7 8 9 10 11 12 java 代码解读复制代码@Target (ElementType.TYPE) @Retention (RetentionPolicy.RUNTIME)@Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan (excludeFilters = { @Filter (type = FilterType.CUSTOM, classes = TypeExcludeFilter.class ), @Filter (type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class ) }) public @interface SpringBootApplication { ... }
1.1.1 @SpringBootConfiguration 该注解就是spirng ioc容器中java config 配置方式的@Configuration ,注册当前类为spring ioc容器的配置类。
搭配@bean注解创建一个简单spring ioc配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 java 代码解读复制代码 @Configuration public class Conf { @Bean public Car car () { Car car = new Car(); car.setWheel(wheel()); return car; } @Bean public Wheel wheel () { return new Wheel(); } }
1.1.2 @EnableAutoConfiguration 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 java 代码解读复制代码@Target (ElementType.TYPE) @Retention (RetentionPolicy.RUNTIME)@Documented @Inherited @AutoConfigurationPackage @Import (AutoConfigurationImportSelector.class ) //最为重要 public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration" ; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
@EnableAutoConfiguration借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器,会根据类路径中的jar依赖为项目进行自动配置,如:添加了spring-boot-starter-web依赖,会自动添加Tomcat和Spring MVC的依赖,Spring Boot会对Tomcat和Spring MVC进行自动配置。
最关键的要属@Import(EnableAutoConfigurationImportSelector.class) ,借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。就像一只“八爪鱼”一样,借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自动配置功效才得以大功告成!
1.1.3 @ComponentScan @ComponentScan这个注解在Spring中很重要,它对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。
我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。
2.构造器(Constructor) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 java 代码解读复制代码 @SuppressWarnings ({ "unchecked" , "rawtypes" })public SpringApplication (ResourceLoader resourceLoader, Class<?>... primarySources) { this .resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null" ); this .primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this .webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class )) ; setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class )) ; this .mainApplicationClass = deduceMainApplicationClass(); }
2.1 判断当前程序类型 根据classpath里面是否存在某个特征类
(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 css 代码解读复制代码NONE ,SERVLET ,REACTIVE ;
3.启动方法(RUN) 初始化完成之后就进到了run方法,run方法完成了所有Spring的整个启动过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 java 代码解读复制代码 public ConfigurableApplicationContext run (String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null ; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class , new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this .logStartupInfo) { new StartupInfoLogger(this .mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null ); throw new IllegalStateException(ex); } return context; }
3.1 SpringApplicationRunListener 的使用 首先通过getSpringFactoriesInstances 获取到所有实现SpringApplicationRunListener 接口的实例,默认情况下该接口的实现类只有 EventPublishingRunListener 他的主要作用是作为springboot 的一个广播器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 java 代码解读复制代码public interface SpringApplicationRunListener { default void starting () {} default void environmentPrepared (ConfigurableEnvironment environment) {} default void contextPrepared (ConfigurableApplicationContext context) {} default void contextLoaded (ConfigurableApplicationContext context) {} default void started (ConfigurableApplicationContext context) {} default void running (ConfigurableApplicationContext context) {} default void failed (ConfigurableApplicationContext context, Throwable exception) {} }
3.2 prepareEnvironment 一般在写业务代码时使用的都是只读类型的接口Environment
,该接口是对运行程序环境的抽象,是保存系统配置的中心,而在启动过程中使用的则是可编辑的ConfigurableEnvironment
。接口的UML类图如下,提供了合并父环境、添加active profile以及一些设置解析配置文件方式的接口。
其中一个比较重要的方法MutablePropertySources getPropertySources();
,该方法返回一个可编辑的PropertySources
,如果有在启动阶段自定义环境的PropertySources的需求,就可以通过该方法设置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 java 代码解读复制代码private ConfigurableEnvironment prepareEnvironment (SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this .isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
3.3 创建springApplicationContext 上下文
继承的三个父类接口里,Closeable
提供了关闭时资源释放的接口,Lifecycle
是提供对生命周期控制的接口(start\stop)以及查询当前运行状态的接口,ApplicationContext
则是配置上下文的中心配置接口,继承了其他很多配置接口,其本身提供查询诸如id、应用程序名等上下文档案信息的只读接口,以及构建自动装配bean的工厂。
提供Environment接口。
国际化资源接口。
ApplicationEventPublisher
事件发布器。
资源加载器。
HierarchicalBeanFactory、ListableBeanFactory
这两个都继承了bean容器的根接口BeanFactory
简而言之就是根据Web容器类型的不同来创建不用的上下文实例。
3.4 上下文初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 java 代码解读复制代码private void prepareContext (ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this .logStartupInfo) { logStartupInfo(context.getParent() == null ); logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments" , applicationArguments); if (printedBanner != null ) { beanFactory.registerSingleton("springBootBanner" , printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this .allowBeanDefinitionOverriding); } if (this .lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty" ); load(context, sources.toArray(new Object[0 ])); listeners.contextLoaded(context); }
3.5 刷新上下文 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 代码解读 复制代码AbstractApplicationContext java 代码解读复制代码public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { //记录启动时间、状态,web容器初始化其property,复制listener prepareRefresh(); //这里返回的是context的BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //beanFactory注入一些标准组件,例如ApplicationContextAwareProcessor,ClassLoader等 prepareBeanFactory(beanFactory); try { //给实现类留的一个钩子,例如注入BeanPostProcessors,这里是个空方法 postProcessBeanFactory(beanFactory); // 调用切面方法 invokeBeanFactoryPostProcessors(beanFactory); // 注册切面bean registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // bean工厂注册一个key为applicationEventMulticaster的广播器 initApplicationEventMulticaster(); // 给实现类留的一钩子,可以执行其他refresh的工作,这里是个空方法 onRefresh(); // 将listener注册到广播器中 registerListeners(); // 实例化未实例化的bean finishBeanFactoryInitialization(beanFactory); // 清理缓存,注入DefaultLifecycleProcessor,发布ContextRefreshedEvent 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(); } } }
至此spring 启动主要工作基本完成,接下来发布AppStartedEvent事件,回调ApplicationRunner,CommandLineRunner等runner,发布applicationReadyEvent事件,spring 正式启动开始运行。
转载地址 https://juejin.cn/post/7035910505810100255
This is copyright.