( 六 ) Spring Bean 后置处理器-BeanPostProcessor

( 六 ) Spring Bean 后置处理器-BeanPostProcessor

 

 

1、简介

BeanPostProcessor 接口也被称为后置处理器,通过该接口可以自定义调用Bean初始化前后执行的操作方法。

如果我们希望容器中创建的每一个bean,在创建的过程中可以执行一些自定义的逻辑,那么我们就可以编写一个类,并让他实现 BeanPostProcessor 接口,然后将这个类注册到一个容器中。容器在创建bean的过程中,会优先创建实现了 BeanPostProcessor 接口的 bean,然后,在创建其他bean的时候,会将创建的每一个bean作为参数,调用BeanPostProcessor的方法。而BeanPostProcessor接口的方法,即是由我们自己实现的。下面就来具体介绍一下 BeanPostProcessor 的使用。

 

2、BeanPostProcessor 接口源码如下:

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

postProcessBeforeInitialization:  在 Bean 实例化、依赖注入后,初始化前调用。(初始化前是指: init-method、InitializingBean、@PostConstruct)

postProcessAfterInitialization    在 Bean 实例化、依赖注入、初始化都完成后调用。

这两个方法有着相同的参数:

  • bean:容器正在创建的那个bean的引用;
  • beanName:容器正在创建的那个bean的名称;

 

3、那这两个方法何时执行呢?

这就涉及到Spring中,bean的生命周期了,下图表现了bean的生命周期:

    上图中标红的两个地方就是BeanPostProcessor中两个方法的执行时机。Spring容器在创建bean时,如果容器中包含了BeanPostProcessor的实现类对象,那么就会执行这个类的这两个方法,并将当前正在创建的bean的引用以及名称作为参数传递进方法中。这也就是说,BeanPostProcessor 的作用域是当前容器中的所有bean(不包括一些特殊的bean,这个后面说)。

当需要添加多个后置处理器实现类时,默认情况下 Spring 容器会根据后置处理器的定义顺序来依次调用。也可以通过实现 Ordered 接口的 getOrder 方法指定后置处理器的执行顺序。该方法返回值为整数,默认值为 0,值越大优先级越低

 

4、使用示例

HelloWorld 类代码如下:

public class HelloWorld {
    private String message;
    public void setMessage(String message) {
        this.message = message;
    }
    public void getMessage() {
        System.out.println("Message : " + message);
    }
    public void init() {
        System.out.println("Bean正在初始化");
    }
    public void destroy() {
        System.out.println("Bean将要被销毁");
    }
}

InitHelloWorld 类代码如下:

public class InitHelloWorld implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("A Before : " + beanName);
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("A After : " + beanName);
        return bean;
    }
    @Override
    public int getOrder() {
        return 5;
    }
}

   需要注意的是:  postProcessBeforeInitialization 和 postProcessAfterInitialization 方法返回值不能为 null,否则会报空指针异常或者通过 getBean() 方法获取不到 Bean 实例对象,因为后置处理器从Spring IoC 容器中取出 Bean 实例对象后没有再次放回到 IoC 容器中。

 

InitHelloWorld2 的代码如下:

public class InitHelloWorld2 implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("B Before : " + beanName);
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("B After : " + beanName);
        return bean;
    }
    @Override
    public int getOrder() {
        return 0;
    }
}

 

Beans.xml 代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="helloWorld" class="com.dw.study.HelloWorld"
        init-method="init" destroy-method="destroy">
        <property name="message" value="Hello World!" />
    </bean>
    <!-- 注册处理器 -->
    <bean class="com.dw.study.InitHelloWorld" />
    <bean class="com.dw.study.InitHelloWorld2" />
</beans>

 

MainApp 类代码如下:

public class MainApp {
    public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
        obj.getMessage();
        context.registerShutdownHook();
    }
}

运行结果如下:

B Before : helloWorld
A Before : helloWorld
Bean正在初始化
B After : helloWorld
A After : helloWorld
Message : Hello World!
Bean将要被销毁

    由运行结果可以看出:  postProcessBeforeInitialization 方法是在 Bean 实例化和依赖注入后,自定义初始化方法前执行的。而 postProcessAfterInitialization 方法是在自定义初始化方法后执行的。由于 getOrder 方法返回值越大,优先级越低,所以 InitHelloWorld2 先执行。

 

5、使用注意事项

5.1、BeanPostProcessor依赖的bean,不会执行BeanPostProcessor的方法

当我们在BeanPostProcessor的实现类中,依赖了其他的bean,那么被依赖的bean被创建时,将不会执行它所在的BeanPostProcessor实现类实现的方法,如下所示:

@Component
public class PostBean implements BeanPostProcessor, Ordered {
    // 让PostBean依赖User
    @Autowired
    private User user;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) 
        throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) 
        throws BeansException {
        return bean;
    }
}

此时,容器在创建User这个bean时,不会执行PostBean实现的两个方法,因为由于PostBean依赖于user,所以user需要在PostBean之前创建完成,这也就意味着在user创建时,PostBean还未初始化完成,所以不会调用它的方法。

 

5.2、BeanPostProcessor以及依赖的bean无法使用AOP

以下是Spring官方文档中的一段话:

Because AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessor s nor the beans they reference directly are eligible for auto-proxying, and thus do not have aspects woven into them.

上面这段话的意思大致是说,SpringAOP代理就是作为BeanPostProcessor实现的,所以我们无法对BeanPostProcessor的实现类使用AOP织入通知,也无法对BeanPostProcessor的实现类依赖的bean使用AOP织入通知SpringAOP实现我暂时还没有研究过,所以上面的说AOP作为BeanPostProcessor实现的意思我不是特别明白,但是我们现在只需要关注BeanPostProcessor以及它依赖的bean都无法使用AOP这一点。

 

posted @ 2021-07-28 10:17  邓维-java  阅读(715)  评论(0)    收藏  举报