强大!牢记这16个Spring Boot扩展接口,代码优雅提升一个层次

科技   2024-11-11 07:30   河北  


强大!牢记这16个Spring Boot扩展接口,代码优雅提升一个层次

Spring的核心理念是它的容器。当容器刷新时,表面看似风平浪静,内部却如同风起云涌的大海,广阔而汹涌。SpringBoot更进一步,将Spring封装起来,遵循“约定优于配置”的原则,并结合自动配置机制。通常情况下,只需添加一个依赖,我们就能以最小配置甚至零配置实现功能。

我尤其喜欢自动配置机制,因此在开发中间件和通用依赖工具时经常使用这个功能。这种方法允许用户以最低的成本进行集成。要掌握自动配置,必须了解Spring Bean的构建生命周期以及各种扩展接口。当然,理解Bean的不同生命周期也能帮助更深入地理解Spring,业务代码也可以合理地利用这些扩展点编写更加优雅的代码。

在本文中,我总结了几乎所有SpringSpringBoot的扩展接口及其应用场景。同时,我整理了Bean从加载到最终初始化过程中所有可扩展点的时序图,这使我们能够一窥Bean是如何逐步加载到Spring容器中的。

文章内容较长,请耐心阅读!

启动期间可扩展接口调用的时序图

以下是Bean在Spring容器中的生命周期中所有可扩展点的顺序图。

接下来我将逐一分析每一个。

ApplicationContextInitializer

org.springframework.context.ApplicationContextInitializer

这是一个用于在整个Spring容器刷新之前初始化ConfigurableApplicationContext的回调接口。简单来说,在容器刷新之前,会调用该类的initialize方法,此时允许用户扩展。用户可以在整个Spring容器初始化之前做一些事情。

可能的使用场景包括在最初激活某些配置,或利用类加载器加载类之前的时机执行如动态字节码注入等操作。

扩展方法如下:

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

BeanDefinitionRegistryPostProcessor

org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor

该接口是在读取项目中的beanDefinition后执行的,提供了一个补充的扩展点。

使用场景:你可以在此处动态注册自定义的beanDefinition,并加载类路径之外的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");
}
}

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]");
}
}

InstantiationAwareBeanPostProcessor

org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor

此接口继承自BeanPostProcessor接口,区别在于:

BeanPostProcessor接口只在Bean的初始化阶段(即注入Spring上下文之前和之后)扩展,而InstantiationAwareBeanPostProcessor接口增加了三个方法,扩展了Bean的实例化和属性注入阶段的作用范围。

该类的主要扩展点是以下五个方法,它们在Bean生命周期的实例化阶段初始化阶段发挥作用。按调用顺序如下:

  • postProcessBeforeInstantiation: 在实例化Bean之前,相当于在创建(new)Bean之前。

  • postProcessAfterInstantiation: 在实例化Bean之后,相当于创建(new)Bean之后。

  • postProcessPropertyValues: Bean实例化后,在属性注入阶段触发。像@Autowired@Resource等注解的原理就基于这个方法。

  • postProcessBeforeInitialization: Bean初始化之前,相当于在Bean注入Spring上下文之前。

  • postProcessAfterInitialization: Bean初始化之后,相当于在Bean注入Spring上下文之后。

使用场景:该扩展点在中间件开发和业务逻辑中都非常有用。例如,可以在Bean生命周期的不同阶段收集实现某个接口的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;
}

SmartInstantiationAwareBeanPostProcessor

org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor

该扩展接口有三个触发方法:

  • predictBeanType: 在postProcessBeforeInstantiation之前触发(在时序图中未标出,通常不需要扩展此点)。该方法用于预测Bean的类型,返回第一个成功预测的Class类型,如果无法预测则返回null。当调用BeanFactory.getType(name)且无法通过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;
}
}

BeanFactoryAware

org.springframework.beans.factory.BeanFactoryAware

此类只有一个触发点,即在Bean实例化后、属性注入之前(即Setter方法之前)触发。该类的扩展点方法是setBeanFactory,当用户想要获取当前BeanFactory的引用时,可以扩展此接口来获取。

扩展方法如下:

public class TestBeanFactoryAware implements BeanFactoryAware {      
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("[TestBeanFactoryAware] " + beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName());
}
}

ApplicationContextAwareProcessor

org.springframework.context.support.ApplicationContextAwareProcessor

虽然这个类本身没有扩展点,但它内部提供了六个扩展点用于实现。这些扩展点是在bean实例化后、初始化之前触发的。

正如你所看到的,这个类用于在bean实例化并填充属性之后执行各种驱动接口。通过执行上述突出显示的扩展接口,可以获得相应的容器变量。因此,这里实际上有六个扩展点,我将一起讨论:

  • EnvironmentAware: 用于获取 EnvironmentAware 的扩展类。这个变量非常有用,可以访问系统中的所有参数。个人认为,没必要扩展这个 Aware,因为Spring内部已经支持通过注入直接获取。

  • EmbeddedValueResolverAware: 用于获取 StringValueResolver 的扩展类。StringValueResolver 用于获取基于字符串的属性变量。通常我们使用 @Value 注解获取这些变量,但如果实现了这个 Aware 接口并缓存 StringValueResolver,就可以使用它来获取基于字符串的变量,效果相同。

  • ResourceLoaderAware: 用于获取 ResourceLoader 的扩展类。ResourceLoader 可以访问类路径中的所有资源对象。你可以扩展这个类来获取 ResourceLoader 对象。

  • ApplicationEventPublisherAware: 用于获取 ApplicationEventPublisher 的扩展类。ApplicationEventPublisher 用于发布事件,通常与 ApplicationListener 结合使用,我将在后面详细介绍。此对象也可以通过Spring注入获得。

  • MessageSourceAware: 用于获取 MessageSource 的扩展类。MessageSource 主要用于国际化。

  • ApplicationContextAware: 用于获取 ApplicationContext 的扩展类。许多人都熟悉 ApplicationContext,它是Spring的上下文管理器,允许手动访问Spring上下文中注册的任何bean。我们经常扩展这个接口来缓存Spring上下文,并将其包装成静态方法。此外,ApplicationContext 还实现了 BeanFactoryMessageSourceApplicationEventPublisher 等接口,可以用于相关任务。

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);
}
}

@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");
}
}

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");
}
}

FactoryBean

org.springframework.beans.factory.FactoryBean

在正常情况下,Spring使用反射机制和bean的类属性来实例化bean。但在某些情况下,bean的实例化过程可能非常复杂,如果按照传统方式进行,则需要在bean中配置大量信息,配置方法的灵活性有限。在这种情况下,编码的方式可能会更简单。为此,Spring提供了 org.springframework.beans.factory.FactoryBean 接口,允许用户自定义bean实例化的逻辑。

FactoryBean 接口在Spring框架中具有重要地位。Spring自身提供了超过70种 FactoryBean实现,它们隐藏了某些复杂bean实例化的细节,给高级应用带来了方便。从Spring 3.0开始,FactoryBean 支持泛型,接口声明变为 FactoryBean<T>

使用场景:用户可以扩展此类,为他们希望实例化的bean创建代理。例如,他们可以拦截对象的所有方法,在每次调用之前和之后输出一行日志,模拟 ProxyFactoryBean 的功能。

扩展方法:

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{

}
}

SmartInitializingSingleton

org.springframework.beans.factory.SmartInitializingSingleton

这个接口只有一个方法 afterSingletonsInstantiated,其目的是作为回调接口,在Spring容器管理的所有单例对象(非延迟加载对象)初始化后调用。它的触发点是在 postProcessAfterInitialization 之后。

使用场景:用户可以扩展此接口,在所有单例对象完全初始化后执行一些后处理业务。

扩展方法:

public class TestSmartInitializingSingleton implements SmartInitializingSingleton {      
@Override
public void afterSingletonsInstantiated() {
System.out.println("[TestSmartInitializingSingleton]");
}
}

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]");
}
}

** 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");
}
}

ApplicationListener

org.springframework.context.ApplicationListener

严格来说,这不应被视为Spring & Spring Boot中的扩展点。ApplicationListener 可以监听特定的事件 (event)。触发时机可以穿插在业务方法执行过程中,允许用户定义自己的业务事件。

然而,Spring内部有一些内置事件。这些事件可以与启动过程交织在一起。我们也可以利用此功能,为这些内置事件创建自己的监听器,达到与之前某些触发点类似的效果。

让我们列出Spring中的一些主要内置事件:

  • ContextRefreshedEvent: 当 ApplicationContext 初始化或刷新时发布此事件。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法时发生。这里的初始化是指所有Beans成功加载、后置处理器Beans被检测并激活、所有单例Beans被预实例化,并且 ApplicationContext 容器已准备好使用。

  • ContextStartedEvent : 当使用ConfigurableApplicationContext(ApplicationContext的子接口)中的start()方法启动ApplicationContext时发布此事件。在spring中,您可以使用start()stop()方法控制ApplicationContext的生命周期。启动容器后,可以通过stop()停止容器。当容器启动时,您可以通过getLifecycle()方法获取所有Lifecycle接口的Bean,并激活它们的start() 方法。这通常用于具有后台任务的Bean。

  • ContextStoppedEvent : 与 ContextStartedEvent 相反,stop() 方法会触发 ContextStoppedEvent 事件。

  • ContextClosedEvent: 当使用 ConfigurableApplicationContext 中的 close() 方法关闭 ApplicationContext 时,发布此事件。关闭的上下文 context 不会被重新启动或刷新。

  • RequestHandledEvent: Web应用程序中特有的事件。它表示Web请求的完成(只有在使用Spring的 DispatcherServlet 时才适用)。

  • ApplicationFailedEvent: 该事件在启动Spring Boot时遇到异常时触发。

总结

通过这些 Spring 和 Spring Boot 的扩展点,我们可以大致了解一个 bean 的整个生命周期。在业务开发或中间件业务编写过程中,我们可以合理利用 Spring 提供的扩展点,在 Spring 启动的各个阶段执行特定操作,从而实现自定义初始化的目的。


今天就讲到这里,如果有问题需要咨询,大家可以直接留言或扫下方二维码来知识星球找我,我们会尽力为你解答。


AI资源聚合站已经正式上线,该平台不仅仅是一个AI资源聚合站,更是一个为追求知识深度和广度的人们打造的智慧聚集地。通过访问 AI 资源聚合网站 https://ai-ziyuan.techwisdom.cn/,你将进入一个全方位涵盖人工智能和语言模型领域的宝藏库


作者:路条编程(转载请获本公众号授权,并注明作者与出处)

路条编程
路条编程是一个友好的社区,在这里你可以免费学习编程技能,我们旨在激励想学编程的人尝试新的想法和技术,在最短的时间学习到工作中使用到的技术!
 最新文章