Spring注解驱动之BeanPostProcessor后置处理器讲解
作者:融极
概述
在学习Spring的时候,在了解基本用法的时候,如果有时间一定要深入源码了解Spring的底层原理,这样在做一些适配工作、写一些轮子的时候就会比较容易,否则会很难,甚至一头雾水,无法完成工作。
吃透Spring的原理和源码,往往可以拉开人们之间的差距,当前只要是使用Java技术栈开发的Web项目,几乎都会使用Spring框架。
而且目前各招聘网站上对于Java开发的要求几乎清一色的都是熟悉或者精通Spring,所以,你很有必要学习Spring的细节知识点。
BeanPostProcessor后置处理器概述
首先,看下BeanPostProcessor的源码。
package org.springframework.beans.factory.config; import org.springframework.beans.BeansException; public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }
从源码可以看出,BeanPostProcessor是一个接口,其中有两个方法:
postProcessBeforeInitialization和postProcessAfterInitialization这两个方法分别是在Spring容器中的bean初始化前后执行,所以Spring容器中的每个bean对象初始化前后,都会执行BeanPostProcessor接口的两个方法。
也就是说,postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。当容器中存在多个BeanPostProcessor的实现类时,会按照它们在容器中注册的顺序执行。对于自定义的BeanPostProcessor实现类,还可以让其实现Ordered接口自定义排序。
因此我们可以在每个bean对象初始化前后,加上自己的逻辑。实现方式是自定义一个BeanPostProcessor接口的实现类,例如MyBeanPostProcessor,然后在该类的postProcessBeforeInitialization和postProcessAfterInitialization这两个方法写上自定义逻辑。
BeanPostProcessor后置处理器实例
我们创建一个MyBeanPostProcessor类,实现BeanPostProcessor接口,如下所示。
package com.meimeixia.bean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了,否则无法工作 public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean); return bean; } }
接下来,我们应该是要编写测试用例来进行测试了。
package com.meimeixia.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import com.meimeixia.bean.Car; @ComponentScan("com.meimeixia.bean") @Configuration public class MainConfigOfLifeCycle { @Bean(initMethod="init", destroyMethod="destroy") public Car car() { return new Car(); } }
第二处改动是将Cat类上添加的@Scope(“prototype”)注解给注释掉,因为咱们之前做测试的时候,也是将Cat对象设置成多实例bean了。
package com.meimeixia.bean; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; // @Scope("prototype") @Component public class Cat implements InitializingBean, DisposableBean { public Cat() { System.out.println("cat constructor..."); } /** * 会在容器关闭的时候进行调用 */ @Override public void destroy() throws Exception { // TODO Auto-generated method stub System.out.println("cat destroy..."); } /** * 会在bean创建完成,并且属性都赋好值以后进行调用 */ @Override public void afterPropertiesSet() throws Exception { // TODO Auto-generated method stub System.out.println("cat afterPropertiesSet..."); } }
好了,现在咱们就可以编写测试用例来进行测试了。
可喜的是,我们也不用再编写一个测试用例了,直接运行IOCTest_LifeCycle类中的test01()方法就行,该方法的代码如下所示。
@Test public void test01() { // 1. 创建IOC容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class); System.out.println("容器创建完成"); // 关闭容器 applicationContext.close(); }
此时,运行IOCTest_LifeCycle类中的test01()方法,输出的结果信息如下所示。
可以看到,postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。
当然了,也可以让我们自己写的MyBeanPostProcessor类来实现Ordered接口自定义排序,如下所示。
package com.meimeixia.bean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; /** * 后置处理器,在初始化前后进行处理工作 * @author liayun * */ @Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了 public class MyBeanPostProcessor implements BeanPostProcessor, Ordered { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean); return bean; } @Override public int getOrder() { return 3; } }
BeanPostProcessor后置处理器作用
后置处理器可用于bean对象初始化前后进行逻辑增强。
Spring提供了BeanPostProcessor接口的很多实现类,例如AutowiredAnnotationBeanPostProcessor用于@Autowired注解的实现,AnnotationAwareAspectJAutoProxyCreator用于Spring AOP的动态代理等等。
除此之外,我们还可以自定义BeanPostProcessor接口的实现类,在其中写入咱们需要的逻辑。
bean的初始化和销毁流程
我们知道BeanPostProcessor的postProcessBeforeInitialization()方法是在bean的初始化之前被调用;而postProcessAfterInitialization()方法是在bean初始化的之后被调用。
并且bean的初始化和销毁方法我们可以通过如下方式进行指定。
1. 通过@Bean指定init-method和destroy-method
@Bean(initMethod="init", destroyMethod="destroy")
2. 通过让bean实现InitializingBean和DisposableBean这俩接口
@Componentpublic class Cat implements InitializingBean, DisposableBean {}
3. 使用JSR-250规范里面定义的@PostConstruct和@PreDestroy这俩注解
@PostConstruct
:在bean创建完成并且属性赋值完成之后,来执行初始化方法@PreDestroy
:在容器销毁bean之前通知我们进行清理工作
4. 通过让bean实现BeanPostProcessor接口
@Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了 public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {}
通过以上四种方式就可以对bean的整个生命周期进行控制:
- bean的实例化:调用bean的构造方法,我们可以在bean的无参构造方法中执行相应的逻辑。
- bean的初始化:在初始化时可以通过BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法进行拦截,执行自定义的逻辑。通过@PostConstruct注解、InitializingBean和init-method来指定bean初始化前后执行的方法,在该方法中可以执行自定义的逻辑。
- bean的销毁:可以通过@PreDestroy注解、DisposableBean和destroy-method来指定bean在销毁前执行的方法,在方法中可以执行自定义的逻辑。
所以,通过上述4种方式,我们可以控制Spring中bean的整个生命周期。
BeanPostProcessor源码解析
如果想深刻理解BeanPostProcessor的工作原理,那么就不得不看下相关的源码,我们可以在MyBeanPostProcessor类的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法这两处打上断点来进行调试,如下所示。
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization" + ",beanName:"+beanName + ",bean=>" + bean); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization" + ",beanName:"+beanName + ",bean=>" + bean); return bean; } @Override public int getOrder() { return 3; } }
通过断点调试,我们可以看到,在applyBeanPostProcessorBeforeInitialization()方法中,会遍历所有BeanPostProcessor对象,然后依次执行所有BeanPostProcessor对象的postProcessorBeforeInitialization()方法,一旦BeanPostProcessor对象的postProcessBeforeInitialization()方法返回null以后,则后面的BeanPostProcessor对象便不再执行了,而是直接退出for循环。这些都是我们看源码看到的。
看Spring源码,我们还看到一个细节,在Spring中调用initializeBean()方法之前,还调用了populateBean()方法来为bean的属性赋值。
经过一系列的跟踪源码分析,我们可以将关键代码的调用过程使用如下伪代码表述出来。
populateBean(beanName, mbd, instanceWrapper); // 给bean进行属性赋值 initializeBean(beanName, exposedObject, mbd) { applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); invokeInitMethods(beanName, wrappedBean, mbd); // 执行自定义初始化 applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); }
也就是说,在Spring中,调用initializeBean()方法之前,调用了populateBean()方法为bean的属性赋值,为bean的属性赋好值之后,再调用initializeBean()方法进行初始化。
在initializeBean()中,调用自定义的初始化方法(即invokeInitMethods())之前,调用了applyBeanPostProcessorsBeforeInitialization()方法,而在调用自定义的初始化方法之后,又调用了applyBeanPostProcessorsAfterInitialization()方法。至此,整个bean的初始化过程就这样结束了。
BeanPostProcessor接口在Spring底层的应用案例
ApplicationContextAwareProcessor类
org.springframework.context.support.ApplicationContextAwareProcessor是BeanPostProcessor接口的一个实现类,这个类的作用是可以向组件中注入IOC容器,大致的源码如下。
注意:我这里的Spring版本为4.3.12.RELEASE。
那具体如何使用ApplicationContextAwareProcessor类向组件中注入IOC容器呢?
如果需要向组件中注入IOC容器,那么可以让组件实现ApplicationContextAware接口。
例如,我们创建一个创建一个Dog类,使其实现ApplicationContextAware接口,此时,我们需要实现ApplicationContextAware接口中的setApplicationContext()方法,在setApplicationContext()方法中有一个ApplicationContext类型的参数,这个就是IOC容器对象,我们可以在Dog类中定义一个ApplicationContext类型的成员变量,然后在setApplicationContext()方法中为这个成员变量赋值,此时就可以在Dog类中的
其他方法中使用ApplicationContext对象了,如下所示。
package com.meimeixia.bean; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * ApplicationContextAwareProcessor这个类的作用是可以帮我们在组件里面注入IOC容器, * 怎么注入呢?我们想要IOC容器的话,比如我们这个Dog组件,只需要实现ApplicationContextAware接口就行 * */ @Component public class Dog implements ApplicationContextAware { private ApplicationContext applicationContext; public Dog() { System.out.println("dog constructor..."); } // 在对象创建完成并且属性赋值完成之后调用 @PostConstruct public void init() { System.out.println("dog...@PostConstruct..."); } // 在容器销毁(移除)对象之前调用 @PreDestroy public void destory() { System.out.println("dog...@PreDestroy..."); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 在这儿打个断点调试一下 // TODO Auto-generated method stub this.applicationContext = applicationContext; } }
看到这里,相信不少小伙伴们都有一种很熟悉的感觉,没错,我之前也在项目中使用过!是的,这就是BeanPostProcessor在Spring底层的一种使用场景。至于上面的案例代码为何会在setApplicationContext()方法中获取到ApplicationContext对象,这就是ApplicationContextAwareProcessor类的功劳了!
接下来,我们就深入分析下ApplicationContextAwareProcessor类。
我们先来看下ApplicationContextAwareProcessor类中对于postProcessBeforeInitialization()方法的实现,如下所示。
在bean初始化之前,首先对当前bean的类型进行判断,如果当前bean的类型不是EnvironmentAware,不是EmbeddedValueResolverAware,不是ResourceLoaderAware,不是ApplicationEventPublisherAware,不是MessageSourceAware,也不是ApplicationContextAware,那么直接返回bean。
如果是上面类型中的一种类型,那么最终会调用invokeAwareInterfaces()方法,并将bean传递给该方法。
invokeAwareInterfaces()方法又是个什么呢?我们继续看invokeAwareInterfaces()方法的源码,如下所示。
可以看到invokeAwareInterfaces()方法的源码比较简单,就是判断当前bean属于哪种接口类型,然后将bean强转为哪种接口类型的对象,接着调用接口中的方法,将相应的参数传递到接口的方法中。
我们可以看到,此时会将this.applicationContext传递到ApplicationContextAware接口的setApplicationContext()方法中。所以,我们在Dog类的setApplicationContext()方法中就可以直接接收到ApplicationContext对象了。
BeanValidationPostProcessor类
org.springframework.validation.beanvalidation.BeanValidationPostProcessor类注意是用来为bean进行校验操作的,当我们创建bean,并为bean赋值后,我们可以通过BeanValidationPostProcessor类为bean进行校验操作。BeanValidationPostProcessor类源码如下:
这里,我们也来看看postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法的实现,如下所示。
InitDestroyAnnotationBeanPostProcessor类
org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor类主要用来处理@PostConstruct注解和@PreDestroy注解。
使用了@PostConstruct注解和@PreDestroy注解来标注方法,Spring怎么就知道什么时候执行@PostConstruct注解标注的方法,什么时候执行@PreDestroy注解标注的方法呢?这就要归功于InitDestroyAnnotationBeanPostProcessor类了。
接下来,我们也通过Debug的方式来跟进下代码的执行流程。首先,在Dog类的initt()方法上打上一个断点,如下所示。
在InitDestroyAnnotationBeanPostProcessor类的postProcessBeforeInitialization()方法中,首先会找到bean中有关生命周期的注解,比如@PostConstruct注解等,找到这些注解之后,则将这些信息赋值给LifecycleMetadata类型的变量metadata,之后调用metadata的invokeInitMethods()方法,通过反射来调用标注了@PostConstruct注解的方法。
这就是为什么标注了@PostConstruct注解的方法会被Spring执行的原因。
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass()); try { metadata.invokeInitMethods(bean, beanName); } catch (InvocationTargetException ex) { throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException()); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Failed to invoke init method", ex); } return bean; }
AutowiredAnnotationBeanPostProcessor类
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor类主要是用于处理标注了@Autowired注解的变量或方法。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。