Spring Boot启动流程源码解析
定义:
BeanFactoryPostProcessor
BeanDefinitionRegistryPostProcessor:是一个接口,继承接口BeanFactoryPostProcessor,该接口新增postProcessBeanDefinitionRegistry方法,方法参数是BeanDefinitionRegistry对象,BeanDefinitionRegistry对象是beanDefiinition的保存中心
BeanPostProcessor:在bean实例化的时候,进行切面处理,在实例化的前执行postProcessBeforeInitialization,在实例化后执行postProcessAfterInitialization
一、SpringApllication实例化,进行初始化操作:
this.resourceLoader = resourceLoader; // 资源加载器,resourceLoader = null Assert.notNull(primarySources, "PrimarySources must not be null"); // this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //赋值启动类 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //根据classpath存在的类推断web所属应用类型:servelt,Reactive,后面根据不同的类型初始化对应的环境 this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories(); //获取启动加载器 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //设置初始化器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //设置监听器 this.mainApplicationClass = deduceMainApplicationClass(); //根据运行调用栈track,根据main方法找到入口启动类
getBootstrapRegistryInitializersFromSpringFactories() :获取启动加载器
它调用getSpringFactoriesInstances(Bootstrapper.class),在多个应用的META-INF/spring.factories文件下获取,key=Bootstrapper的启动类加载器的包路径,并通过class.forName反射,然后把实例放到集合进行Order排序,设置优先级,返回集合。
但是此处,由于META-INF/spring.factories文件没有key=Bootstrapper的信息,故返回了一个空集合。
setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class))):设置初始化器
getSpringFactoriesInstances(ApplicationContextInitializer.class))的作用:在多个应用的META-INF/spring.factories文件下获取,key=ApplicationContextInitializer初始化器的包路径,并通过class.forName反射,然后把实例放到集合进行Order排序,设置优先级,返回集合,并通过setInitializers对本对象属性initializers(初始化器)进行赋值。初始化器,在spring容器刷新之前,进行接口回调initialize方法
返回的初始化器如下:
0 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer" 作用:用作报告常见错误配置的警告(通过分析源码实际情况是如果系统配置包扫描(@ComponentScan)到了org或者org.springframework包就会发出警告并停止系统启动) 1 = "org.springframework.boot.context.ContextIdApplicationContextInitializer" 作用:用于获取、设置spring上下文ID 2 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer" 作用:使用环境属性 context.initializer.classes,指定的初始化器(initializers)进行初始化工作,如果没有指定则什么都不做。 通过它使得我们可以把自定义实现类配置在 application.properties 里成为了可能。 3 = "org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer" 作用:为server设置local.rsocket.server.port通讯监听端口 4 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer" 作用:将内置servlet容器实际使用的监听端口写入到Environment环境属性中。这样属性local.server.port就可以直接通过@Value注入到测试中,或者通过环境属性Environment获取。 5 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer" 作用:创建一个SpringBoot和ConfigurationClassPostProcessor共用的CachingMetadataReaderFactory对象。实现类为:ConcurrentReferenceCachingMetadataReaderFactory 6 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener" 作用:将ConditionEvaluationReport报告写入日志。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)) :设置监听器
getSpringFactoriesInstances(ApplicationListener.class)的作用:在多个应用的META-INF/spring.factories文件下获取,key=ApplicationListener初始化器的包路径,并通过class.forName反射,然后把实例放到集合进行Order排序,设置优先级,返回集合,并通过setListeners对本对象属性listeners(监听器)进行赋值。
返回的监听器如下:
0 = "org.springframework.boot.ClearCachesApplicationListener" 作用:上下文加载完成后,清理缓存,比如,从META-INF/spring.factories获取的信息后,会放到cache,下次直接从cache拿。 1 = "org.springframework.boot.builder.ParentContextCloserApplicationListener" 作用:监听当双亲应用的上下文关闭,如果双亲应用关闭了,自身也要关闭。从这个类中拿到上下文去刷新监听双亲关闭事件。 2 = "org.springframework.boot.context.FileEncodingApplicationListener" 作用:文件编码应用监听器。该监听器实质作用是在收到应用环境准备就绪事件时,对配置中关于文件编码的配置作一个校验,判断配置中的文件编码是否和JVM系统的file.encoding一致。无配置或JVM系统的file.encoding无效的情况下,都不会报异常,但是,当JVM中的file.encoding有效,且在配置中包含了spring.mandatory-file-encoding,而二者又不一致时,就会报IllegalStateException异常。 3 = "org.springframework.boot.context.config.AnsiOutputApplicationListener" 作用:当收到应用环境准备就绪事件时,对Ansi输出的相关状态进行设置,并绑定到应用环境中 4 = "org.springframework.boot.context.config.DelegatingApplicationListener" 作用:而是在应用环境准备就绪事件发生时,通过环境中的配置的context.listener.classes,指定自定义的监听器,然后创建一个简单事件广播器实例,放到类属性上,同时,把自定义的监听器,绑定到该广播器上。就可以使得我们自定义的监听器发挥作用 5 = "org.springframework.boot.context.logging.LoggingApplicationListener" 作用:日志配置监听器,如果没配置日志信息,则使用系统默认的配置。 6 = "org.springframework.boot.env.EnvironmentPostProcessorApplicationListener" 作用:监听环境准备好后,如果配置信息spring.factories指定了“环境后置处理程序”,则运行该后置处理程序。一般用于自定义环境变量或者编写第三方扩展点 7 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer" 作用:对于一些耗时的任务,使用一个后台线程尽早触发它们开始执行初始化, 这也可以叫做预初始化。 设置系统属性spring.backgroundpreinitializer.ignore为true可禁用该机制。 禁用时,相应的初始化任务会发生在前台线程。
二、SpringApplication的run方法
StopWatch stopWatch = new StopWatch(); stopWatch.start(); //用于计时 //【1】创建临时上下文,临时存放一些上下文数据,使用完后会colse. DefaultBootstrapContext bootstrapContext = createBootstrapContext(); //Spring的上下文 ConfigurableApplicationContext context = null; //【2】配置系统属性java.awt.headless,如果系统缺少硬件,就让系统模拟对应硬件数据以供使用 configureHeadlessProperty(); //【3】获取应用运行的监听器 SpringApplicationRunListeners listeners = getRunListeners(args); //启动监听器 listeners.starting(bootstrapContext, this.mainApplicationClass); try { //封装形参args ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //【4】准备容器环境,初始化environment,即应用上下文环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); //根据用户自定义spring.beaninfo.ignore,为容器设置需要忽略加载的bean configureIgnoreBeanInfo(environment); //【5】打印spring的logo Banner printedBanner = printBanner(environment); //【6】创建上下文和容器 context = createApplicationContext(); //为上下文设置应用启动时候的记录器,如:启动报错的报告 context.setApplicationStartup(this.applicationStartup); //【7】刷新上下文的准备阶段 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //【8】刷新上下文 refreshContext(context); //【9】刷新上下文后操作,空方法 afterRefresh(context, applicationArguments); //停止计时 stopWatch.stop(); //打印日志 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //【10】发布“容器加载完成”事件给监听器 listeners.started(context); //【11】调用ApplicationRunner和CommandLineRunner的run方法,实现spring容器启动后需要做的一些东西,比如加载一些业务数据等 //默认是没有Runner要执行的 callRunners(context, applicationArguments); //发布"应用running"事件 listeners.running(context);
【1】创建临时上下文
【2】配置系统硬件属性
【3】获取监听器并启动:getRunListeners(args)
在META-INF/spring.factories文件下获取key=SpringApplicationRunListener的类路径获得org.springframework.boot.context.event.EventPublishingRunListener,
并通过class.forName反射实例化返回instances,然后通过new SpringApplicationRunListeners( log, instances, applicationStartup),把EventPublishingRunListener封装到SpringApplicationRunListeners的List集合里。
SpringApplicationRunListener负责在SpringBoot启动的不同阶段,使用内部ApplicationEventMulticaster来广播不同的SpringApplicationEven事件,传递给前面的ApplicatioinListener监听器实现类。比如,“刷新上下文准备阶段” 完成了,就向各个SpringApplicationListener监听器发布“刷新上下文准备阶段” 事件,让各个监听器此时执行对应的逻辑。
【4】准备容器环境:prepareEnvironment
上下文环境本质上就是环境信息的集合:系统变量、环境变量、JDK环境信息、命令行参数,默认变量,servlet相关配置变量,随机值、JNDI属性值,以及配置文件(比如application.properties)等。
这些环境变量是有优先级的。对这些信息加载进来,封装到environment对象中。
发布事件
具体prepareEnvironment方法:
//创建相应的应用环境 ConfigurableEnvironment environment = getOrCreateEnvironment(); //根据用户配置,配置environment应用环境 configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); //发布"上下文环境已准备好"事件,其中一个重要的监听器ConfigFileApplicationListener就是加载项目配置文件的 listeners.environmentPrepared(bootstrapContext, environment); //把PropertySources中默认的属性“defaultProperties”,移动到PropertySources的末尾。 DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); //将获取到的environment中的spring.main配置绑定到SpringApplication的source中 bindToSpringApplication(environment); //是否为自定义环境 if (!this.isCustomEnvironment) { //把ConfigurableEnvironment根据应用类型转换成对应对应的ApplicationEnviroment environment = convertEnvironment(environment); } // 解除附加的特定的环境配置(避免冲突) ConfigurationPropertySources.attach(environment); return environment;
创建相应的应用环境:getOrCreateEnvironment
根据webApplicationType实例化不同的环境类:
SERVLET -> new ApplicationServletEnvironment()
REACTIVE -> new ApplicationReactiveWebEnvironment()
other -> new ApplicationEnvironment()
配置environment应用环境:configureEnvironment
if (this.addConversionService) { environment.setConversionService(new ApplicationConversionService()); } //将main函数的args封装成SimpleCommandLinePropertySource加入环境中 configurePropertySources(environment, args); //激活相应配置文件,比如:设置spring-profiles.active=prod,封装成SimpleCommandLinePropertySource //在此激活application-prod.properties这个配置文件 configureProfiles(environment, args);
【5】打印spring的logo
【6】创建上下文和容器:createApplicationContext()
根据webApplicationType实例化不同的上下文ConfigurableApplicationContext实现类:
SERVLET -> new AnnotationConfigServletWebServerApplicationContext()
REACTIVE -> new AnnotationConfigReactiveWebServerApplicationContext()
other ->new AnnotationConfigApplicationContext()
于此同时,因为这几个实现类,继承了GenericApplicationContext,而GenericApplicationContext的构造函数中会创建beanFactory,故子类实例化,父类GenericApplicationContext也会跟着实例化,beanFactory其实际上就是IOC容器,类型为DefaultListableBeanFactory,即上下文中有了一个IOC容器
public GenericApplicationContext(DefaultListableBeanFactory beanFactory) { Assert.notNull(beanFactory, "BeanFactory must not be null"); this.beanFactory = beanFactory; }
【7】刷新上下文准备阶段prepareContext
-
在装载好了各种环境配置后,去刷新上下文里的配置;
-
并创建一些bean对象,其中包含启动类的bean对象创建。
//设置上下文环境 context.setEnvironment(environment); //执行容器后置处理:其实就是为ioc的转化器ApplicationConversionService设值 postProcessApplicationContext(context); //执行初始化器的initialize方法 applyInitializers(context); //listeners是SpringApplicationRunListeners,向各个监听器发送"容器已经准备好"的事件, listeners.contextPrepared(context); //发布“临时上下文关闭事件”,默认没有监听器监听此事件。 bootstrapContext.close(context); //打印日志信息 if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans //向IOC容器中添加args封装成ApplicationArguments的单例 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); //IOC容器添加打印springBootBanner单例 if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } //设置BeanDefinition是否能被重载(修改),默认设置为false if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } //是否懒加载,默认为false if (this.lazyInitialization) { //上下文添加一个处理bean后置处理器 context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()) } // Load the sources,实际上获取的只有启动类对象 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); //加载启动类的beanDefinition,并注入容器中 load(context, sources.toArray(new Object[0])); //发布“容器已加载完成”事件 listeners.contextLoaded(context);
【8】刷新上下文:refreshContext(context)
刷新之前,会在context上下文添加一个ApplicationContextClosedListener监听,就是为了在shutdown的时候,优雅的关闭应用。
刷新的核心代码如下:
//在应用程序启动期间标记步骤,并收集有关执行上下文数据和处理事件 StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // Prepare this context for refreshing //1.就是对一些属性赋值。 prepareRefresh(); // Tell the subclass to refresh the internal bean factory //2.就是获取beanFactory,即IOC容器,并刷新容器=为容器设置id ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context //3.容器初始化属性 prepareBeanFactory(beanFactory); // Allows post-processing of the bean factory in context subclasses. //4.设置beanDefinition的后置处理器、beanFactory的一些属性 postProcessBeanFactory(beanFactory); //在应用程序启动期间标记步骤,并收集有关执行上下文数据和处理事件 StartupStep beanPostProcess = . this.applicationStartup.start("spring.context.beans.post-process"); // Invoke factory processors registered as beans in the context. //5.调用容器中的工厂 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //6.注册所有bean的后置处理器 registerBeanPostProcessors(beanFactory); beanPostProcess.end(); //空方法 // Initialize message source for this context. // 7.初始化此上下文的消息源,比如国际化 initMessageSource(); // Initialize event multicaster for this context. //8.初始化应用事件发布器,并存在IOC容器中 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //9.开始刷新 onRefresh(); // Check for listener beans and register them. //10.注册新的监听器 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //11.实例化剩余非懒加载的单例bean finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. //12.刷新完成的收尾工作 finishRefresh();
1、刷新准备阶段:prepareRefresh
this.startupDate = System.currentTimeMillis(); //记录时间点 this.closed.set(false); //目前容器非close状态 this.active.set(true); //目前容器是活跃的 if (this.logger.isDebugEnabled()) { if (this.logger.isTraceEnabled()) { this.logger.trace("Refreshing " + this); } else { this.logger.debug("Refreshing " + this.getDisplayName()); } } this.initPropertySources(); //默认空方法,让AbstractApplicationContext子类重载 this.getEnvironment().validateRequiredProperties(); //校验一些必填配置是否可以被解析 //earlyApplicationListeners、applicationListeners两个监听器相互赋值 if (this.earlyApplicationListeners == null) { this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); } else { // Reset local application listeners to pre-refresh state. this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); } this.earlyApplicationEvents = new LinkedHashSet(); //初始化用于发布事件对象的集合
2、刷新并获取容器:obtainFreshBeanFactory
3、容器初始化属性:prepareBeanFactory(beanFactory)
beanFactory初始化的值:
1. 设置类加载器 :setBeanClassLoader
2.设置Spel表达式的解析器 :setBeanExpressionResolver
3.添加属性编辑器 :addPropertyEditorRegistrar 添加属性编辑器
4.添加Bean后置处理器:addBeanPostProcessor
5.设置忽略指定类的自动装配功能:ignoreDependencyInterface
6.自动装配是,指定对应接口的注入对象(一个接口可有多个对象):registerResolvableDependency
7.设置单例:registerSingleton
4、设置beanDefinition的后置处理器、IOC属性:postProcessBeanFactory
1.设置request、session作用域的注入对象
2.设置自动装配时接口注入指定对象
5.调用容器中的beanDefinition后置处理器:invokeBeanFactoryPostProcessors
Set<String> processedBeans = new HashSet<>(); if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); //注册两个beanDefinition后置处理器:CachingMetadataReaderFactoryPostProcessor、ConfigurationWarningsPostProcessor for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; //调用的是:CachingMetadataReaderFactoryPostProcessor类的该方法,作用是注册beanDefinition后置处理器 registryProcessor.postProcessBeanDefinitionRegistry(registry); registryProcessors.add(registryProcessor); } else { regularPostProcessors.add(postProcessor); } } List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered. //获取对应类型的后置处理器key:internalConfigurationAnnotationProcessor String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { //添加ConfigurationClassPostProcessor后置处理器 currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } //设置beanDefinition后置处理器得优先级 sortPostProcessors(currentRegistryProcessors, beanFactory); //收集要注册到容器得后置处理器,经过前面可得3个后置处理器。 registryProcessors.addAll(currentRegistryProcessors); //把ConfigurationClassPostProcessor后置处理器bean注册到容器 //这是自动装配的重点方法!!! invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); currentRegistryProcessors.clear(); // 执行到这里,重复上一次得操作 postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); currentRegistryProcessors.clear(); //中间都是一些重复类似上面的操作 …… // Clear cached merged bean definitions since the post-processors might have // modified the original metadata, e.g. replacing placeholders in values... //容器清除beanDefinition缓存,因为beanDefinition后置处理器有可能已经修改了beanDefinition的内容,比如占位符 beanFactory.clearMetadataCache();
①检索启动类注解入口,自动装配的关键
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
该方法顾名思义,就是调用Bean定义注册后处理器,形参:
currentRegistryProcessors :BeanDefinition注册后处理器集合,就一个ConfigurationClassPostProcessor类处理器
registry:BeanDefinition的注册中心,本质是就是IOC容器里有该注册中心Map
applicationStartup:记录器,用于记录程序执行
②调用接口实现方法
postProcessor.postProcessBeanDefinitionRegistry(registry);
该处理器为类型ConfigurationClassPostProcessor,是接口BeanDefinitionRegistryPostProcessor的实现类,如实现类:
CachingMetadataReaderFactoryPostProcessor.postProcessBeanDefinitionRegistry:是把beanDefinition注册到容器中;
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry:是处理有注解的beanDefinition,该方法实现逻辑:
一、在beanDefinition容器中,通过判断是否包含注解@component,拿到启动类
二、通过ConfigurationClassParser.parser解析
1:先解析到@SpringBootApplication,把启动类的beanDefinition封装成SourceClass,被用来更直接检测包含哪些注解的一个类
2:通过递归处理内部类,把有注解的类记录下来
3:解析标注@PropertySource的类,处理类成员的值
4:解析标注@ComponentScan的类,检测到启动类,在启动类所在包及子包进行扫描,直接或间接标注@component的类,都会被扫描到,并把对应类的beanDefinition注册到容器中,然后递归调用ConfigurationClassParser.parser进行解析。
5:解析标注@Import的类,递归解析自身和父注解的@import,解析到@import.value=ImportBeanDefinitionRegistrar 进行记录;解析到@import.value=ImportSelector进行记录,并通过调用selectImports方法把扫描配置文件META-INF/spring.factories,把里面的类扫描,并检测这类是否有注解,如果有,则也需要递归调用ConfigurationClassParser.parser进行解析
经过这一系列解析,就通过注解拿到了全部需要实例化注入到IOC容器的类
6、注册bean的后置处理器:registerBeanPostProcessors
注册方式:PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, applicationContent);
7、初始化此上下文的消息源:initMessageSource
比如:国际化
8、初始化应用事件发布器,并存在IOC中:initApplicationEventMulticaster
9、开始刷新:onRefresh
1.初始化主题来源ThemeSource
2.构造TomcatServletWebServer,并初始化server的配置,并注入到IOC容器中
10、注册监听器:registerListeners
11.实例化剩余非懒加载的单例:finishBeanFactoryInitialization
拿到容器中的beanDefinition集合,结合bean后置处理器进行实例化,并注入到IOC容器中
12.刷新完成的收尾工作:finishRefresh()
1.设置管理bean生命周期的LifecycleProcessor处理器
2.用LifecycleProcessor开始webServerGracefulShutdown、webServerStartStop两个Group 的生命周期管理
3.发布”容器刷新完成“事件
4.为MBeanServer注册LiveBeansView