谈谈Spring中的BeanPostProcessor接口

1、前言

  这几天正在复习Spring的相关内容,在了解bean的生命周期的时候,发现其中涉及到一个特殊的接口——BeanPostProcessor接口。因为网上没有找到比较好的博客,全部最后花了好几个小时,经过Spring的官方文档对它作了一个大体的了解,下面就来简单介绍一下这个接口。html


2、正文

2.1 BeanPostProcessor的功能

  有时候,咱们但愿Spring容器在建立bean的过程当中,可以使用咱们本身定义的逻辑,对建立的bean作一些处理,或者执行一些业务。而实现方式有多种,好比自定义bean的初始化话方法等,而BeanPostProcessor接口也是用来实现相似的功能的。java

  若是咱们但愿容器中建立的每个单例bean,在建立的过程当中能够执行一些自定义的逻辑,那么咱们就能够编写一个类,并让他实现BeanPostProcessor接口,而后将这个类注册到一个容器中。容器在建立bean的过程当中,会优先建立实现了BeanPostProcessor接口的bean,而后,在建立其余bean的时候,会将建立的每个bean做为参数,调用BeanPostProcessor的方法。而BeanPostProcessor接口的方法,便是由咱们本身实现的。下面就来具体介绍一下BeanPostProcessor的使用spring


2.2 BeanPostProcessor的使用

  咱们先看一看BeanPostProcessor接口的代码:app

public interface BeanPostProcessor {
	// 注意这个方法名称关键的是before这个单词
	Object postProcessBeforeInitialization(Object bean, String beanName) 
        throws BeansException;

    // 注意这个方法名称关键的是after这个单词
	Object postProcessAfterInitialization(Object bean, String beanName) 
        throws BeansException;
}

  能够看到,BeanPostProcessor接口只有两个抽象方法,由实现这个接口的类去实现(后面简称这两个方法为beforeafter),这两个方法有着相同的参数:less

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

  那这两个方法什么时候执行呢?这就涉及到Spring中,bean的生命周期了。下面引用《Spring实战》中的一张图,这张图表现了bean的生命周期,而Spring容器建立bean的具体过程,请参考这篇博客——简单谈谈Spring的IoCide

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

  值得注意的是,咱们能够在一个容器中注册多个不一样的BeanPostProcessor的实现类对象,而bean在建立的过程当中,将会轮流执行这些对象实现的beforeafter方法。那执行顺序如何肯定呢?Spring提供了一个接口Ordered,咱们可让BeanPostProcessor的实现类实现这个Ordered接口,并实现接口的getOrder方法。这个方法的返回值是一个int类型,Spring容器会经过这个方法的返回值,对容器中的多个BeanPostProcessor对象进行从小到大排序,而后在建立bean时依次执行它们的方法。也就是说,getOrder方法返回值越小的BeanPostProcessor对象,它的方法将越先被执行。测试


2.3 一个简单的demo

  下面就来写一个简单的demo,来看看BeanPostProcessor的效果。首先定义两个普通的bean,就叫UserCar吧:this

public class User {

    private String name;
    private int age;
	
    // ... 省略getter和setter...
}

public class Car {
    private int speed;
    private double price;

    // ... 省略getter和setter...
}

  在定义一个BeanPostProcessor的实现类,重写接口的方法:lua

public class PostBean implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) 
        throws BeansException {
        // 输出信息,方便咱们看效果
        System.out.println("before -- " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) 
        throws BeansException {
        // 输出信息,方便咱们看效果
        System.out.println("after -- " + beanName);
        return bean;
    }

}

  咱们直接使用一个Java类做为Spring的配置,就不使用xml配置文件了。配置以下,在这个配置类中,声明了UserCar以及PostBean这三个bean的工厂方法,前两个是普通bean,而PostBean是实现BeanPostProcessorbean

@Configuration
public class BeanConfig {
	// 在Spring中注册User这个bean
    @Bean
    public User user() {
        return new User();
    }
    
    // 在Spring中注册Car这个bean
    @Bean
    public Car car() {
        return new Car();
    }

    // 在Spring中注册PostBean这个bean,这个bean实现了BeanPostProcessor接口
    @Bean
    public PostBean postBean() {
        return new PostBean();
    }

}

  好,有了上面四个类,就能够开始测试了,下面是测试方法:

@Test
public void testConfig() {
    ApplicationContext context =
        new AnnotationConfigApplicationContext(BeanConfig.class);
}

  上面这个方法啥也不干,就是建立一个Spring的上下文对象,也就是SpringIoC容器。这个容器将去加载BeanConfig这个类的配置,而后建立配置类中声明的对象。在建立User和Car的过程当中,就会执行BeanPostProcessor实现类的方法。咱们看看执行结果:

before -- org.springframework.context.event.internalEventListenerProcessor
after -- org.springframework.context.event.internalEventListenerProcessor
before -- org.springframework.context.event.internalEventListenerFactory
after -- org.springframework.context.event.internalEventListenerFactory
before -- car
after -- car
before -- user
after -- user

  能够看到,BeanPostProcessorbefore方法和after方法都被调用了四次,最后两次调用时,传入的参数正是咱们本身定义的Bean——UserCar。那为何调用了四次呢,明明咱们只定义了两个普通bean。咱们看上面的输出发现,前两次调用,传入的beanSpring内部的组件。Spring在初始化容器的过程当中,会建立一些本身定义的bean用来实现一些功能,而这些bean,也会执行咱们注册进容器中的BeanPostProcessor实现类的方法。


2.4 使用BeanPostProcessor时容易踩的坑

  BeanPostProcessor这个接口,在使用的过程当中,其实还有许多的限制和坑点,若不了解的话,可能会让你对某些结果感到莫名其妙。下面我就来简单地说一说:

(一)BeanPostProcessor依赖的bean,不会执行BeanPostProcessor的方法

  当咱们在BeanPostProcessor的实现类中,依赖了其余的bean,那么被依赖的bean被建立时,将不会执行它所在的BeanPostProcessor实现类实现的方法,好比咱们修改PostBean的实现,以下所示:

@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还未初始化完成,因此不会调用它的方法。


(二)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这一点。为了验证上面的说法,我稍微修改一下2.3中的例子,来测试一波。

  首先,咱们修改2.3中用到的PostBeanUser这两个类,让PostBean依赖User这个类,同时为了输出更加地简单,咱们将beforeafter方法中的println语句删了:

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

    // 此方法用来测试AOP,做为切点
    public void testAOP() {
        System.out.println("Post Bean");
    }
}

@Component
public class User {

    private String name;
    private int age;
	
    // ... 省略getter和setter...
    
    // 此方法用来测试AOP,用做切点
    public void testAOP() {
        System.out.println("user bean");
    }
}

  而后,咱们定义一个AOP的切面,在切面中将PostBeantestAOP方法做为切点,代码以下:

@Aspect
public class BeanPostProcessorAspect {
    
	// 此方法织入PostBean的testAOP方法
    @Before("execution(* cn.tewuyiang.pojo.PostBean.testAOP(..))")
    public void before() {
        System.out.println("before1");
    }

    // 此方法织入User的testAOP方法
    @Before("execution(* cn.tewuyiang.pojo.User.testAOP(..))")
    public void before2() {
        System.out.println("before2");
    }
}

  好,这就准备完毕,能够开始测试了。咱们此次使用Spring注解扫描来配置bean以及为bean注入依赖,测试代码以下:

@Test
public void testConfig() {
    ApplicationContext context =
        new AnnotationConfigApplicationContext(AutoConfig.class);
    // 获取User这个bean,执行测试AOP的方法
    User user = context.getBean(User.class);
    user.testAOP();
    // 获取PostBean这个bean,执行测试AOP的方法
    PostBean bean = context.getBean(PostBean.class);
    bean.testAOP();
}

输出以下:
	user bean
	post Bean

  从输出中能够看到,使用AOP织入的前置通知没有执行,这也就验证了上面所说的,BeanPostProcessor的实现类以及实现类依赖的bean,没法使用AOP为其织入通知。可是这个限制具体有到什么程度,我也不是很肯定,由于我使用xml配置依赖,以及上面使用注解扫描两种方式,AOP织入都无法使用,可是我在使用@Bean这种配置方式时,被依赖的bean却成功执行了通知。因此,关于此处提到的限制,还须要深刻了解Spring容器的源码实现才能下定论。


(三)注册BeanPostProcessor的方式以及限制

  咱们如何将BeanPostProcessor注册到Spring容器中?方式主要有两种,第一种就是上面一直在用的,将其声明在Spring的配置类或xml文件中,做为普通的bean,让ApplicationContext对象去加载它,这样它就被自动注册到容器中了。并且Spring容器会对BeanPostProcessor的实现类作特殊处理,即会将它们挑选出来,在加载其余bean前,优先加载BeanPostProcessor的实现类。

  还有另一种方式就是使用ConfigurableBeanFactory接口的addBeanPostProcessor方法手动添加,ApplicationContext对象中组合了一个ConfigurableBeanFactory的实现类对象。可是这种方式添加BeanPostProcessor有一些缺点。首先,咱们一建立Spring容器,在配置文件中配置的单例bean就会被加载,此时addBeanPostProcessor方法尚未执行,那咱们手动添加的BeanPostProcessor也就没法做用于这些bean了,因此手动添加的BeanPostProcessor只能做用于那些延迟加载的bean,或者非单例bean

  还有一个就是,使用addBeanPostProcessor方式添加的BeanPostProcessor,Ordered接口的做用将失效,而是以注册的顺序执行。咱们前面提过,Ordered接口用来指定多个BeanPostProcessor实现的方法的执行顺序。这是Spring官方文档中提到的:

While the recommended approach for BeanPostProcessor registration is through ApplicationContext auto-detection (as described above), it is also possible to register them programmatically against a ConfigurableBeanFactory using the addBeanPostProcessor method. This can be useful when needing to evaluate conditional logic before registration, or even for copying bean post processors across contexts in a hierarchy. Note however that BeanPostProcessor s added programmatically do not respect the Ordered interface. Here it is the order of registration that dictates the order of execution. Note also that BeanPostProcessor s registered programmatically are always processed before those registered through auto-detection, regardless of any explicit ordering.


(四)使用@Bean配置BeanPostProcessor的限制

  若是咱们使用Java类的方式配置Spring,并使用@Bean声明一个工厂方法返回bean实例,那么返回值的类型必须是BeanPostProcessor类型,或者等级低于BeanPostProcessor的类型。这里很差口头描述,直接看代码吧。如下是一个BeanPostProcessor的实现类,它实现了多个接口:

/**
 * 此BeanPostProcessor的实现类,还实现了Ordered接口
 */
public class PostBean implements BeanPostProcessor, Ordered {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) 
        throws BeansException {
        System.out.println("before -- " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) 
        throws BeansException {
        System.out.println("after -- " + beanName);
        return bean;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

  咱们在配置类中,声明PostBean能够有如下几种方式:

@Configuration
public class BeanConfig {

	// 方式1:PostBean
    @Bean
    public PostBean postBean() {
        return new PostBean();
    }
    
    // 方式2:返回值为BeanPostProcessor
    @Bean
    public BeanPostProcessor postBean() {
        return new PostBean();
    }
    
    // 方式3:返回值为Ordered
    @Bean
    public Ordered postBean() {
        return new PostBean();
    }
}

  以上三种方式均可以让Spring容器建立PostBean实例对象,由于PostBean实现了BeanPostProcessorOrdered接口,因此它也是这两种类型的对象。可是须要注意,上面三种方式中,只有第一种和第二种方式,会让Spring容器将PostBean看成BeanPostProcessor处理;而第三种方式,则会被看成一个普通Bean处理,实现BeanPostProcessor的两个方法都不会被调用。由于在PostBean的继承体系中,OrderedBeanPostProcessor是同级别的,Spring没法识别出这个Ordered对象,也是一个BeanPostProcessor对象;可是使用PostBean却能够,由于PostBean类型就是BeanPostProcessor的子类型。因此,在使用@Bean声明工厂方法返回BeanPostProcessor实现类对象时,返回值必须是BeanPostProcessor类型,或者更低级的类型Spring官方文档中,这一部分的内容以下:

Note that when declaring a BeanPostProcessor using an @Bean factory method on a configuration class, the return type of the factory method should be the implementation class itself or at least the org.springframework.beans.factory.config.BeanPostProcessor interface, clearly indicating the post-processor nature of that bean. Otherwise, the ApplicationContext won’t be able to autodetect it by type before fully creating it. Since a BeanPostProcessor needs to be instantiated early in order to apply to the initialization of other beans in the context, this early type detection is critical.


3、总结

  以上就对BeanPostProcessor的功能、使用以及须要注意的问题作了一个大体的介绍。须要注意的是,上面所提到的问题,可能根据不一样的状况,会有不一样的结果,由于文档中的资料只是简单地提了几句,并不详细,上面的内容大部分都是我基于官方文档的描述,以及本身的测试得出,因此可能并不许确。还须要本身在实践中去尝试,或者阅读源码,才能完全了解BeanPostProcessor的执行机制。

  以上描述若存在错误或不足,但愿可以提出来,由于这一部份内容,我也不太了解,因此但愿有人帮忙指正


4、参考

相关文章
相关标签/搜索