最近在重读spring源码(为什么要重读?因为不得不承认,去年跟着《深入解析sping源码》一书过了一遍spring的源码,除了满脑袋都是各种BeanFactory跟BeanDefinition外,真的没什么收获...),

而第二遍由于是分模块分功能点去看,有了针对性,再加上之前囫囵吞枣的那一遍给我带来的整体认识,这一次顺畅了许多,对spring的理解亦深入了下去。所以说,阅读源码真的能带给人很多的收获,

而且不止于代码。后面会专门找个合适的时机对自己学习以及阅读源码这个过程做个总结,今天先聊重点:Spring的后处理器是如何发挥作用的

零:什么是后处理器

Spring的后处理器只要有两大类,一个是针对BeanDefinition的容器级别的后处理器 - BeanFactoryPostProcessor(后面简称BFPP);一个是针对getBean操作获得的对象的后处理器 -

BeanPostProcessor(后面简称BPP)。

此两个后处理器不同之处主要有三点:

1、触发时机不同,前者BFPP是在容器refresh方法中调用,而后者实际调用时机是在getBean方法获取对象时调用;

2、因触发时机不同导致二者处理的对象不同。BFPP处理的是解析完配置文件后注册在容器中的BeanDefinition,而BPP处理的是通过反射生成

的实例Bean;

3、接口样式不同,BFPP只有一个后处理方法,而BPP有一个前置处理方法一个后置处理方法。

接口如下所示:

 public interface BeanFactoryPostProcessor {

     void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

 }

 public interface BeanPostProcessor {

     default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
} default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
} }

一:BeanFactoryPostProcessor

在ApplicationContext容器初始化的核心方法refresh方法中,初始化完BeanFactory后会执行invokeBeanFactoryPostProcessors方法,就是在此方法中完成了对BFPP的调用。代码如下所示:

 protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}

可以知道,完成后处理器调用的方法是第二行的代码。此处请记住这个类PostProcessorRegistrationDelegate,它是处理后处理器的核心类。对于此处的方法invokeBeanFactoryPostProcessors,

代码很长,就不贴出来了,下面只做一些简单的梳理。

1、先判断当前BeanFactory是不是BeanDefinitionRegistry,如果是则将实现BeanDefinitionRegistryPostProcessor(简称BDRPP)接口的类找到,按顺序执行后处理器的方法。此BDRPP接口是BFPP

接口的子类。其作用是什么?为什么要单独对其进行处理?看其后置方法便可只其一二。它的后置处理方法参数是一个BeanDefinitionRegistry,它是做什么用的?往容器中注册BeanDefinition的。所以此

处单独处理BDRPP的原因也就基本明了了 - 就是根据实际需要在此处创建BeanDefinition往容器中注册的,即留了一个往容器中注册bean的后门。

2、再获取所有实现了BeanFactoryPostProcessor接口的子类,按照顺序执行后处理器方法。

二:BeanPostProcessor

BPP后处理器的处理,相比BFPP多了一步,BFPP是在ApplicationContext容器初始化的时候就调用了,而BPP是在容器初始化时注册,调用则是在getBean获取对象实例时触发。

        1、BPP的注册

同样是在refresh方法中,就在调用BFPP方法的下面,调用了注册BPP的方法  - registerBeanPostProcessors方法。

 protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

看到熟悉的面孔了吧,对,还是它,那个调用BFPP的类!

此方法代码同样不少,此处就不贴出来了,主要梳理一下代码逻辑:

1)、首先是获取BPP实例。获取BPP子类的套路跟上文中获取BFPP子类的套路基本一样,先通过类的type获取到注册到当前容器中的所有的子类,然后根据是否实现了PriorityOrdered、Ordered接口

对其进行分类、排序,最后再通过beanFactory的getBean方法获取到BeanPostProcessor的实例对象;

2)、注册BPP实例。注册的顺序是先注册实现了PriorityOrdered的实例,再注册Ordered的实例,最后注册普通的BPP。此处的注册是指将这个BPP放入AbstractBeanFactory维护的一个成员变量中,

此变量是一个CopyOnWriteArrayList。在加入之前会先执行一下remove方法,防止重复加入。

2、BPP的调用

BeanFactory的多个getBean方法,大多是在AbstractBeanFactory方法中实现,而最终创建bean实例,则是在AbstractAutowireCapableBeanFactory中实现。在此类的doCreateBean方法的initializeBean

方法中,实现了对BPP的调用。方法代码如下所示:

 protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
} Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
} try {
invokeInitMethods(beanName, wrappedBean, mbd);
}//此处catch异常处理代码已被BZ去掉 if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
} return wrappedBean;
}

由代码可知,BPP的前置、后置方法,分别在第14行、第22行被调用,返回的结果即创建出来的bean。

三:小结

通过这两大类后处理器,能充分感觉到Spring对于框架扩展性做出的努力。后处理器理解实现过程不难,难的是如何在实际工作中灵活的利用spring的这个扩展性解决项目中的问题,或者利用其给

开发带来效率上的提升。常用的场景比如过滤bean的某些内容,对其进行特殊处理(实际工作中BZ也没用过 ><)。以上就是BZ对spring中后处理器的理解,可能因水平或者思考角度等原因有理解不到位的

地方,欢迎各位猿友们批评指正!

就此别过,下期初步打算讲讲AOP的相关原理。

Spring的后处理器-BeanPostProcessor跟BeanFactoryPostProcessors的更多相关文章

  1. Spring Bean后处理器以及容器后处理器【转】

    Bean后处理器:即当spring容器实例化Bean实例之后进行的增强处理. 容器后处理器:对容器本身进行处理,并总是在容器实例化其他任何Bean之前读取配置文件的元数据并可能修改这些数据. 一.Be ...

  2. Spring框架——后处理器

    Bean的后处理 Spring容器实例化Bean实例之后进行的增强处理,关于这里的描述之前有点错误,现在来纠正一下:这个过程有点像AOP,不过我们知道AOP是对方法而言的,而Bean后处理器是针对Ja ...

  3. Spring.net 后处理器 可用来切换实例

    .xml配置 <!--我们在Object.xml文件上将HexuObjectPostProcessor注册到上下文对象中去--> <object id="hexu" ...

  4. 8 -- 深入使用Spring -- 1...1Bean后处理器

    8.1.1 Bean后处理器(BeanPostProcessor) Bean后处理器主要负责对容器中其他Bean执行后处理,例如为容器中的目标Bean生成代理等. Bean后处理器会在Bean实例创建 ...

  5. Spring - BeanPostProcessor接口(后处理器)讲解

    概述: BeanPostProcessor接口是众多Spring提供给开发者的bean生命周期内自定义逻辑拓展接口中的一个,其他还有类似InitializingBean,DisposableBean, ...

  6. Spring点滴五:Spring中的后置处理器BeanPostProcessor讲解

    BeanPostProcessor接口作用: 如果我们想在Spring容器中完成bean实例化.配置以及其他初始化方法前后要添加一些自己逻辑处理.我们需要定义一个或多个BeanPostProcesso ...

  7. Spring中的后置处理器BeanPostProcessor讲解

    Spring中提供了很多PostProcessor供开发者进行拓展,例如:BeanPostProcessor.BeanFactoryPostProcessor.BeanValidationPostPr ...

  8. spring学习四:Spring中的后置处理器BeanPostProcessor

    BeanPostProcessor接口作用: 如果我们想在Spring容器中完成bean实例化.配置以及其他初始化方法前后要添加一些自己逻辑处理.我们需要定义一个或多个BeanPostProcesso ...

  9. 04Spring_bean 后处理器(后处理Bean),BeanPostProcessor ,bean创建时序,动态代理

    这篇文章很重要,讲解的是动态代理,以及bean创建前后的所发生的事情.介绍一个接口:在Spring构造Bean对象过程中,有一个环节对Bean对象进行 后处理操作 (钩子函数) ----- Sprin ...

随机推荐

  1. 距离不是一个连续的物理量(Distance is not a continuous physical quantity)

    量子距:不同于现有物理学的长度计量.量子距,空间中的两个粒子之间的距离并不是连续的,而是某个单位距(量子单位距)的整数倍,而这个距离被称为量子距. Quantum distance: Length m ...

  2. Codeforces Round #555 (Div. 3) E. Minimum Array

    题意:b数组可以自由排序,c[i]=(a[i]+b[i])%n. 题目中要求c数组的字典序是最小的.那么我们需要尽量满足前面的c[i],才能使字典序最小. 我们知道a[i]和b[i]都是[0,n-1] ...

  3. final,static,abstract

    一.final 1.可以修饰属性,表示常量,其值在运行期间不允许改变.常量必须赋初始值,初始值可以在声明出,构造方法的任意一个地方赋 优点:增强程序的可读性,可维护性 2.可以修饰方法,表示该方法不能 ...

  4. HTML表单标签

    <form>标签 1.在HTML中,<form></form>标记对用来创建一个表单,即定义表单的开始和结束位置,在标记对之间的一切都属于表单的内容.每个表单元素开 ...

  5. 安装 redis 拓展

    PHP API 20121113 PHP Extension 20121212 Zend Extension 220121212 Zend Extension Build API220121212,T ...

  6. unic

    在线考试 答题剩余时间0小时51分18秒 考生须知 1.本次考试结束后,剩余补考次数:2次 2.考试时间为60分钟,超时系统自动交卷 3.本次考试满分100分(5*20道),60分通过考试 1. (单 ...

  7. Jsp处理过程and数据交互

    request处理客户端请求 客户端-------------->jsp页面--------------->服务器 常用方法 1.String getParameter(String na ...

  8. 初探ansible

    Ansible 基于ssh的自动化运维工具 ansible 配置文件详解 ansible.cfg 文件 文件默认放置在/etc/ansible下,ansible读取配置文件的顺序是: 当前命令执行目录 ...

  9. django安装及简单使用

    1 web应用2 cs和bs架构3 http协议----重点 -特点:1 2 3 4 -请求头: 请求首行 get / http/1.1\r\n key:value\r\n key:value\r\n ...

  10. ADO.NET学习笔记(1)

    ADO.Net是.Net框架中为数据库的访问而封装的一个库.通过这个库我们可以简单便捷的访问数据库,并对数据库进行一些增删改查的操作,目前ADO.Net支持四种主流的数据库,分别是SQL.OLE DB ...