Spring Bean的生命周期流程如下图:
Spring Bean生命周期代码验证
下面我们来通过代码来验证下:
public class SpringBean implements BeanNameAware,
BeanFactoryAware,
ApplicationContextAware,
InitializingBean, DisposableBean {
@Autowired
private void setOwnBeanFactory(BeanFactory beanFactory) {
System.out.println("填充属性");
}
public SpringBean() {
System.out.println("实例化");
}
/**
* BeanNameAware
* @param name
*/
@Override
public void setBeanName(String name) {
System.out.println("调用BeanNameAware.setBeanName():" + name);
}
/**
* BeanFactoryAware
* @param beanFactory
* @throws BeansException
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("调用BeanFactoryAware.setBeanFactory()");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("调用ApplicationContextAware.setApplicationContext()");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("调用InitializingBean.afterPropertiesSet()");
}
@PostConstruct
private void init() {
System.out.println("调用@PostConstruct自定义初始化方法");
}
public void initMethod() {
System.out.println("调用配置的initMethod自定义初始化方法");
}
@PreDestroy
public void destory(){
System.out.println("销毁:调用@PreDestroy自定义销毁方法");
}
@Override
public void destroy() throws Exception {
System.out.println("销毁:调用DisposableBean.destroy()");
}
public void destoryMethod() {
System.out.println("销毁:调用配置的destoryMethod");
}
}
下面是BeanPostProcessor的实现类
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("springBean")) {
System.out.println("调用BeanPostProcessor.postProcessBeforeInitialization(),beanName:" + beanName);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("springBean")) {
System.out.println("调用BeanPostProcessor.postProcessAfterInitialization(),beanName:" + beanName);
}
return bean;
}
}
配置该类
@Bean(initMethod = "initMethod", destroyMethod = "destoryMethod")
public SpringBean springBean() {
return new SpringBean();
}
启动程序后,控制台打印如下:
关闭程序,控制台打印如下:
那么我们上面实现的Spring的这一系列接口:BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean 有什么作用呢?
钩子接口
以上提到的实现的Spring接口是Spring提供的扩展接口,官方将这些接口称之为钩子,这些钩子会在特定的时间被回调,以此来增强 Spring 功能,众多优秀的框架也是通过扩展这些接口,来实现自身特定的功能,如 SpringBoot、mybatis 等。
Aware系列接口
Aware 从字面意思理解就是“知道”、“感知”的意思,是用来获取 Spring 内部对象的接口。Aware 自身是一个顶级接口,它有一系列子接口,在一个 Bean 中实现这些子接口并重写里面的 set 方法后,Spring 容器启动时,就会回调该 set 方法,而相应的对象会通过方法参数传递进去。
执行时机:是在Bean的属性设置之后、初始化方法(如InitializingBean
的afterPropertiesSet
方法或配置的init-method
)调用之前。这个阶段确保了当Aware
接口的回调方法被调用时,Bean的属性已经被设置,但Bean尚未完全初始化。
我们以其中的 ApplicationContextAware 接口为例。
大部分 Aware 系列接口都有一个规律,它们以对象名称为前缀,获取的就是该对象,所以 ApplicationContextAware 获取的对象是 ApplicationContext 。
public interface ApplicationContextAware extends Aware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
比如,我们想在非Spring Bean的环境下获取Spring容器中的bean,我们可以通过如下方式获取
@Component
public class SpringContextUtil implements ApplicationContextAware {
/**
* 上下文对象实例
*/
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
/**
* 通过class获取Bean.
*
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
}
在 Spring 启动过程中,会回调 setApplicationContext 方法,并传入 ApplicationContext 对象,之后就可对该对象进行操作。其它系列的 Aware 接口也是如此使用。
以下是几种常用的 Aware 接口:
- BeanFactoryAware:获取 BeanFactory 对象,它是基础的容器接口。
- BeanNameAware:获取 Bean 的名称。
- EnvironmentAware:获取 Environment 对象,它表示整个的运行时环境,可以设置和获取配置属性。
- ApplicationEventPublisherAware:获取 ApplicationEventPublisher 对象,它是用来发布事件的。
- ResourceLoaderAware:获取 ResourceLoader 对象,它是获取资源的工具。
InitializingBean
InitializingBean接口如下:
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
它只有一个afterPropertiesSet方法,当一个 Bean 实现该接口时,在 Bean 的初始化阶段(设置完属性之后),会回调 afterPropertiesSet 方法。我它和@PostConstruct,还有通过@Bean设置initMethod的作用是一样的。
BeanPostProcessor
BeanPostProcessor 和 InitializingBean 有点类似,也是可以在 Bean 的生命周期执行自定义操作,一般称之为 Bean 的后置处理器,不同的是,
BeanPostProcessor 可以在 Bean 初始化前、后执行自定义操作,且针对的目标也不同,InitializingBean 针对的是实现 InitializingBean 接口的 Bean,而 BeanPostProcessor 针对的是所有的 Bean
public interface BeanPostProcessor {
// Bean 初始化前调用
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// Bean 初始化后调用
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
所有的 Bean 在初始化前、后都会回调接口中的 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法,入参是当前正在初始化的 Bean 对象和 BeanName。
在上面我们演示Spring生命周期的代码中 MyBeanPostProcessor类中的方法如果没有
if (beanName.equals("springBean")) {}
这个判断,会打印很多Spring内部的Bean。
BeanPostProcessor 使用场景其实非常多,因为它可以获取正在初始化的 Bean 对象,然后可以依据该 Bean 对象做一些定制化的操作,如:判断该 Bean 是否为某个特定对象、获取 Bean 的注解元数据等。
BeanFactoryPostProcessor
BeanFactoryPostProcessor 是 Bean 工厂的后置处理器,它的执行时机:BeanFactory标准初始化之后,所有的Bean定义已经被加载,但标准Bean的实例还没被创建。一般用来修改上下文中的 BeanDefinition,修改 Bean 的属性值。
public interface BeanFactoryPostProcessor {
// 入参是一个 Bean 工厂:ConfigurableListableBeanFactory。该方法执行时,所有 BeanDefinition 都已被加载,但还未实例化 Bean。
// 可以对其进行覆盖或添加属性,甚至可以用于初始化 Bean。
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
它与 BeanPostProcessor的区别是:BeanPostProcessor是bean级别的处理,针对某个具体的bean进行处理,而BeanFactoryPostProcessor是针对整个Bean的工厂进行处理
Spring的BeanPostProcessor和BeanFactoryPostProcessor区别
Spring高级进阶:BeanFactoryPostProcessor
一文吃透Spring Boot扩展之BeanFactoryPostProcessor
ImportSelector
可以参考:Spring Boot之ImportSelector
ImportSelector 是一个较为重要的扩展接口,通过该接口可动态的返回需要被容器管理的类,不过一般用来返回外部的配置类。可在标注 @Configuration 注解的类中,通过 @Import 导入 ImportSelector 来使用。
public interface ImportSelector {
// 方法入参是注解的元数据对象,返回值是类的全路径名数组
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
selectImports 方法返回的是类的全路径名。
自定义 ImportSelector:
public class TestImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
if (importingClassMetadata.hasAnnotation("")) {
// 判断是否包含某个注解
}
// 返回 Test 的全路径名,Test 会被放入到 Spring 容器中
return new String[]{"com.loong.diveinspringboot.test.Test"};
}
}
selectImports 方法中可以针对通过 AnnotationMetadata 对象进行逻辑判断,AnnotationMetadata 存储的是注解元数据信息,根据这些信息可以动态的返回需要被容器管理的类名称。
定义的 Test 类:
public class Test {
public void hello() {
System.out.println("Test -- hello");
}
}
这里,我们没有对 Test 标注 @Component 注解,所以,Test 不会自动加入到 Spring 容器中。
@SpringBootApplication
@Import(TestImportSelector.class)
public class Main {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication();
ConfigurableApplicationContext run = springApplication.run(Main.class);
Test bean = run.getBean(Test.class);
bean.hello();
}
}
之后通过 @Import 导入自定义的 TestImportSelector ,前面也说过,@Import 一般配合 @Configuration 使用,而 @SpringBootApplication 中包含了 @Configuration 注解。之后,通过 getBean 方法从容器中获取 Test 对象,并调用 hello 方法。最终,结果正确输出。
6、ImportBeanDefinitionRegistrar
Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean
该接口和 ImportSelector 类似,也是配合 @Import 使用,不过 ImportBeanDefinitionRegistrar 更为直接一点,它可以直接把 Bean 注册到容器中。
public interface ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
入参除了注解元数据对象 AnnotationMetadata 外,还多了一个 BeanDefinitionRegistry 对象,该对象定义了关于 BeanDefinition 的一系列的操作,如:注册、移除、查询等。
自定义 ImportBeanDefinitionRegistrar:
public class TestRegistrar implements ImportBeanDefinitionRegistrar {
// 一般通过 AnnotationMetadata 进行业务判断,然后通过 BeanDefinitionRegistry 直接注册 Bean
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(Test.class);
beanDefinition.setLazyInit(true);
registry.registerBeanDefinition(Test.class.getName(), beanDefinition);
}
}
这里,主要通过 BeanDefinitionRegistry 手动注册 Test 类的 BeanDefinition,并设置懒加载属性。
ImportSelector 和 ImportBeanDefinitionRegistrar 是实现 @Enable 模式注解的核心接口,而 @Enable 模式注解在 Spring、SpringBoot、SpringCloud 中被大量使用,其依靠这些注解来实现各种功能及特性,是较为重要的扩展接口。
值得注意的是,SpringBoot 外部化配置、自动装配特性就是通过 @Enable 注解配合 ImportSelector 和 ImportBeanDefinitionRegistrar 接口来实现的。
FactoryBean
FactoryBean 也是一种 Bean,不同于普通的 Bean,它是用来创建 Bean 实例的,属于工厂 Bean,不过它和普通的创建不同,它提供了更为灵活的方式,其实现有点类似于设计模式中的工厂模式和修饰器模式。
Spring 框架内置了许多 FactoryBean 的实现,它们在很多应用如(Spring的AOP、ORM、事务管理)及与其它第三框架(ehCache)集成时都有体现。
public interface FactoryBean<T> {
// 该方法会返回该 FactoryBean “生产”的对象实例,我们需要实现该方法以给出自己的对象实例化逻辑
T getObject() throws Exception;
// Bean的类型
Class<?> getObjectType();
// 是否是单例
default boolean isSingleton() {
return true;
}
}
自定义 FactoryBean:
@Component
public class TestFactoryBean implements FactoryBean<Test> {
@Override
public Test getObject() throws Exception {
// 这里可以灵活的创建 Bean,如:代理、修饰
return new Test();
}
@Override
public Class<?> getObjectType() {
return null;
}
}
Test 类:
public class Test {
public void hello() {
System.out.println("Test -- hello");
}
}
启动类:
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication();
ConfigurableApplicationContext run = springApplication.run(Main.class);
Test bean = (Test) run.getBean("testFactoryBean");
bean.hello();
}
}
输出:
2020-02-27 23:16:00.334 INFO 32234 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-02-27 23:16:00.338 INFO 32234 --- [ main] com.loong.diveinspringboot.test.Main : Started Main in 3.782 seconds (JVM running for 4.187)
Test -- hello
可以看到,启动类中 getBean 的参数是 testFactoryBean ,从这可以看出,当容器中的 Bean 实现了 FactoryBean 后,通过 getBean(String BeanName) 获取到的 Bean 对象并不是 FactoryBean 的实现类对象,而是这个实现类中的 getObject() 方法返回的对象。如果想获取 FactoryBean 的实现类,需通过这种方式:getBean(&BeanName),在 BeanName 之前加上&。
ApplicationListener
ApplicationListener 是 Spring 实现事件机制的核心接口,属于观察者设计模式,一般配合 ApplicationEvent 使用。在 Spring 容器启动过程中,会在相应的阶段通过 ApplicationContext 发布 ApplicationEvent 事件,之后所有的 ApplicationListener 会被回调,根据事件类型,执行不同的操作。
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
在 onApplicationEvent 方法中,通过 instanceof 判断 event 的事件类型。
自定义 ApplicationListener:
@Component
public class TestApplicationListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof TestApplicationEvent) {
TestApplicationEvent testApplicationEvent = (TestApplicationEvent) event;
System.out.println(testApplicationEvent.getName());
}
}
}
当自定义的 TestApplicationListener 被回调时,判断当前发布的事件类型是否是自定义的 TestApplicationEvent,如果是则输出事件名称。
自定义 TestApplicationEvent:
public class TestApplicationEvent extends ApplicationEvent {
private String name;
public TestApplicationEvent(Object source, String name) {
super(source);
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
启动类:
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication();
ConfigurableApplicationContext run = springApplication.run(Main.class);
run.publishEvent(new TestApplicationEvent(new Main(),"Test 事件"));
}
}
通过 ApplicationContext 发布 TestApplicationEvent 事件。当然也可以在业务代码中通过 ApplicationContextAware 获取 ApplicationContext 发布事件。
结果:
2020-02-27 08:37:10.972 INFO 30984 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2020-02-27 08:37:11.026 INFO 30984 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-02-27 08:37:11.029 INFO 30984 --- [ main] com.loong.diveinspringboot.test.Main : Started Main in 3.922 seconds (JVM running for 4.367)
Test 事件
ApplicationListener 也被 SpringBoot 进行扩展,来实现自身特定的事件机制。