这篇文章主要介绍了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注解的变量或方法。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程学习网。
本文标题为:Spring注解驱动之BeanPostProcessor后置处理器讲解
基础教程推荐
- ConditionalOnProperty配置swagger不生效问题及解决 2023-01-02
- java基础知识之FileInputStream流的使用 2023-08-11
- Java并发编程进阶之线程控制篇 2023-03-07
- JDK数组阻塞队列源码深入分析总结 2023-04-18
- Java数据结构之对象比较详解 2023-03-07
- java实现多人聊天系统 2023-05-19
- Java文件管理操作的知识点整理 2023-05-19
- Java实现线程插队的示例代码 2022-09-03
- springboot自定义starter方法及注解实例 2023-03-31
- Java实现查找文件和替换文件内容 2023-04-06