Spring之Bean后处理器——BeanPostProcessor的使用与源码解析
文章目录
- BeanPostProcessor介绍
- BeanPostProcessor执行流程
- BeanPostProcessor使用
- Book
- BookProxyBeanPostProcessor
- main
- 全量代码
- 执行结果
- 源码解析
- 总结
BeanPostProcessor介绍
BeanPostProcessor
接口使得程序可以在Bean被初始化的前后执行自定义动作,如检查或更改Bean
的属性值、包装即将注册到BeanFactory
的singletonObjects
中的原对象等。
BeanPostProcessor
接口提供了如下方法:
方法 | 描述 |
---|---|
Object postProcessBeforeInitialization(Object bean, String beanName)
|
在容器调用Bean 的InitializingBean#afterPropertiesSet() 或自定义的init-method 之前,调用该方法进行处理;传入的bean对象属性此时已经装配好。
|
Object postProcessAfterInitialization(Object bean, String beanName)
|
在容器调用Bean 的InitializingBean#afterPropertiesSet() 或自定义的init-method 之后,调用该方法进行处理;传入的bean对象属性此时已经装配好。
|
BeanPostProcessor执行流程
BeanPostProcessor
的执行会在Bean
实例化并装配好属性之后,before和after之间会插入Bean
的AfterPropertiesSet()
和init-method
的执行。
下图能比较好的反应出过程:
BeanPostProcessor使用
接下来写个例子。全量代码会在最后贴出来,直接CV运行即可。
Book
Book
类,它实现了InitializingBean
接口,其afterPropertiesSet()
方法仅输出一行信息用来记录执行流程;还有一个customInit()
方法使其加入到Spring
的init-method
的执行过程;该类还有一个show()
方法,用来在后面的BeanPostProcessor
处理器中对原始的Book
对象生成一个代理类,该代理类将对show()
方法进行增强处理,用来让咱们的BeanPostProcessor
能实际干点事儿。
public class Book implements InitializingBean {private String name;private String author;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public void show() {System.out.printf("book#show():%s\n", this);}public void customInit() {System.out.printf("%-70s:%s{%s}%n", "------Book#customInit", this.getClass().getSimpleName(), this);}@Overridepublic String toString() {return "Book{" +"name='" + name + '\'' +", author='" + author + '\'' +'}';}@Overridepublic void afterPropertiesSet() throws Exception {System.out.printf("%-70s:%s{%s}%n", "------Book#afterPropertiesSet", this.getClass().getSimpleName(), this);}}
BookProxyBeanPostProcessor
BookProxyBeanPostProcessor
实现了BeanPostProcessor
接口;在其postProcessBeforeInitialization()
方法中对Book
类型进行判断,如果是Book
类型则使用CGLIB生成其代理,用来增强Book#show()
方法,在其前后各打印一句话。
public class BookProxyBeanPostProcessor implements BeanPostProcessor {/*** 在容器调用Bean的InitializingBean#afterPropertiesSet()或自定义的init-method之前,调用该方法进行处理;传入的bean对象属性此时已经装配好。*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.printf("%-70s:%s{%s}%n","------BookProxyBeanPostProcessor#postProcessBeforeInitialization",bean.getClass().getSimpleName(),bean);if (Book.class.equals(bean.getClass())) {System.out.printf("\033[1;33m%-70s:对[%s]的\033[1;31mshow()\033[1;33m方法进行增强 \033[0m%n","------BookProxyBeanPostProcessor#postProcessBeforeInitialization",bean.getClass().getSimpleName());Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Book.class);enhancer.setCallback((MethodInterceptor) (obj, method, args, methodProxy) -> {if (!method.equals(Book.class.getMethod("show"))) {return methodProxy.invokeSuper(obj, args);}System.out.printf("\033[1;33m%-70s\033[0m%n", "Book#show()执行前");Object result = methodProxy.invokeSuper(obj, args);System.out.printf("\033[1;33m%-70s\033[0m%n", "Book#show()执行后");return result;});Book book = (Book) enhancer.create();book.setName(((Book) bean).getName());book.setAuthor(((Book) bean).getAuthor());return book;}return bean;}/*** 在容器调用Bean的InitializingBean#afterPropertiesSet()或自定义的init-method之后,调用该方法进行处理;传入的bean对象属性此时已经装配好。*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.printf("%-70s:%s{%s}%n","------BookProxyBeanPostProcessor#postProcessAfterInitialization",bean.getClass().getSimpleName(), bean);return bean;}
}
main
定义一个Book
类型的GenericBeanDefinition
,将Book#customInit()
添加到BeanDefinition
中,并设置MutablePropertyValues
用于初始化后的属性装配。
同时将BookProxyBeanPostProcessor
注册到容器中。
public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();GenericBeanDefinition bookBeanDefinition = new GenericBeanDefinition();bookBeanDefinition.setBeanClass(Book.class);bookBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);//指定为单例模式//设置属性bookBeanDefinition.setPropertyValues(new MutablePropertyValues().addPropertyValue(new PropertyValue("name", "《非暴力沟通》")).addPropertyValue(new PropertyValue("author", "马歇尔·卢森堡")));bookBeanDefinition.setInitMethodName("customInit");//设置初始化方法context.registerBeanDefinition("book", bookBeanDefinition);//注册BeanDefinitioncontext.registerBean("bookProxyBeanPostProcessor", BookProxyBeanPostProcessor.class);//注册Bean后处理器context.refresh();//刷新容器context.getBean("book", Book.class).show();
}
全量代码
package com.baiyang.beanpostprocessor;import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.context.support.GenericApplicationContext;public class BookProxyBeanPostProcessor implements BeanPostProcessor {/*** 在容器调用Bean的InitializingBean#afterPropertiesSet()或自定义的init-method之前,调用该方法进行处理;传入的bean对象属性此时已经装配好。*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.printf("%-70s:%s{%s}%n","------BookProxyBeanPostProcessor#postProcessBeforeInitialization",bean.getClass().getSimpleName(),bean);if (Book.class.equals(bean.getClass())) {System.out.printf("\033[1;33m%-70s:对[%s]的\033[1;31mshow()\033[1;33m方法进行增强 \033[0m%n","------BookProxyBeanPostProcessor#postProcessBeforeInitialization",bean.getClass().getSimpleName());Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Book.class);enhancer.setCallback((MethodInterceptor) (obj, method, args, methodProxy) -> {if (!method.equals(Book.class.getMethod("show"))) {return methodProxy.invokeSuper(obj, args);}System.out.printf("\033[1;33m%-70s\033[0m%n", "Book#show()执行前");Object result = methodProxy.invokeSuper(obj, args);System.out.printf("\033[1;33m%-70s\033[0m%n", "Book#show()执行后");return result;});Book book = (Book) enhancer.create();book.setName(((Book) bean).getName());book.setAuthor(((Book) bean).getAuthor());return book;}return bean;}/*** 在容器调用Bean的InitializingBean#afterPropertiesSet()或自定义的init-method之后,调用该方法进行处理;传入的bean对象属性此时已经装配好。*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.printf("%-70s:%s{%s}%n","------BookProxyBeanPostProcessor#postProcessAfterInitialization",bean.getClass().getSimpleName(), bean);return bean;}public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();GenericBeanDefinition bookBeanDefinition = new GenericBeanDefinition();bookBeanDefinition.setBeanClass(Book.class);bookBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);//指定为单例模式//设置属性bookBeanDefinition.setPropertyValues(new MutablePropertyValues().addPropertyValue(new PropertyValue("name", "《非暴力沟通》")).addPropertyValue(new PropertyValue("author", "马歇尔·卢森堡")));bookBeanDefinition.setInitMethodName("customInit");//设置初始化方法context.registerBeanDefinition("book", bookBeanDefinition);//注册BeanDefinitioncontext.registerBean("bookProxyBeanPostProcessor", BookProxyBeanPostProcessor.class);//注册Bean后处理器context.refresh();//刷新容器context.getBean("book", Book.class).show();}static class Book implements InitializingBean {private String name;private String author;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public void show() {System.out.printf("book#show():%s\n", this);}public void customInit() {System.out.printf("%-70s:%s{%s}%n", "------Book#customInit", this.getClass().getSimpleName(), this);}@Overridepublic String toString() {return "Book{" +"name='" + name + '\'' +", author='" + author + '\'' +'}';}@Overridepublic void afterPropertiesSet() throws Exception {System.out.printf("%-70s:%s{%s}%n", "------Book#afterPropertiesSet", this.getClass().getSimpleName(), this);}}
}
执行结果
从执行结果即可反应出BeanPostProcessor
的执行流程。
源码解析
spring版本:5.2.9
该节源码解析仅仅只讲关键的与BeanPostProcessor
相关的位置。如果想要知其与BeanPostProcessor
前后的细节,可以针对本节的所讲述的位置自行Debug进入详细阅读。
首先是Spring容器的经典入口:refresh()
然后调用AbstractApplicationContext
所持有的实际BeanFactory
(DefaultListableBeanFactory
)句柄的preInstantiateSingletons()
做Bean容器的初始化工作。
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
:
进入getBean()
方法,由于我们是新创建bean,所以流程将会是getBean()->doGetBean()->createBean()->doCreateBean()
的流程,咱们一路DEBUG到doCreateBean()
:
由于我们重点是在了解BeanPostProcessor
的内部执行流程上,所以上图隐藏了一些不太相关的代码,避免干扰;
此时还没有调用到BeanPostProcessor
,所以从doCreateBean
的执行流程可以看出,此时已经将Bean进行的实例化,并且装配好了属性值。
接着进入到关键方法exposedObject = initializeBean(beanName, exposedObject, mbd);
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
从上面的initializeBean()
的流程可以看到我们首先调用了BeanPostProcessor#postProcessBeforeInitialization()
,然后执行了invokeInitMethods()
来处理bean的实现的InitializingBean
接口的afterPropertiesSet()
与自定义init-method
;最后执行BeanPostProcessor#postProcessAfterInitialization()
;
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
从上面的方法可以看出BeanPostProcessor#postProcessBeforeInitialization
是可以改变即将注册到BeanFactory
中的实际对象的。
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods
从上面的invokeInitMethods()
可以看到包含了InitializingBean
接口的处理与init-method
的方法处理流程;
init-method
方法最终会通过反射执行。
最后就是BeanPostProcessor#postProcessAfterInitialization()
方法的执行:
至此initializeBean()
方法执行结束返回到doCreateBean()
:
doCreateBean()
将initializeBean()
返回的对象返回出去,一路回到getSingleton()
:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
可以看到此时,已经将一个经过了BeanPostProcess
处理的Bean
对象注册到了BeanFactory
中(在本例子里面是我们的BookProxyBeanPostProcessor
创建的Book
对象的代理类)。
总结
以上通过围绕BeanPostProcessor
创建一个对指定Bean
对象的代理对象,对其特定方法进行代理增强的例子,来介绍了BeanPostProcessor
的应用。
同时,通过对源码的浅析来对BeanPostProcessor
的执行前后流程进行了讲解。
Spring之Bean后处理器——BeanPostProcessor的使用与源码解析相关推荐
- Spring 注解面面通 之 @CrossOrigin 处理请求源码解析
@CrossOrigin源码解析主要分为两个阶段: ① @CrossOrigin注释的方法扫描注册. ② 请求匹配@CrossOrigin注释的方法. 本文针对第②阶段从源码角度进行解 ...
- Spring的Autowired自动装配(XML版本+Annotation版本+源码+解析)
http://moshowgame.iteye.com/blog/1607718 @Autowired自动装配 上面的例子我们用的都是手动装配的,如果DAO-Service一多那就很麻烦了,那么我们需 ...
- Spring 注解面面通 之 @CrossOrigin 注册处理方法源码解析
参照<Spring 注解面面通 之 @RequestMapping 注册处理方法源码解析>,其讲解了@RequestMapping注释的处理方法注册过程,而@CrossOrigin是基 ...
- 品Spring:详细解说bean后处理器
一个小小的里程碑 首先感谢能看到本文的朋友,感谢你的一路陪伴. 如果每篇都认真看的话,会发现本系列以bean定义作为切入点,先是详细解说了什么是bean定义,接着又强调了bean定义为什么如此重要. ...
- Spring Bean的生命周期以及IOC源码解析
IOC源码这一块太多只能讲个大概吧,建议还是去买本Spring IOC源码解析的书来看比较好,我也是自己看源代码以及视频整理的笔记 Bean的生命周期大概可以分为四个阶段,具体的等会再说,先看看IOC ...
- Spring MVC源码解析——HandlerMapping(处理器映射器)
Sping MVC 源码解析--HandlerMapping处理器映射器 1. 什么是HandlerMapping 2. HandlerMapping 2.1 HandlerMapping初始化 2. ...
- Spring源码解析(七)-Bean属性间的循环依赖
首先复习一下前面学习的Spring容器启动的大致流程,首先Spring会先扫描所有需要实例化的Bean,将这些Bean的信息封装成一个个BeanDefinition,然后注册到BeanDefiniti ...
- Spring源码解析-bean实例化
Spring源码解析-bean实例化 本文介绍Spring创建 bean 过程中的第一个步骤:实例化 bean. 1. Bean实例化源码 虽然实例化Bean有多种方式(包括静态工厂和工厂实例 ...
- Spring源码解析(五)-Bean的实例化流程(上)
在前面已经完成了对需要实例化bean的收集并封装成BeanDefinition,并且将BeanPostProcess等组件进行了提前实例化.接下来就到了容器启动的最后一步,也是最复杂的一步-实例化be ...
- Spring 源码解析 - Bean创建过程 以及 解决循环依赖
一.Spring Bean创建过程以及循环依赖 上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析,我们可以得到结论,资源文件中的 bean 定义信息,被组装成了 BeanDef ...
最新文章
- VC解析XML--使用CMarkup类解析XML
- 1、计算机系统硬件(面试小知识)
- 跨域解决方案之CORS
- python处理文本数据
- web语义化与h5新增标签
- Linux 命令(85)—— md5sum 命令
- 在线JWT Token解析解码
- Java词法分析器的设计与实现
- 泰勒公式和海森矩阵(Hessian-matrix)
- xy坐标转换为极坐标_xy坐标怎么换算(色坐标xy换算uv计算式)
- LaTex的箭头符号及命令
- 从零基础开始学习(一) esp32 micro python编程软件环境Thonny的安装
- ubuntu 打印git邮箱_win 10配置Ubuntu子系统—可以考虑放弃VMware了
- 很早以前就想要的东西,没想到今天会“妙手偶得”
- 救灾帐篷惊现高档小区
- 无线wifi如何远程唤醒?wake on lan网络唤醒及远程控制踩过的坑!
- 中山大学曾兆阳_实习派 | 曾兆阳: “宝藏男孩”的进阶之路
- GoogleCode中git push密码错误提示解决方案(Invalid username/password)
- Android8.0以上打开相机并裁剪图片
- Android 9 (P)版本解决VNDK library: XXX‘s ABI has EXTENDING CHANGES