1.背景
Spring的核心思想就是容器,当容器refresh的时候,内部看上去惊涛骇浪,其实外部则是一片风平浪静,汪洋一片。Springboot更是封装了Spring,遵循约定大于配置,加上主动拆卸的机制。很多时候咱们只有援用了一个依赖,简直是零配置就能实现一个性能的拆卸。
我十分喜爱这种主动拆卸的机制,所以在本人开发中间件和公共依赖工具的时候也会用到这个个性。让使用者以最小的代价接入。想要把主动拆卸玩的转,就必须要理解spring对于bean的结构生命周期以及各个扩大接口。当然理解了bean的各个生命周期也能促成咱们加深对spring的了解。业务代码也能正当利用这些扩大点写出更加丑陋的代码。
在网上搜寻spring扩大点,发现很少有博文说的很全的,只有一些罕用的扩大点的阐明。
所以在这篇文章里,我总结了简直Spring & Springboot所有的扩大接口,以及各个扩大点的应用场景。并且整顿出了一个bean在spring外部从被加载到最初初始化实现所有可扩大点的顺序调用图。从而咱们也能窥探到bean是如何一步步加载到spring容器中的。
2.可扩大的接口启动调用程序图
以下是我整顿的spring容器中Bean的生命周期内所有可扩大的点的调用程序,上面会一个个剖析
3.ApplicationContextInitializer
org.springframework.context.ApplicationContextInitializer
这是整个spring容器在刷新之前初始化 ConfigurableApplicationContext
的回调接口,简略来说,就是在容器刷新之前调用此类的 initialize
办法。这个点容许被用户本人扩大。用户能够在整个spring容器还没被初始化之前做一些事件。
能够想到的场景可能为,在最开始激活一些配置,或者利用这时候class还没被类加载器加载的机会,进行动静字节码注入等操作。
扩大形式为:
public class TestApplicationContextInitializer implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext applicationContext) { System.out.println("[ApplicationContextInitializer]"); } }
因为这时候spring容器还没被初始化,所以想要本人的扩大的失效,有以下三种形式:
- 在启动类中用
springApplication.addInitializers(new TestApplicationContextInitializer())
语句退出 - 配置文件配置
context.initializer.classes=com.example.demo.TestApplicationContextInitializer
- Spring SPI扩大,在spring.factories中退出
org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer
4.BeanDefinitionRegistryPostProcessor
org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
这个接口在读取我的项目中的 beanDefinition
之后执行,提供一个补充的扩大点
应用场景:你能够在这里动静注册本人的 beanDefinition
,能够加载classpath之外的bean
扩大形式为:
public class TestBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanDefinitionRegistry"); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanFactory"); } }
5.BeanFactoryPostProcessor
org.springframework.beans.factory.config.BeanFactoryPostProcessor
这个接口是 beanFactory
的扩大接口,调用机会在spring在读取 beanDefinition
信息之后,实例化bean之前。
在这个机会,用户能够通过实现这个扩大接口来自行处理一些货色,比方批改曾经注册的 beanDefinition
的元信息。
扩大形式为:
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("[BeanFactoryPostProcessor]"); } }
6.InstantiationAwareBeanPostProcessor
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
该接口继承了 BeanPostProcess
接口,区别如下:
BeanPostProcess
接口只在bean的初始化阶段进行扩大(注入spring上下文前后),而 InstantiationAwareBeanPostProcessor
接口在此基础上减少了3个办法,把可扩大的范畴减少了实例化阶段和属性注入阶段。
该类次要的扩大点有以下5个办法,次要在bean生命周期的两大阶段: 实例化阶段 和 初始化阶段 ,上面一起进行阐明,按调用程序为:
postProcessBeforeInstantiation
:实例化bean之前,相当于new这个bean之前postProcessAfterInstantiation
:实例化bean之后,相当于new这个bean之后postProcessPropertyValues
:bean曾经实例化实现,在属性注入时阶段触发,@Autowired
,@Resource
等注解原理基于此办法实现postProcessBeforeInitialization
:初始化bean之前,相当于把bean注入spring上下文之前postProcessAfterInitialization
:初始化bean之后,相当于把bean注入spring上下文之后
应用场景:这个扩大点十分有用 ,无论是写中间件和业务中,都能利用这个个性。比方对实现了某一类接口的bean在各个生命期间进行收集,或者对某个类型的bean进行对立的设值等等。
扩大形式为:
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("[TestInstantiationAwareBeanPostProcessor] before initialization " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("[TestInstantiationAwareBeanPostProcessor] after initialization " + beanName); return bean; } @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { System.out.println("[TestInstantiationAwareBeanPostProcessor] before instantiation " + beanName); return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { System.out.println("[TestInstantiationAwareBeanPostProcessor] after instantiation " + beanName); return true; } @Override public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessPropertyValues " + beanName); return pvs; }
7.SmartInstantiationAwareBeanPostProcessor
org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor
该扩大接口有3个触发点办法:
predictBeanType
:该触发点产生在postProcessBeforeInstantiation
之前(在图上并没有表明,因为个别不太须要扩大这个点),这个办法用于预测Bean的类型,返回第一个预测胜利的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)
时当通过bean的名字无奈失去bean类型信息时就调用该回调办法来决定类型信息。determineCandidateConstructors
:该触发点产生在postProcessBeforeInstantiation
之后,用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表。用户能够扩大这个点,来自定义抉择相应的结构器来实例化这个bean。getEarlyBeanReference
:该触发点产生在postProcessAfterInstantiation
之后,当有循环依赖的场景,当bean实例化好之后,为了避免有循环依赖,会提前裸露回调办法,用于bean实例化的后置解决。这个办法就是在提前裸露的回调办法中触发。
扩大形式为:
public class TestSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor { @Override public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException { System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] predictBeanType " + beanName); return beanClass; } @Override public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException { System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] determineCandidateConstructors " + beanName); return null; } @Override public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] getEarlyBeanReference " + beanName); return bean; } }
8.BeanFactoryAware
org.springframework.beans.factory.BeanFactoryAware
这个类只有一个触发点,产生在bean的实例化之后,注入属性之前,也就是Setter之前。这个类的扩大点办法为 setBeanFactory
,能够拿到 BeanFactory
这个属性。
应用场景为,你能够在bean实例化之后,但还未初始化之前,拿到 BeanFactory
,在这个时候,能够对每个bean作特殊化的定制。也或者能够把 BeanFactory
拿到进行缓存,日后应用。
扩大形式为:
public class TestBeanFactoryAware implements BeanFactoryAware { @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("[TestBeanFactoryAware] " + beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName()); } }
9.ApplicationContextAwareProcessor
org.springframework.context.support.ApplicationContextAwareProcessor
该类自身并没有扩大点,然而该类外部却有6个扩大点可供实现 ,这些类触发的机会在bean实例化之后,初始化之前
能够看到,该类用于执行各种驱动接口,在bean实例化之后,属性填充之后,通过执行以上红框标出的扩大接口,来获取对应容器的变量。 所以这里应该来说是有6个扩大点 ,这里就放一起来说了
EnvironmentAware
:用于获取EnviromentAware
的一个扩大类,这个变量十分有用, 能够取得零碎内的所有参数。当然集体认为这个Aware没必要去扩大,因为spring外部都能够通过注入的形式来间接取得。EmbeddedValueResolverAware
:用于获取StringValueResolver
的一个扩大类,StringValueResolver
用于获取基于String
类型的properties的变量,个别咱们都用@Value
的形式去获取,如果实现了这个Aware接口,把StringValueResolver
缓存起来,通过这个类去获取String
类型的变量,成果是一样的。ResourceLoaderAware
:用于获取ResourceLoader
的一个扩大类,ResourceLoader
能够用于获取classpath内所有的资源对象,能够扩大此类来拿到ResourceLoader
对象。ApplicationEventPublisherAware
:用于获取ApplicationEventPublisher
的一个扩大类,ApplicationEventPublisher
能够用来公布事件,联合ApplicationListener
来独特应用,下文在介绍ApplicationListener
时会具体提到。这个对象也能够通过spring注入的形式来取得。MessageSourceAware
:用于获取MessageSource
的一个扩大类,MessageSource
次要用来做国际化。ApplicationContextAware
:用来获取ApplicationContext
的一个扩大类,ApplicationContext
应该是很多人十分相熟的一个类了,就是spring上下文管理器,能够手动的获取任何在spring上下文注册的bean,咱们常常扩大这个接口来缓存spring上下文,包装成静态方法。同时ApplicationContext
也实现了BeanFactory
,MessageSource
,ApplicationEventPublisher
等接口,也能够用来做相干接口的事件。
10.BeanNameAware
org.springframework.beans.factory.BeanNameAware
能够看到,这个类也是Aware扩大的一种,触发点在bean的初始化之前,也就是 postProcessBeforeInitialization
之前,这个类的触发点办法只有一个: setBeanName
应用场景为:用户能够扩大这个点,在初始化bean之前拿到spring容器中注册的的beanName,来自行批改这个beanName的值。
扩大形式为:
public class NormalBeanA implements BeanNameAware{ public NormalBeanA() { System.out.println("NormalBean constructor"); } @Override public void setBeanName(String name) { System.out.println("[BeanNameAware] " + name); } }
11.@PostConstruct
javax.annotation.PostConstruct
这个并不算一个扩大点,其实就是一个标注。其作用是在bean的初始化阶段,如果对一个办法标注了 @PostConstruct
,会先调用这个办法。这里重点是要关注下这个规范的触发点,这个触发点是在 postProcessBeforeInitialization
之后, InitializingBean.afterPropertiesSet
之前。
应用场景:用户能够对某一办法进行标注,来进行初始化某一个属性
扩大形式为:
public class NormalBeanA { public NormalBeanA() { System.out.println("NormalBean constructor"); } @PostConstruct public void init(){ System.out.println("[PostConstruct] NormalBeanA"); } }
12.InitializingBean
org.springframework.beans.factory.InitializingBean
这个类,顾名思义,也是用来初始化bean的。 InitializingBean
接口为bean提供了初始化办法的形式,它只包含 afterPropertiesSet
办法,但凡继承该接口的类,在初始化bean的时候都会执行该办法。这个扩大点的触发机会在 postProcessAfterInitialization
之前。
应用场景:用户实现此接口,来进行系统启动的时候一些业务指标的初始化工作。
扩大形式为:
public class NormalBeanA implements InitializingBean{ @Override public void afterPropertiesSet() throws Exception { System.out.println("[InitializingBean] NormalBeanA"); } }
13.FactoryBean
org.springframework.beans.factory.FactoryBean
个别状况下,Spring通过反射机制利用bean的class属性指定干线类去实例化bean,在某些状况下,实例化Bean过程比较复杂,如果依照传统的形式,则须要在bean中提供大量的配置信息。配置形式的灵活性是受限的,这时采纳编码的形式可能会失去一个简略的计划。Spring为此提供了一个 org.springframework.bean.factory.FactoryBean
的工厂类接口,用户能够通过实现该接口定制实例化Bean的逻辑。
FactoryBean
接口对于Spring框架来说占用重要的位置,Spring本身就提供了70多个 FactoryBean
的实现。它们暗藏了实例化一些简单bean的细节,给下层利用带来了便当。从Spring3.0开始, FactoryBean
开始反对泛型,即接口申明改为 FactoryBean<T>
的模式
应用场景:用户能够扩大这个类,来为要实例化的bean作一个代理,比方为该对象的所有的办法作一个拦挡,在调用前后输入一行log,模拟 ProxyFactoryBean
的性能。扩大:SpringBoot内容聚合
扩大形式为:
public class TestFactoryBean implements FactoryBean<TestFactoryBean.TestFactoryInnerBean> { @Override public TestFactoryBean.TestFactoryInnerBean getObject() throws Exception { System.out.println("[FactoryBean] getObject"); return new TestFactoryBean.TestFactoryInnerBean(); } @Override public Class<?> getObjectType() { return TestFactoryBean.TestFactoryInnerBean.class; } @Override public boolean isSingleton() { return true; } public static class TestFactoryInnerBean{ } }
14.SmartInitializingSingleton
org.springframework.beans.factory.SmartInitializingSingleton
这个接口中只有一个办法 afterSingletonsInstantiated
,其作用是是 在spring容器治理的所有单例对象(非懒加载对象)初始化实现之后调用的回调接口。其触发机会为 postProcessAfterInitialization
之后。
应用场景:用户能够扩大此接口在对所有单例对象初始化结束后,做一些后置的业务解决。
扩大形式为:
public class TestSmartInitializingSingleton implements SmartInitializingSingleton { @Override public void afterSingletonsInstantiated() { System.out.println("[TestSmartInitializingSingleton]"); } }
15.CommandLineRunner
org.springframework.boot.CommandLineRunner
这个接口也只有一个办法: run(String... args)
,触发机会为整个我的项目启动结束后,主动执行。如果有多个 CommandLineRunner
,能够利用 @Order
来进行排序。
应用场景:用户扩大此接口,进行启动我的项目之后一些业务的预处理。
扩大形式为:
public class TestCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("[TestCommandLineRunner]"); } }
16.DisposableBean
org.springframework.beans.factory.DisposableBean
这个扩大点也只有一个办法: destroy()
,其触发机会为当此对象销毁时,会主动执行这个办法。比如说运行 applicationContext.registerShutdownHook
时,就会触发这个办法。
扩大形式为:
public class NormalBeanA implements DisposableBean { @Override public void destroy() throws Exception { System.out.println("[DisposableBean] NormalBeanA"); } }
17.ApplicationListener
org.springframework.context.ApplicationListener
精确的说,这个应该不算spring&springboot当中的一个扩大点, ApplicationListener
能够监听某个事件的 event
,触发机会能够穿插在业务办法执行过程中,用户能够自定义某个业务事件。
然而spring外部也有一些内置事件,这种事件,能够穿插在启动调用中。咱们也能够利用这个个性,来本人做一些内置事件的监听器来达到和后面一些触发点大致相同的事件。
接下来列举下spring次要的内置事件:
ContextRefreshedEvent
ApplicationContext 被初始化或刷新时,该事件被公布。这也能够在
ConfigurableApplicationContext
接口中应用refresh()
办法来产生。此处的初始化是指:所有的Bean被胜利装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext
容器已就绪可用。ContextStartedEvent
当应用
ConfigurableApplicationContext
(ApplicationContext子接口)接口中的start()
办法启动ApplicationContext
时,该事件被公布。你能够考察你的数据库,或者你能够在承受到这个事件后重启任何进行的应用程序。ContextStoppedEvent
当应用
ConfigurableApplicationContext
接口中的stop()
进行ApplicationContext
时,公布这个事件。你能够在承受到这个事件后做必要的清理的工作ContextClosedEvent
当应用
ConfigurableApplicationContext
接口中的close()
办法敞开ApplicationContext
时,该事件被公布。一个已敞开的上下文达到生命周期末端;它不能被刷新或重启RequestHandledEvent
这是一个 web-specific 事件,通知所有 bean HTTP 申请曾经被服务。只能利用于应用DispatcherServlet的Web利用。在应用Spring作为前端的MVC控制器时,当Spring解决用户申请完结后,零碎会主动触发该事件
18.最初
咱们从这些spring&springboot的扩大点当中,大抵能够窥视到整个bean的生命周期。在业务开发或者写中间件业务的时候,能够正当利用spring提供给咱们的扩大点,在spring启动的各个阶段内做一些事件。以达到自定义初始化的目标。此篇总结,如果有谬误或者疏漏的中央,恳请斧正。