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

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

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

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

在本文中,我总结了几乎所有Spring和SpringBoot的扩展接口及其应用场景。同时,我整理了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.Applicatinotallow=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 还实现了 BeanFactory、MessageSource、ApplicationEventPublisher 等接口,可以用于相关任务。

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 启动的各个阶段执行特定操作,从而实现自定义初始化的目的。

责任编辑:武晓燕 来源: 路条编程
相关推荐

2024-01-22 09:01:00

SpringBoot接口代码

2023-09-25 13:06:36

SpringBoot扩展接口

2022-06-06 08:42:04

spring-boo开发接口防盗刷

2020-03-16 17:20:02

异常处理Spring Boot

2022-06-21 14:44:38

接口数据脱敏

2024-11-08 15:56:36

2024-11-07 10:55:26

2024-07-25 14:36:10

2023-08-01 08:54:02

接口幂等网络

2020-06-22 07:55:28

接口爬虫

2020-11-13 07:08:51

Spring Boot应用Spring

2020-09-15 11:40:37

Spring Boot代码Java

2022-12-12 08:14:47

2020-09-27 14:13:50

Spring BootJava框架

2013-08-01 15:12:43

2020-11-03 16:00:33

API接口微服务框架编程语言

2024-11-06 11:33:09

2024-02-19 08:26:59

wxPython界面库开发

2015-03-30 12:20:07

DemoStoryboard

2021-03-09 13:18:53

加密解密参数
点赞
收藏

51CTO技术栈公众号