这几天正在复习Spring
的相关内容,在了解bean
的生命周期的时候,发现其中涉及到一个特殊的接口——BeanPostProcessor
接口。因为网上没有找到比较好的博客,全部最后花了好几个小时,经过Spring
的官方文档对它作了一个大体的了解,下面就来简单介绍一下这个接口。html
有时候,咱们但愿Spring
容器在建立bean
的过程当中,可以使用咱们本身定义的逻辑,对建立的bean
作一些处理,或者执行一些业务。而实现方式有多种,好比自定义bean
的初始化话方法等,而BeanPostProcessor
接口也是用来实现相似的功能的。java
若是咱们但愿容器中建立的每个单例bean
,在建立的过程当中能够执行一些自定义的逻辑,那么咱们就能够编写一个类,并让他实现BeanPostProcessor
接口,而后将这个类注册到一个容器中。容器在建立bean
的过程当中,会优先建立实现了BeanPostProcessor
接口的bean
,而后,在建立其余bean
的时候,会将建立的每个bean
做为参数,调用BeanPostProcessor
的方法。而BeanPostProcessor
接口的方法,便是由咱们本身实现的。下面就来具体介绍一下BeanPostProcessor
的使用spring
咱们先看一看BeanPostProcessor
接口的代码:app
public interface BeanPostProcessor { // 注意这个方法名称关键的是before这个单词 Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; // 注意这个方法名称关键的是after这个单词 Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }
能够看到,BeanPostProcessor
接口只有两个抽象方法,由实现这个接口的类去实现(后面简称这两个方法为before
和after
),这两个方法有着相同的参数:less
bean
的引用;bean
的名称; 那这两个方法什么时候执行呢?这就涉及到Spring
中,bean
的生命周期了。下面引用《Spring实战》
中的一张图,这张图表现了bean
的生命周期,而Spring
容器建立bean
的具体过程,请参考这篇博客——简单谈谈Spring的IoC。ide
上图中标红的两个地方就是BeanPostProcessor
中两个方法的执行时机。Spring
容器在建立bean
时,若是容器中包含了BeanPostProcessor
的实现类对象,那么就会执行这个类的这两个方法,并将当前正在建立的bean
的引用以及名称做为参数传递进方法中。这也就是说,BeanPostProcessor
的做用域是当前容器中的全部bean
(不包括一些特殊的bean
,这个后面说)。post
值得注意的是,咱们能够在一个容器中注册多个不一样的BeanPostProcessor
的实现类对象,而bean
在建立的过程当中,将会轮流执行这些对象实现的before
和after
方法。那执行顺序如何肯定呢?Spring
提供了一个接口Ordered
,咱们可让BeanPostProcessor
的实现类实现这个Ordered
接口,并实现接口的getOrder
方法。这个方法的返回值是一个int
类型,Spring
容器会经过这个方法的返回值,对容器中的多个BeanPostProcessor
对象进行从小到大排序,而后在建立bean
时依次执行它们的方法。也就是说,getOrder
方法返回值越小的BeanPostProcessor
对象,它的方法将越先被执行。测试
下面就来写一个简单的demo
,来看看BeanPostProcessor
的效果。首先定义两个普通的bean
,就叫User
和Car
吧: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
配置文件了。配置以下,在这个配置类中,声明了User
、Car
以及PostBean
这三个bean
的工厂方法,前两个是普通bean
,而PostBean
是实现BeanPostProcessor
的bean
:
@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
的上下文对象,也就是Spring
的IoC
容器。这个容器将去加载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
能够看到,BeanPostProcessor
的before
方法和after
方法都被调用了四次,最后两次调用时,传入的参数正是咱们本身定义的Bean
——User
和Car
。那为何调用了四次呢,明明咱们只定义了两个普通bean
。咱们看上面的输出发现,前两次调用,传入的bean
是Spring
内部的组件。Spring
在初始化容器的过程当中,会建立一些本身定义的bean
用来实现一些功能,而这些bean
,也会执行咱们注册进容器中的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, neitherBeanPostProcessor
s nor the beans they reference directly are eligible for auto-proxying, and thus do not have aspects woven into them.
上面这段话的意思大体是说,Spring
的AOP
代理就是做为BeanPostProcessor
实现的,因此咱们没法对BeanPostProcessor的实现类使用AOP织入通知,也没法对BeanPostProcessor的实现类依赖的bean使用AOP织入通知。Spring
的AOP
实现我暂时尚未研究过,因此上面的说AOP
做为BeanPostProcessor
实现的意思我不是特别明白,可是咱们如今只须要关注BeanPostProcessor
以及它依赖的bean
都没法使用AOP
这一点。为了验证上面的说法,我稍微修改一下2.3
中的例子,来测试一波。
首先,咱们修改2.3
中用到的PostBean
和User
这两个类,让PostBean
依赖User
这个类,同时为了输出更加地简单,咱们将before
和after
方法中的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
的切面,在切面中将PostBean
的testAOP
方法做为切点,代码以下:
@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 throughApplicationContext
auto-detection (as described above), it is also possible to register them programmatically against aConfigurableBeanFactory
using theaddBeanPostProcessor
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 thatBeanPostProcessor
s added programmatically do not respect theOrdered
interface. Here it is the order of registration that dictates the order of execution. Note also thatBeanPostProcessor
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
实现了BeanPostProcessor
和Ordered
接口,因此它也是这两种类型的对象。可是须要注意,上面三种方式中,只有第一种和第二种方式,会让Spring
容器将PostBean
看成BeanPostProcessor
处理;而第三种方式,则会被看成一个普通Bean
处理,实现BeanPostProcessor
的两个方法都不会被调用。由于在PostBean
的继承体系中,Ordered
和BeanPostProcessor
是同级别的,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 theorg.springframework.beans.factory.config.BeanPostProcessor
interface, clearly indicating the post-processor nature of that bean. Otherwise, theApplicationContext
won’t be able to autodetect it by type before fully creating it. Since aBeanPostProcessor
needs to be instantiated early in order to apply to the initialization of other beans in the context, this early type detection is critical.
以上就对BeanPostProcessor
的功能、使用以及须要注意的问题作了一个大体的介绍。须要注意的是,上面所提到的问题,可能根据不一样的状况,会有不一样的结果,由于文档中的资料只是简单地提了几句,并不详细,上面的内容大部分都是我基于官方文档的描述,以及本身的测试得出,因此可能并不许确。还须要本身在实践中去尝试,或者阅读源码,才能完全了解BeanPostProcessor
的执行机制。
以上描述若存在错误或不足,但愿可以提出来,由于这一部份内容,我也不太了解,因此但愿有人帮忙指正。