1.ApplicationContextInitializer
1 | org.springframework.context.ApplicationContextInitializer |
这是整个spring容器在刷新之前初始化ConfigurableApplicationContext
的回调接口,简单来说,就是在容器刷新之前调用此类的initialize
方法。这个点允许被用户自己扩展。用户可以在整个spring容器还没被初始化之前做一些事情。
可以想到的场景可能为,在最开始激活一些配置,或者利用这时候class还没被类加载器加载的时机,进行动态字节码注入等操作。
扩展方式为:
1 2 3 4 5 6 | 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
2.BeanDefinitionRegistryPostProcessor
1 | org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor |
这个接口在读取项目中的beanDefinition
之后执行,提供一个补充的扩展点
使用场景:你可以在这里动态注册自己的beanDefinition
,可以加载classpath之外的bean
扩展方式为:
1 2 3 4 5 6 7 8 9 10 11 | 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" ); } } |
3 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]"); } }
4 InstantiationAwareBeanPostProcessor
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
该接口继承了BeanPostProcess
接口,区别如下:
<font color="red">BeanPostProcess
接口只在bean的初始化阶段进行扩展(注入spring上下文前后),而InstantiationAwareBeanPostProcessor
接口在此基础上增加了3个方法,把可扩展的范围增加了实例化阶段和属性注入阶段。</font>
该类主要的扩展点有以下5个方法,主要在bean生命周期的两大阶段:<font color=red>实例化阶段</font>和<font color=red>初始化阶段</font>,下面一起进行说明,按调用顺序为:
postProcessBeforeInstantiation
:实例化bean之前,相当于new这个bean之前postProcessAfterInstantiation
:实例化bean之后,相当于new这个bean之后postProcessPropertyValues
:bean已经实例化完成,在属性注入时阶段触发,@Autowired
,@Resource
等注解原理基于此方法实现postProcessBeforeInitialization
:初始化bean之前,相当于把bean注入spring上下文之前postProcessAfterInitialization
:初始化bean之后,相当于把bean注入spring上下文之后-
使用场景:这个扩展点非常有用 ,无论是写中间件和业务中,都能利用这个特性。比如对实现了某一类接口的bean在各个生命期间进行收集,或者对某个类型的bean进行统一的设值等等。
扩展方式为:

1 public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { 2 3 @Override 4 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 5 System.out.println("[TestInstantiationAwareBeanPostProcessor] before initialization " + beanName); 6 return bean; 7 } 8 9 @Override 10 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 11 System.out.println("[TestInstantiationAwareBeanPostProcessor] after initialization " + beanName); 12 return bean; 13 } 14 15 @Override 16 public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { 17 System.out.println("[TestInstantiationAwareBeanPostProcessor] before instantiation " + beanName); 18 return null; 19 } 20 21 @Override 22 public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { 23 System.out.println("[TestInstantiationAwareBeanPostProcessor] after instantiation " + beanName); 24 return true; 25 } 26 27 @Override 28 public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { 29 System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessPropertyValues " + beanName); 30 return pvs; 31 }
5 BeanFactoryAware
org.springframework.beans.factory.BeanFactoryAware
这个类只有一个触发点,发生在bean的实例化之后,注入属性之前,也就是Setter之前。这个类的扩展点方法为setBeanFactory
,可以拿到BeanFactory
这个属性。
使用场景为,你可以在bean实例化之后,但还未初始化之前,拿到 BeanFactory
,在这个时候,可以对每个bean作特殊化的定制。也或者可以把BeanFactory
拿到进行缓存,日后使用。
扩展方式为:

1 public class TestBeanFactoryAware implements BeanFactoryAware { 2 @Override 3 public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 4 System.out.println("[TestBeanFactoryAware] " + beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName()); 5 } 6 }
6 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
等接口,也可以用来做相关接口的事情。
7BeanNameAware
org.springframework.beans.factory.BeanNameAware
可以看到,这个类也是Aware扩展的一种,触发点在bean的初始化之前,也就是postProcessBeforeInitialization
之前,这个类的触发点方法只有一个:setBeanName
使用场景为:用户可以扩展这个点,在初始化bean之前拿到spring容器中注册的的beanName,来自行修改这个beanName的值。
扩展方式为:

1 public class NormalBeanA implements BeanNameAware{ 2 public NormalBeanA() { 3 System.out.println("NormalBean constructor"); 4 } 5 6 @Override 7 public void setBeanName(String name) { 8 System.out.println("[BeanNameAware] " + name); 9 } 10 }
8 @PostConstruct
javax.annotation.PostConstruct
这个并不算一个扩展点,其实就是一个标注。其作用是在bean的初始化阶段,如果对一个方法标注了@PostConstruct
,会先调用这个方法。这里重点是要关注下这个标准的触发点,这个触发点是在postProcessBeforeInitialization
之后,InitializingBean.afterPropertiesSet
之前。
使用场景:用户可以对某一方法进行标注,来进行初始化某一个属性
扩展方式为:

1 public class NormalBeanA { 2 public NormalBeanA() { 3 System.out.println("NormalBean constructor"); 4 } 5 6 @PostConstruct 7 public void init(){ 8 System.out.println("[PostConstruct] NormalBeanA"); 9 } 10 }
9 InitializingBean
org.springframework.beans.factory.InitializingBean
这个类,顾名思义,也是用来初始化bean的。InitializingBean
接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet
方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。这个扩展点的触发时机在postProcessAfterInitialization
之前。
使用场景:用户实现此接口,来进行系统启动的时候一些业务指标的初始化工作。
扩展方式为:

1 public class NormalBeanA implements InitializingBean{ 2 @Override 3 public void afterPropertiesSet() throws Exception { 4 System.out.println("[InitializingBean] NormalBeanA"); 5 } 6 }
10 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处理用户请求结束后,系统会自动触发该事件
第一步:创建一个火灾事件类
火灾事件类继承ApplicationEvent

1 // 火灾事件 2 public class FireEvent extends ApplicationEvent { 3 4 public FireEvent(String source){ 5 super(source); 6 } 7 8 }
第二步:创建火灾事件的监听器
打119的火灾事件的监听器:

1 public class Call119FireEventListener implements ApplicationListener<FireEvent> { 2 3 @Override 4 public void onApplicationEvent(FireEvent event){ 5 System.out.println("打119"); 6 } 7 8 }
救人的火灾事件的监听器:

1 public class SavePersonFireEventListener implements ApplicationListener<FireEvent> { 2 3 @Override 4 public void onApplicationEvent(FireEvent event){ 5 System.out.println("救人"); 6 } 7 8 }
事件和对应的监听都有了,接下来进行测试:

1 public class Application { 2 3 public static void main(String[] args){ 4 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); 5 //将 事件监听器 注册到容器中 6 applicationContext.register(Call119FireEventListener.class); 7 applicationContext.register(SavePersonFireEventListener.class); 8 applicationContext.refresh(); 9 10 // 发布着火的事件,触发监听 11 applicationContext.publishEvent(new FireEvent("着火了")); 12 } 13 14 }
将两个事件注册到Spring容器中,然后发布FireEvent事件
1 2 | 打 119 救人 |
本文摘自https://segmentfault.com/a/1190000023033670
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步