写在前面

有些小伙伴问我,学习Spring是不是不用学习到这么细节的程度啊?感觉这些细节的部分在实际工作中使用不到啊,我到底需不需要学习到这么细节的程度呢?我的答案是:有必要学习到这么细节的程度,而且是有机会、有条件一定要学!吃透Spring的原理和源码!往往拉开人与人之间差距的就是这些细节的部分,当前只要是使用Java技术栈开发的Web项目,几乎都会使用Spring框架。而且目前各招聘网站上对于Java开发的要求几乎清一色的都是熟悉或者精通Spring。所以,你,很有必要学习Spring的细节知识点。

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

BeanPostProcessor后置处理器概述

首先,我们来看下BeanPostProcessor的源码,看下它到底是个什么鬼,如下所示。

package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
} }

从源码可以看出:BeanPostProcessor是一个接口,其中有两个方法,postProcessBeforeInitialization和postProcessAfterInitialization两个方法,这两个方法分别是在spring容器中的bean初始化前后执行,所以spring容器中的每一个bean对象初始化前后,都会执行BeanPostProcessor接口的实现类的这两个方法。

也就是说,postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。当容器中存在多个BeanPostProcessor的实现类时,会按照它们在容器中注册的顺序执行。对于自定义BeanPostProcessor实现类,还可以让其实现Ordered接口自定义排序。

因此我们可以在每个bean对象初始化前后,加上自己的逻辑。实现方式:自定义一个BeanPostProcessor接口的实现类MyBeanPostProcessor,然后在类MyBeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法里面写上自己的逻辑。

BeanPostProcessor后置处理器实例

我们创建一个MyBeanPostProcessor类,实现BeanPostProcessor接口,如下所示。

package io.mykit.spring.plugins.register.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component; /**
* @author binghe
* @version 1.0.0
* @description 测试BeanPostProcessor
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor { @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;
}
}

接下来,我们运行BeanLifeCircleTest类的testBeanLifeCircle04()方法,输出的结果信息如下所示。

调用了postProcessBeforeInitialization方法,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$e8ab4f2e@56528192
调用了postProcessAfterInitialization,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$e8ab4f2e@56528192
Cat类的构造方法...
调用了postProcessBeforeInitialization方法,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@1b1473ab
Cat的postConstruct()方法...
Cat的init()方法...
调用了postProcessAfterInitialization,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@1b1473ab
Cat的preDestroy()方法...
Cat的destroy()方法...

可以看到,postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。

也可以让实现Ordered接口自定义排序,如下所示。

package io.mykit.spring.plugins.register.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component; /**
* @author binghe
* @version 1.0.0
* @description 测试BeanPostProcessor
*/
@Component
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;
}
}

再次运行BeanLifeCircleTest类的testBeanLifeCircle04()方法,输出的结果信息如下所示。

调用了postProcessBeforeInitialization方法,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$b045438a@1ed1993a
调用了postProcessAfterInitialization,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$b045438a@1ed1993a
Cat类的构造方法...
调用了postProcessBeforeInitialization方法,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@36c88a32
Cat的postConstruct()方法...
Cat的init()方法...
调用了postProcessAfterInitialization,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@36c88a32
Cat的preDestroy()方法...
Cat的destroy()方法...

BeanPostProcessor后置处理器作用

后置处理器用于bean对象初始化前后进行逻辑增强。spring提供了BeanPostProcessor的很多实现类,例如AutowiredAnnotationBeanPostProcessor用于@Autowired注解的实现,AnnotationAwareAspectJAutoProxyCreator用于SpringAOP的动态代理等等。

除此之外,我们还可以自定义BeanPostProcessor的实现类,在其中写入需要的逻辑。下面以AnnotationAwareAspectJAutoProxyCreator为例,说明后置处理器是怎样工作的。我们都知道springAOP的实现原理是动态代理,最终放入容器的是代理类的对象,而不是bean本身的对象,那么spring是什么时候做到这一步的?就是在AnnotationAwareAspectJAutoProxyCreator后置处理器的postProcessAfterInitialization方法,即bean对象初始化完成之后,后置处理器会判断该bean是否注册了切面,如果是,则生成代理对象注入容器。Spring中的关键代码如下所示。

/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

好了,咱们今天就聊到这儿吧!别忘了给个在看和转发,让更多的人看到,一起学习一起进步!!

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

写在最后

如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Spring注解驱动开发。公众号回复“spring注解”关键字,领取Spring注解驱动开发核心知识图,让Spring注解驱动开发不再迷茫。

部分参考:https://www.cnblogs.com/dubhlinn/p/10668156.html

【Spring注解驱动开发】关于BeanPostProcessor后置处理器,你了解多少?的更多相关文章

  1. 【Spring注解驱动开发】BeanPostProcessor在Spring底层是如何使用的?看完这篇我懂了!!

    写在前面 在<[String注解驱动开发]面试官再问你BeanPostProcessor的执行流程,就把这篇文章甩给他!>一文中,我们详细的介绍了BeanPostProcessor的执行流 ...

  2. 0、Spring 注解驱动开发

    0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...

  3. 【spring 注解驱动开发】spring对象的生命周期

    尚学堂spring 注解驱动开发学习笔记之 - 生命周期 生命周期 1.生命周期-@Bean指定初始化和销毁方法 2.生命周期-InitializingBean和DisposableBean 3.生命 ...

  4. 【spring 注解驱动开发】Spring AOP原理

    尚学堂spring 注解驱动开发学习笔记之 - AOP原理 AOP原理: 1.AOP原理-AOP功能实现 2.AOP原理-@EnableAspectJAutoProxy 3.AOP原理-Annotat ...

  5. 【spring 注解驱动开发】扩展原理

    尚学堂spring 注解驱动开发学习笔记之 - 扩展原理 扩展原理 1.扩展原理-BeanFactoryPostProcessor BeanFactoryPostProcessor * 扩展原理: * ...

  6. 【spring 注解驱动开发】spring ioc 原理

    尚学堂spring 注解驱动开发学习笔记之 - Spring容器创建 Spring容器创建 1.Spring容器创建-BeanFactory预准备 2.Spring容器创建-执行BeanFactory ...

  7. spring注解驱动开发

    1.全图: 一.IOC容器部分 1.第一个初始化实例: @Configuration @ComponentScans @Bean("person") 注意: @repeatable ...

  8. 【Spring注解驱动开发】使用@Scope注解设置组件的作用域

    写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...

  9. 【Spring注解驱动开发】二狗子让我给他讲讲@EnableAspectJAutoProxy注解

    写在前面 最近,二狗子入职了新公司,新入职的那几天确实有点飘.不过慢慢的,他发现他身边的人各个身怀绝技啊,有Spring源码的贡献者,有Dubbo源码的贡献者,有MyBatis源码的贡献者,还有研究A ...

随机推荐

  1. Linux SCP命令远程复制文件

    从本地复制到远程 scp 本地文件 远程用户名@远程地址:远程目录(此命令回车后会要求输入密码,验证通过后会把本地文件复制到远程目录中,文件名不变) 或者 scp 本地文件 远程用户名@远程地址:远程 ...

  2. VNC下载,Windows系统下VNC如何下载和安装!

    IIS7服务器管理工具是一款VNC viewer的客户端,能够操作VNC,进行日常的功能实现:同时还可进行FTP的日常操作,能够高效地利用FTP的传输功能:也可以批量操作Windows.Linux系统 ...

  3. 深入理解JS:var、let、const的异同

    目录 序言 var 与 let 的区别 作用域 重复声明 绑定全局对象 变量提升与暂存死区 let 与 const 异同 参考 1.序言 var.let 和 const 都是 JavaScript 中 ...

  4. ajax实现注册并选择头像后上传

    在初次接触ajax后,我们做了一个crm训练的项目,大多数小组都有注册用户这一项,但是都忽略掉了一个功能,那就是,很多网站的注册是可以上传头像的,在这里我做了一个在已有的头像数组里选择图片上传作头像的 ...

  5. 初窥Ansible playbook

    Ansible是一个系列文章,我会尽量以通俗易懂.诙谐幽默的总结方式给大家呈现这些枯燥的知识点,让学习变的有趣一些. Ansible系列博文直达链接:Ansible入门系列 前言 在上一篇文章中说到A ...

  6. (Java实现) 洛谷 P1115 最大子段和

    题目描述 给出一段序列,选出其中连续且非空的一段使得这段和最大. 输入输出格式 输入格式: 第一行是一个正整数NN,表示了序列的长度. 第二行包含NN个绝对值不大于1000010000的整数A_iA ...

  7. java实现第N个素数

    素数就是不能再进行等分的整数.比如:7,11.而9不是素数,因为它可以平分为3等份.一般认为最小的素数是2,接着是3,5,... 请问,第100002(十万零二)个素数是多少? 请注意:2 是第一素数 ...

  8. Java实现第九届蓝桥杯测试次数

    测试次数 题目描述 x星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机. 各大厂商也就纷纷推出各种耐摔型手机.x星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后 ...

  9. iOS - 多线程——GCD

    什么是GCD           Grand Central Dispatch(强大的调度器),是一个C语言API:           作用:多核并行运算的解决方案:  GCD中有2个核心概念    ...

  10. ASP.NET Core 3.1 WebApi部署到腾讯云CentOS 7+Docker

    一.准备 首先需要有一台CentOS服务器,安装最新版Docker,配置镜像加速等,安装方法网上很多,下面是一些相关指令: yum install -y yum-utils device-mapper ...