写在文章前
本文干货十足,有任何问题可以加我微信 or 在下方留言
Spring boot 版本 2.0.4.RELEASE
希望大家不要无脑转载,我写这个花了很长时间哈,转自请备注转载地址
预先准备
Spring
1.BeanPostProcessor
如果我们想在Spring容器中完成bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理。
我们需要定义一个或多个BeanPostProcessor接口实现类,然后注册到Spring IoC容器中
2.BeanFactoryPostProcessor
注册一个BeanFactoryPostProcessor实例需要定义一个Java类来实现BeanFactoryPostProcessor接口,并重写该接口的postProcessorBeanFactory方法
3.ApplicationContextInitializer
调用该接口的initialize方法在ConfigurableApplicationContext#refresh之前
4.BeanNameAware
#setBeanName() 配置这个Bean的名称
5.InitializingBean
在properties注入完毕后会 调用#afterPropertiesSet()
6.DisposableBean
在Bean销毁时会调用#destroy()方法
7.BeanFactoryAware
获取setBeanFactory 在Bean填充后 初始化之前
Spring Bean 生命周期
copy from:https://www.cnblogs.com/redcool/p/6397398.html
初始化Bean开始
|
|
反射调用Bean的初始化方法实例化Bean
|
|
通过反射注入bean的属性值
|
|
Aware注入 比如BeanNameAware BeanClassLoaderAware
|
|
调用每个BeanPostInitializer接口中postProcessorBefore
|
|
调用初始化方法
|
|
调用每个BeanPostInitializer接口中postProcessorAfter
|
|
注册需要执行销毁方法的Bean
@Bean(initMethod = "init")
public MySpringBean mySpringBean(){
return new MySpringBean();
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import javax.annotation.PostConstruct;
/**
* @author <a href="mailto:boommanpro@gmail.com">BoomManPro</a>
* @date 2019/6/11 15:00
* @created by BoomManPro
*/
@Slf4j
public class MySpringBean implements BeanNameAware, BeanFactoryAware, InitializingBean, ApplicationContextAware , DisposableBean {
private ApplicationContext applicationContext;
@PostConstruct
public void constructInit(){
log.info(" constructInit......");
}
public MySpringBean() {
log.info("new MySpringBean......");
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
log.info("ApplicationContextAware-setApplicationContext......");
this.applicationContext = context;
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("InitializingBean-afterPropertiesSet......");
}
@Override
public void setBeanFactory(BeanFactory bf) throws BeansException {
log.info("BeanFactoryAware-setBeanFactory......");
}
@Override
public void setBeanName(String name) {
log.info("BeanNameAware-setBeanName......");
}
public void init() {
log.info("init-method......");
}
@Override
public void destroy() throws Exception {
log.info("DisposableBean-method......");
}
}
正文源码分析
1.Spring Boot项目启动 最终都是如下方式启动
// org.springframework.boot.SpringApplication 1258
new SpringApplication(primarySources).run(args);
所以第一步是创建一个 SpringApplication实例 然后执行run方法
2.下面来分析SpringApplication的构造函数
// org.springframework.boot.SpringApplication 263
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//resourceLoader默认传值为null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//利用ClassUtils来判断是否有Reactive相关依赖 最终判断spring程序的类型是什么 最终创建的ApplicatioonContext和这个有关系
//详见--->org.springframework.boot.SpringApplication#createApplicationContext 576
this.webApplicationType = deduceWebApplicationType();
//首先getSpringFactoriesInstances 是获取实例对象 获取实现了ApplicationContextInitializer.class的实例对象
//那么ApplicationContextInitializer接口的作用是什么呢?
/*
Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
他会在 ConfigurableApplicationContext#refresh()调用之前调用ApplicationContextInitializer#initialize()方法
具体使用方法
1.通过ConfigurableApplicationContext手动添加
2.在application.properties || application.yml中配置 context.initializer.classes=cn.boommanpro.MyApplicationContextInitializer
3.resources/META-INF/spring.factories 中配置 org.springframework.context.ApplicationContextInitializer
感觉第三种方式更优雅些,源码中大家在这么用
在我这个项目中通过Debug看到加载了6个ApplicationContextInitializer.class实现
通过IDEA快捷键 `Alt`+`F7` 查看他们的Usage
org/springframework/boot/spring-boot/2.0.4.RELEASE/spring-boot-2.0.4.RELEASE.jar!/META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
org/springframework/boot/spring-boot-autoconfigure/2.0.4.RELEASE/spring-boot-autoconfigure-2.0.4.RELEASE.jar!/META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
分别是
1.org.springframework.boot.context.config.DelegatingApplicationContextInitializer
//根据Environment context.initializer.classes取到 相关实现了ApplicationContextInitializer.class的class 并调用initializer放啊
2.org.springframework.boot.context.ContextIdApplicationContextInitializer
//为当前applicationContext设置Id 详细算法查看initialize方法
3.org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
//报告常见的未配置的错误 查看源码默认配置的是 org && org.springframework
4.org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
//添加ApplicationListener
5.org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
//配置BeanFactoryPostProcessor原始资源缓存 什么是原始资源 为什么这里要配置
6.org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
//条件报告评估记录监听器 Spring新出的应用位为什么启动不了的处理
综上所述 我认为ApplicationContextInitializer.class的作用是为给SpringContext在做一些配置 或者一些在Spring容器还未完全构建时才能做得一些事 他在refresh方法之前调用
总结一般会用到:
1.配置BeanFactoryPostProcessor
2.配置ConfigurableApplication#addApplicationListener()
3.根据Environment获取到的一些值做一些事情
*/
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//配置一些监听器 获取到的还是:
// org/springframework/boot/spring-boot-autoconfigure/2.0.4.RELEASE/spring-boot-autoconfigure-2.0.4.RELEASE.jar!/META-INF/spring.factories
// org/springframework/boot/spring-boot-autoconfigure/2.0.4.RELEASE/spring-boot-autoconfigure-2.0.4.RELEASE.jar!/META-INF/spring.factories
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//获取mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
3.然后我们分析run方法
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
* 其实这个注释表明了和这个方法的作用 启动一个SpringApplication 创建和刷新一个新的ApplicationContext 这里传入args作用为命令行传参
* 它传入一些参数的优先级和传入值可参考Spring官方文档:
* https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-external-config
* 注意: 之后的会覆盖之前的 哪个文件被最后加载,哪个才具有最高级别,
*/
public ConfigurableApplicationContext run(String... args) {
//后面会调用 stopWatch.getTotalTimeSeconds() 获取运行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//定义变量 context
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//配置是否初始化AWT 我个人认为是只有Swing编程会用到。。 所以Spring默认配置为true headless 如果你的项目用到了swing这里需要填写false,可能是个坑 自己填下吧
configureHeadlessProperty();
//获取SpringApplicationRunListener.class实例 貌似默认只有一个 EventPublishingRunListener Spring的事件驱动模型
//EventPublishingRunListener 又添加了之前的Listener
SpringApplicationRunListeners listeners = getRunListeners(args);
//执行监听 EventPublishingRunListener包含了之前的Listenner,之前的Listener调用了#onApplicationEvent
listeners.starting();
try {
//此时args派上了用场
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//再次调用 Listener调用了#onApplicationEvent
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//默认为默认为true 参考类 java.beans.SimpleBeanInfo 使用参考自: https://blog.csdn.net/cuiyaoqiang/article/details/53582382
configureIgnoreBeanInfo(environment);
//打印Banner图 可以参考 to: https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-banner
Banner printedBanner = printBanner(environment);
//正式创建ApplicationContext 根据之前的web类型判断 创建 ConfigurableApplicationContext的实现类
context = createApplicationContext();
//根据 ConfigurableApplicationContext 获取到 ConfigurableApplicationContext相关实例
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { Configurable.ApplicationContext.class }, context);
//准备context 大致做了以下事情 1.为context设置Environment 2.postProcess 我看到没做什么。。。 3.之前的Initializers启动#initialize()方法 4.listeners.contextPrepared 5.listeners.contextLoaded
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//核心中的核心啦 ~_~ ... 1.核心调用org.springframework.context.support.AbstractApplicationContext#refresh() 这部分在下面一起看下 2.启动一个hook 不知道干什么用
//
refreshContext(context);
//空方法什么都没有做
afterRefresh(context, applicationArguments);
//现在认为Spring完全启动完毕 打印启动时间
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//SpringApplicationRunListener#started()
listeners.started(context);
//如果有 ApplicationRunner || CommandLineRunner 相关的bean 启动它
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//如果Spring启动失败 打印信息哈
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//SpringApplicationRunListener#running()
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
4.核心 refresh
/**
* Load or refresh the persistent representation of the configuration,
* which might an XML file, properties file, or relational database schema.
* <p>As this is a startup method, it should destroy already created singletons
* if it fails, to avoid dangling resources. In other words, after invocation
* of that method, either all or no singletons at all should be instantiated.
* @throws BeansException if the bean factory could not be initialized
* @throws IllegalStateException if already initialized and multiple refresh
* attempts are not supported
*
* 加载或者刷新配置持久化标识 ,涉及到 xml properties 数据库
* 如果他是一个启动方法,当他启动失败,应该丢弃已经创建的单例对象,避免对资源占用 换句话说,要不全部启动,如失败,全部销毁?
* 这里大部分copy from: https://blog.csdn.net/andy_zhang2007/article/details/78492438
**/
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 为刷新工作做一些当前上下文 context 上的准备工作
//1.实现类可以做一些自己的prepareRefresh
//2.AbstractApplicationContext自己定义了关于Properties操作的相关模板
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// ApplicationContext 实现了 BeanFactory 接口,但是并非直接作为 Bean 容器。
// ApplicationContext 中真正直接作为 Bean 容器的是一个内部Bean工厂 BeanFactory,
// 通过其方法 getBeanFactory() 得到,此方法在 AbstractApplicationContext 中
// 被声明为 abstract, 其实现要求由实现子类提供。下面的语句 obtainFreshBeanFactory()
// 内部就是通过调用 getBeanFactory() 获得这个内部 Bean 工厂的。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 准备当前上下文使用的Bean容器 BeanFactory,设置其标准上下文特征,比如类加载器等
// 1. BeanFactory 的类加载器设置为当前上下文的类加载器
// 2. BeanFactory 的Bean表达式解析器设置为 new StandardBeanExpressionResolver()
// 3. BeanFactory 增加 BeanPostProcessror new ApplicationListenerDetector(this)
// 4.三个单例Bean被注册 : environment,systemProperties,systemEnvironment
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 在当前上下文使用的Bean容器BeanFactory的标准初始化完成后对其做一些修改。此时
// 所有的Bean definition都已经加载但是还没有 Bean 被创建。
// 当前上下文使用的Bean容器 BeanFactory 的 post process
// 1.当前上下文是 EmbeddedWebApplicationContext 时,
// 这个步骤中会对 beanFactory 注册一个 BeanPostProcessor :
// WebApplicationContextServletContextAwareProcessor
// 2.当前上下文是 AnnotationConfigEmbeddedWebApplicationContext 时,
// 如果设置了 basePackages,
// 这里会使用 AnnotatedBeanDefinitionReader扫描basePackages;
// 如果设置了 annotatedClasses,
// 这里会使用 ClassPathBeanDefinitionScanner登记annotatedClasses;
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 在 beanFactory 上调用 BeanFactoryPostProcessors,
// 当前上下文可能会有多个 BeanFactoryPostProcessor 需要应用在 beanFactory 上
// ****************************************************************
// 这里需要尤其注意区别 BeanFactoryPostProcessor 和 BeanPostProcessor
// BeanFactoryPostProcessor : 作用在 Bean定义 上,用来定制修改 Bean定义
// BeanPostProcessor :作用在 Bean实例 上,用来修改或者包装 Bean实例
// ****************************************************************
// 该方法实际上将实现委托出去 :
// PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注册 BeanPostProcessor
// 该步骤实际工作委托给工具类 PostProcessorRegistrationDelegate 的静态方法
// void registerBeanPostProcessors(
// ConfigurableListableBeanFactory beanFactory,
// AbstractApplicationContext applicationContext)
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
// 初始化当前上下文ApplicationContext要使用的 事件多播器
// ApplicationEventMulticaster applicationEventMulticaster。
//
// 如果容器中已经注册类型为ApplicationEventMulticaster并且名称为
// applicationEventMulticaster 的Bean,则直接使用;否则,
// 新建一个SimpleApplicationEventMulticaster实例并注册到
// Bean容器,Bean名称使用 applicationEventMulticaster。
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// AbstractApplicationContext 中 onRefresh() 方法实现为空,其目的就是
// 留给实现子类一个机会做一些上下文相关的刷新工作。在一些特殊Bean初始化时,单
// 例 singleton Bean 初始化之前该方法被调用。
// 1. 当前上下文是 EmbeddedWebApplicationContext 时,该步骤会创建一个
// 内置的 Servlet 容器, 具体参考 EmbeddedWebApplicationContext 的
// 方法 void createEmbeddedServletContainer()
onRefresh();
// Check for listener beans and register them.
// 1. 将外部指定到当前上下文的 ApplicationListener 实例关联到上下文多播器
// Q : 什么时候外部给当前上下文指定 ApplicationListener ?
// A : 举例说明,Springboot 应用 SpringApplication 的情况下,是在
// prepareContext()结尾时SpringApplicationRunListeners的
// contextLoaded() 调用中发生的,此时正在广播事件
// ApplicationPreparedEvent
// 2. 将实现了 ApplicationListener 接口的所有 Bean 关联到上下文多播器
// 3. 如果上下文属性earlyApplicationEvents中有要通知的事件,广播出去
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 完成 BeanFactory 的初始化工作
// 1.BeanFactory冻结所有的Bean定义:不再可以修改或者做post process操作
// 2.确保所有的non-lazy-init单例Bean被初始化,也包括FactoryBean
// 3.如果所初始化的单例Bean实现了接口SmartInitializingSingleton,调用
// 其方法 afterSingletonsInstantiated()
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 1. 初始化生命周期处理器 LifecycleProcessor, 使用已经存在的Bean或者
// 一个新的DefaultLifecycleProcessor实例;
// 2. 生命周期处理器 LifecycleProcessor 上传播 refresh 事件
// 3. 发布事件 ContextRefreshedEvent
// 4. 如果存在 LiveBeansView MBean 的话,关联到当前上下文
// 当前上下文是EmbeddedWebApplicationContext的情况下,还会:
// 5. 启动EmbeddedServletContainer,比如启动内置 tomcat容器
// 6. 发布事件 EmbeddedServletContainerInitializedEvent
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();
}
}
}
总结
SpringApplication启动分为两步
1.准备阶段
2.启动阶段
准备又分为构造准备与其他Build配置
2.启动阶段也有部分准备配置
大致准备了哪些东西呢
1.Environment相关参数 值 配置文件等
2.Initializers
3.Listener
4.PostProcess
3.核心Refresh启动
SpringApplication的run方法的实现
该方法的主要流程大体可以归纳如下:
1) 如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:
①根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
②使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
③使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
④推断并设置main方法的定义类。
2) SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,方法执行伊始,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。
3) 创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。
4) 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。
5) 如果SpringApplication的showBanner属性被设置为true,则打印banner。
6) 根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。
7) ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。
8) 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。
9) 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。
10) 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。
11) 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。
12) 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。
13) 正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)
单词学习
hierarchical 英 /haɪə’rɑːkɪk(ə)l/ adj. 分层的;等级体系的