Spring 中控制 Bean 生命周期的几种方式及 BeanPostProcessor 执行原理

时间:2022-07-22
本文章向大家介绍Spring 中控制 Bean 生命周期的几种方式及 BeanPostProcessor 执行原理,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一、几种方式总览

  1. @Bean 注解的方式;
  2. 通过实现接口的方式;
  3. 使用 JSR250 提供的两个注解;
  4. BeanPostProcessor 后置处理器;

二、@Bean 的方式

可以使用 @Bean 注解的两个属性设置初始化和销毁方法:

@Bean(initMethod = "init", destroyMethod = "destroy")
public Car car() {
    return new Car();
}

这里在实体类中创建了几个方法。

public class Car {

    public Car() {
        System.out.println("汽车构造器...");
    }

    public void init() {
        System.out.println("汽车初始化...");
    }

    public void destroy() {
        System.out.println("汽车销毁了...");
    }
}

实际的测试结果如下:

汽车构造器...
汽车初始化了...
容器初始化了...

三、InitializingBean 和 DisposableBean 的方式

这两个类都是接口,其中 InitializingBean 有一个抽象方法 afterPropertiesSetDisposableBean 有一个抽象方法 destroy,分别代表 initdestroy 方法。

继承之后可以重写这两个方法:

public void destroy() {
    System.out.println("汽车销毁了...");
}
@Override
public void afterPropertiesSet() throws Exception {
    System.out.println("汽车初始化了...");
}

效果也和上面一样,这样就不用再使用 @Bean 指定初始化和销毁方法了。

注意:

  • 这里的对象均为单例的对象,所以在容器初始化的时候就加载了;
  • 注意如果是多例的对象要加以区分。

四、@PostConstruct 和 @PreDestroy 注解

这两个注解都是 JSR250 提供的注解:

  • @PostConstruct: 在 bean 创建完成并且属性赋完值的时候调用方法;
  • @PreDestroy: 在容器销毁 bean 之前通知我们进行清理操作;

该注解在实体类上的方法使用:

/**
 * 对象创建并赋值之后调用
 */
@PostConstruct
public void init() {
    System.out.println("汽车初始化...");
}
/**
 * 容器移除对象之前调用
 */
@PreDestroy
public void destroy() {
    System.out.println("汽车销毁了...");
}

五、BeanPostProcessor 后置处理器

该类的作用就是在 bean 初始化前后进行一些处理工作,它是一个接口;

该接口有两个方法:

  • postProcessBeforeInitialization:在初始化之前调用;
  • postProcessAfterInitialization: 在初始化之后调用;

所以我们可以写一个实现类来实现这些方法:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之前:" + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之后:" + beanName);
        return bean;
    }
}

执行之后,这两个方法会在初始化方法被调用的前后分别被调用,应用于容器中所有的 bean

六、BeanPostProcessor 的执行原理

下面分析一下 BeanPostProcessor 的执行原理:

这是截取的一段源代码,出自 AbstractAutowireCapableBeanFactory 类,该方法叫做 doCreateBean

// Initialize the bean instance.
Object exposedObject = bean;
try {
	populateBean(beanName, mbd, instanceWrapper);
	exposedObject = initializeBean(beanName, exposedObject, mbd);
}

这段代码很容易理解,就是初始化 bean 实例,涉及两个方法:

  • populateBean:将属性值填充到 Bean 中,就是给 Bean 属性赋值;
  • initializeBean:初始化 Bean

来看一下该类的 initializeBean 方法做了什么:

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
	wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
	invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
	throw new BeanCreationException(
			(mbd != null ? mbd.getResourceDescription() : null),
			beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
	wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

该方法中有三处比较重要:

  • applyBeanPostProcessorsBeforeInitialization:在 Bean 初始化之前执行;
  • invokeInitMethods:执行自定义初始化;
  • applyBeanPostProcessorsAfterInitialization:在 Bean 初始化之后执行;

查看前置方法的源码,发现他就是循环遍历所有的 BeanPostProcessor ,然后依次执行,后置方法同理。

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
		throws BeansException {
	Object result = existingBean;
	for (BeanPostProcessor processor : getBeanPostProcessors()) {
		Object current = processor.postProcessBeforeInitialization(result, beanName);
		if (current == null) {
			return result;
		}
		result = current;
	}
	return result;
}

而且这两个方法和我们写实现类重写的两个方法同名,作用也一致,这里是 Spring 使用了代理模式进行了增强,我们重写的部分就是增强的内容。

注:

Spring 底层很多地方都使用了 BeanPostProcessor 及其子类,如 AutowiredAnnotationBeanPostProcessor,就是 @Autowired 注解。