【Spring注解驱动开发】BeanPostProcessor在Spring底层是如何使用的?看完这篇我懂了!!
写在前面
在《【String注解驱动开发】面试官再问你BeanPostProcessor的执行流程,就把这篇文章甩给他!》一文中,我们详细的介绍了BeanPostProcessor的执行流程。那么,BeanPostProcessor在Spring底层是如何使用的?今天,我们就一起来探讨下Spring的源码,一探BeanPostProcessor在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()方法。postProcessBeforeInitialization()方法会在bean初始化之前调用,postProcessAfterInitialization()方法会在bean初始化之后调用。接下来,我们就分析下BeanPostProcessor接口在Spring中的实现。
注意:这里,我列举几个BeanPostProcessor接口在Spring中的实现类,来让大家更加清晰的理解BeanPostProcessor接口在Spring底层的应用。
ApplicationContextAwareProcessor类
org.springframework.context.support.ApplicationContextAwareProcessor是BeanPostProcessor接口的实现类,这个类的作用是可以向组件中注入IOC容器,大致的源码如下所示。
package org.springframework.context.support;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.EmbeddedValueResolver;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.lang.Nullable;
import org.springframework.util.StringValueResolver;
class ApplicationContextAwareProcessor implements BeanPostProcessor {
/****************************省略N多行代码************************/
}
这里,省略了源码的细节,只给出了类结构,感兴趣的小伙伴们可自行翻阅Spring源码进行查看,我这里的Spring版本为5.2.6.RELEASE。
那具体如何使用ApplicationContextAwareProcessor类向组件中注入IOC容器呢?别急,我用一个例子来说明下,相信小伙伴们看完后会有一种豁然开朗的感觉——哦,原来是它啊,我之前在项目中使用过的!
要想使用ApplicationContextAwareProcessor类向组件中注入IOC容器,我们就不得不提Spring中的另一个接口:ApplicationContextAware,如果需要向组件中注入IOC容器,可以使组件实现ApplicationContextAware接口。
例如,我们创建一个Employee类,使其实现ApplicationContextAware接口,此时,我们需要实现ApplicationContextAware接口的setApplicationContext()方法,在setApplicationContext()方法中有一个ApplicationContext类型的参数,这个就是IOC容器对象,我们可以在Employee类中定义一个ApplicationContext类型的成员变量,然后在setApplicationContext()方法中为这个成员变量赋值,此时就可以在Employee中的其他方法中使用ApplicationContext对象了,如下所示。
package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author binghe
* @version 1.0.0
* @description 测试ApplicationContextAware
*/
@Component
public class Employee implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
看到这里,相信不少小伙伴们都有一种很熟悉的感觉:没错,我之前也在项目中使用过!是的,这就是BeanPostProcessor在Spring底层的一种使用场景。至于上面的案例代码为何会在setApplicationContext()方法中获取到ApplicationContext对象,这就是ApplicationContextAwareProcessor类的功劳了!
接下来,我们就深入分析下ApplicationContextAwareProcessor类。
我们先来看下ApplicationContextAwareProcessor类中对于postProcessBeforeInitialization()方法的实现,如下所示。
@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
return bean;
}
AccessControlContext acc = null;
if (System.getSecurityManager() != null) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
在bean初始化之前,首先对当前bean的类型进行判断,如果当前bean的类型不是EnvironmentAware,不是EmbeddedValueResolverAware,不是ResourceLoaderAware,不是ApplicationEventPublisherAware,不是MessageSourceAware,也不是ApplicationContextAware,则直接返回bean。如果是上面类型中的一种类型,则最终会调用invokeAwareInterfaces()方法,并将bean传递给invokeAwareInterfaces()方法。invokeAwareInterfaces()方法又是个什么鬼呢?我们继续看invokeAwareInterfaces()方法的源码,如下所示。
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
可以看到invokeAwareInterfaces()方法的源码比较简单,就是判断当前bean属于哪种接口类型,则将bean强转为哪种接口类型的对象,然后调用接口的方法,将相应的参数传递到接口的方法中。这里,我们在创建Employee类时,实现的是ApplicationContextAware接口,所以,在invokeAwareInterfaces()方法中,会执行如下的逻辑代码。
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
我们可以看到,此时会将this.applicationContext传递到ApplicationContextAware接口的setApplicationContext()方法中。所以,我们在Employee类中的setApplicationContext()方法中就可以直接接收到ApplicationContext对象了。
我们也可以在IDEA中通过Debug的形式来看一下程序的执行过程,此时我们在Employee类的setApplicationContext()方法上设置断点,如下所示。
接下来,我们以Debug的方式来运行SpringBeanTest类的testAnnotationConfig2()方法,运行后的效果如下图所示。
在IDEA的左下角可以看到方法的调用堆栈,通过对方法调用栈的分析,我们看到在执行Employee类中的setApplicationContext()方法之前,执行了ApplicationContextAwareProcessor类的invokeAwareInterfaces方法,如下所示。
当我们点击方法调用栈中的invokeAwareInterfaces()方法时,代码的执行定位到如下一行代码。
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
和我们之前分析的逻辑一致。
BeanValidationPostProcessor类
org.springframework.validation.beanvalidation.BeanValidationPostProcessor类主要是用来为bean进行校验操作,当我们创建bean,并为bean赋值后,我们可以通过BeanValidationPostProcessor类为bean进行校验操作。BeanValidationPostProcessor类的结构如下所示。
package org.springframework.validation.beanvalidation;
import java.util.Iterator;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
public class BeanValidationPostProcessor implements BeanPostProcessor, InitializingBean {
/*******************************省略N行代码**********************************/
}
这里,我们也来看看postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法的实现,如下所示。
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!this.afterInitialization) {
doValidate(bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (this.afterInitialization) {
doValidate(bean);
}
return bean;
}
可以看到,在postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法中的主要逻辑都是调用doValidate()方法对bean进行校验,只不过在两个方法中都会对afterInitialization这个boolean类型的成员变量进行判断,如果afterInitialization的值为false,则在postProcessBeforeInitialization()方法中调用doValidate()方法对bean进行校验;如果afterInitialization的值为true,则在postProcessAfterInitialization()方法中调用doValidate()方法对bean进行校验。
InitDestroyAnnotationBeanPostProcessor类
org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor类主要用来处理@PostConstruct注解和@PreDestroy注解。
例如,我们之前创建的Cat类中就使用了@PostConstruct注解和@PreDestroy注解,如下所示。
package io.mykit.spring.plugins.register.bean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* @author binghe
* @version 1.0.0
* @description 测试@PostConstruct注解和@PreDestroy注解
*/
public class Cat {
public Cat(){
System.out.println("Cat类的构造方法...");
}
public void init(){
System.out.println("Cat的init()方法...");
}
@PostConstruct
public void postConstruct(){
System.out.println("Cat的postConstruct()方法...");
}
@PreDestroy
public void preDestroy(){
System.out.println("Cat的preDestroy()方法...");
}
public void destroy(){
System.out.println("Cat的destroy()方法...");
}
}
那么,在Cat类中使用了 @PostConstruct注解和@PreDestroy注解来标注方法,Spring怎么就知道什么时候执行 @PostConstruct注解标注的方法,什么时候执行@PreDestroy标注的方法呢?这就要归功于InitDestroyAnnotationBeanPostProcessor类的实现了。
接下来,我们也通过Debug的方式来跟进下代码的执行流程。首先,在Cat类的postConstruct()方法上打上断点,如下所示。
接下来,我们以Debug的方式运行BeanLifeCircleTest类的testBeanLifeCircle04()方法,效果如下所示。
我们还是带着问题来分析,Spring怎么就能定位到使用@PostConstruct注解标注的方法呢?通过分析方法的调用栈我们发现了在进入使用@PostConstruct注解标注的方法之前,Spring调用了InitDestroyAnnotationBeanPostProcessor类的postProcessBeforeInitialization()方法,如下所示。
在InitDestroyAnnotationBeanPostProcessor类的postProcessBeforeInitialization()方法中,首先会找到bean中有关生命周期的注解,比如@PostConstruct注解等,找到这些注解之后,则将这些信息赋值给LifecycleMetadata类型的变量metadata,之后调用metadata的invokeInitMethods()方法,通过反射来调用标注了@PostConstruct注解的方法。这就是为什么标注了@PostConstruct注解的方法被Spring执行。
AutowiredAnnotationBeanPostProcessor类
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor类主要是用于处理标注了@Autowired注解的变量或方法。
Spring为何能够自动处理标注了@Autowired注解的变量或方法,就交给小伙伴们自行分析了。大家可以写一个测试方法并通过方法调用堆栈来分析AutowiredAnnotationBeanPostProcessor类的源码,从而找到自己想要的答案。
好了,咱们今天就聊到这儿吧!别忘了给个在看和转发,让更多的人看到,一起学习一起进步!!
项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation
写在最后
如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Spring注解驱动开发。公众号回复“spring注解”关键字,领取Spring注解驱动开发核心知识图,让Spring注解驱动开发不再迷茫。
【Spring注解驱动开发】BeanPostProcessor在Spring底层是如何使用的?看完这篇我懂了!!的更多相关文章
- 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!
写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...
- 【Spring注解驱动开发】关于BeanPostProcessor后置处理器,你了解多少?
写在前面 有些小伙伴问我,学习Spring是不是不用学习到这么细节的程度啊?感觉这些细节的部分在实际工作中使用不到啊,我到底需不需要学习到这么细节的程度呢?我的答案是:有必要学习到这么细节的程度,而且 ...
- 0、Spring 注解驱动开发
0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...
- 【spring 注解驱动开发】spring对象的生命周期
尚学堂spring 注解驱动开发学习笔记之 - 生命周期 生命周期 1.生命周期-@Bean指定初始化和销毁方法 2.生命周期-InitializingBean和DisposableBean 3.生命 ...
- 【Spring注解驱动开发】二狗子让我给他讲讲@EnableAspectJAutoProxy注解
写在前面 最近,二狗子入职了新公司,新入职的那几天确实有点飘.不过慢慢的,他发现他身边的人各个身怀绝技啊,有Spring源码的贡献者,有Dubbo源码的贡献者,有MyBatis源码的贡献者,还有研究A ...
- 【spring 注解驱动开发】spring自动装配
尚学堂spring 注解驱动开发学习笔记之 - 自动装配 自动装配 1.自动装配-@Autowired&@Qualifier&@Primary 2.自动装配-@Resource& ...
- 【spring 注解驱动开发】Spring AOP原理
尚学堂spring 注解驱动开发学习笔记之 - AOP原理 AOP原理: 1.AOP原理-AOP功能实现 2.AOP原理-@EnableAspectJAutoProxy 3.AOP原理-Annotat ...
- 【spring 注解驱动开发】扩展原理
尚学堂spring 注解驱动开发学习笔记之 - 扩展原理 扩展原理 1.扩展原理-BeanFactoryPostProcessor BeanFactoryPostProcessor * 扩展原理: * ...
- 【spring 注解驱动开发】spring ioc 原理
尚学堂spring 注解驱动开发学习笔记之 - Spring容器创建 Spring容器创建 1.Spring容器创建-BeanFactory预准备 2.Spring容器创建-执行BeanFactory ...
随机推荐
- Java并发编程 (二) 并发基础
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.CPU多级缓存-缓存一致性 1.CPU多级缓存 上图展示的是CPU高级缓存的配置,数据的读取和存 ...
- Java实现 蓝桥杯VIP 算法训练 JAM计数法
题目描述 Jam是个喜欢标新立异的科学怪人.他不使用阿拉伯数字计数,而是使用小 写英文字母计数,他觉得这样做,会使世界更加丰富多彩.在他的计数法中,每个数字的位数都是相同的(使用相同个数的字母),英文 ...
- Java实现 LeetCode 673 最长递增子序列的个数(递推)
673. 最长递增子序列的个数 给定一个未排序的整数数组,找到最长递增子序列的个数. 示例 1: 输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列,分别是 [1, 3, 4, ...
- Java实现 LeetCode 289 生命游戏
289. 生命游戏 根据百度百科,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机. 给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞.每个细胞具有 ...
- java实现 蓝桥杯 算法训练 安慰奶牛
问题描述 Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路.道路被用来连接N个牧场,牧场被连续地编号为1到N.每一个牧场都是一个奶牛的家.FJ计划除去P条道路中尽可能多的道路, ...
- java实现蓝桥杯密码脱落
一 问题描述 X星球的考古学家发现了一批古代留下来的密码. 这些密码是由A.B.C.D 四种植物的种子串成的序列. 仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串). 由于年代久远 ...
- java实现第三届蓝桥杯提取子串
提取子串 [代码填空](满分16分) 串"abcba"以字母"c"为中心左右对称:串"abba" 是另一种模式的左右对称.这两种情况我们都称 ...
- 本地存储 localStorage
本地存储localStorage 概念:window对象下面的属性,html5新增的,将5M大小的数据存储本地的浏览器上面. 浏览器支持存储5M大小 本地存储localStorage特点 本地存储属于 ...
- MongoDB知识点总结
一:MongoDB 概述 一.NoSQL 简介 1. 概念:NoSQL(Not Only SQL的缩写),指的是非关系型数据库,是对不同于传统的关系型数据库的数据库管理系统的统称.用于超大规模数 ...
- 如何使用PHP生成图片
/** * 从图片文件创建Image资源 * @param $file 图片文件,支持url * @return bool|resource 成功返回图片image资源,失败返回false */ fu ...