目录

  • 一、解释
    • 1.1、方法
  • 二、demo
    • 2.1 创建一个类继承 InstantiationAwareBeanPostProcessor
  • 三、源码解析
    • 3.1 postProcessAfterInstantiation,postProcessBeforeInstantiation
    • 3.2 postProcessProperties(重要)
      • 3.2.1 postProcessProperties 运行时机
      • 3.2.2 CommonAnnotationBeanPostProcessor # postProcessProperties
      • 3.2.3 AutowiredAnnotationBeanPostProcessor # postProcessProperties
  • 四、总结

一、解释

InstantiationAwareBeanPostProcessor 是 BeanPostProcessor 的子接口,它添加了实例化之前的回调,以及在实例化之后但设置了显式属性或发生自动装配之前的回调。
这里 首先要区分两个概念,一个是 Instantiation ,一个是 Initialization ,
Instantiation :实例化 ,就是创建Bean 的过程(比如调用构造函数) ,
Initialization :初始化, 就是对Bean 进行赋值(比如调用setter 方法),配置属性的过程.

这里的 InstantiationAwareBeanPostProcessor 就是这个Instantiation 阶段。BeanPostProcessor 就是Initialization 阶段,也可以 从两个接口 里面的 方法 可以看出,BeanPostProcessor 里面的方法是 xxxxBeforeInitialization,
xxxAfterInitialization.

1.1、方法

InstantiationAwareBeanPostProcessor 本身就3个方法,
postProcessAfterInstantiation,postProcessBeforeInstantiation 这两个方法 都比较好理解,就是在实例化之前和之后 进行的一系列操作. 我们需要了解 这两个方法 在 启动时,是在具体哪里运行的(下面有分析),便于我们后续的扩展.

postProcessProperties 这个方法 , 主要是后缀处理通过注解注入属性的, 其实里面还有一个 被抛弃的方法 postProcessPropertyValues 和这个是一样的。postProcessProperties 这个方法还是比较重要的,主要就是处理 通过注解注入属性的一系列操作, 下面也会想想分析的。

    //实例化之后的处理default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {return true;}//实例化之前的处理@Nullabledefault Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {return null;}//修改属性值@Nullabledefault PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)throws BeansException {return null;}

二、demo

2.1 创建一个类继承 InstantiationAwareBeanPostProcessor

主要实现postProcessBeforeInstantiation 这个方法,用于在创建之前的操作 ,和postProcessAfterInitialization 这个方法,用于初始化之后的操作,后面源码会提到这两个方法
public class MyselfIBeanPostProcessor implements InstantiationAwareBeanPostProcessor {@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {System.out.print("beanName:" + beanName + "执行..postProcessAfterInstantiation");return true;}@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {if (beanClass == BeanTest.class) {System.out.println("beanName:" + beanName + "执行..postProcessBeforeInstantiation 方法");Enhancer enhancer = new Enhancer();enhancer.setSuperclass(beanClass);enhancer.setCallback(new BeanTestMethodInterceptor());BeanTest beanTest = (BeanTest) enhancer.create();return beanTest;}return null;}@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)throws BeansException {System.out.println("beanName:  postProcessProperties    执行..postProcessProperties");return pvs;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行 postProcessAfterInitialization...");return bean;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行..postProcessBeforeInitialization ...");return bean;}
}

2.2 创建 一个 BeanTestMethodInterceptor
实现 MethodInterceptor ,对方法进行拦截

public class BeanTestMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {if(method.getName().equalsIgnoreCase("getName")){System.out.println("调用 getName 方法 ");}else if(method.getName().equalsIgnoreCase("setName")){objects = new Object[]{"被替换掉啦"};}Object object = methodProxy.invokeSuper(o, objects);return object;}
}

2.2 创建一个BeanTest 类

public class BeanTest {private String name;public BeanTest() {System.out.println("执行构造函数");}public BeanTest(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

2.4 创建一个XML文件
在resource下面 创建一个文件applicationContext-Instantion.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="mselfIBeanPostProcessor"class="com.self.test1.MyselfIBeanPostProcessor"/><bean id="bean" class="com.self.test1.BeanTest" /></beans>

写个Main 方法,测试一下:

public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-Instantion.xml");BeanTest bean = ctx.getBean("bean", BeanTest.class) ;bean.setName("原始值");String name = bean.getName();System.out.println(name);}

结果如下: 可以看到 执行了 postProcessBeforeInstantiation 和 postProcessAfterInitialization 这两个方法

beanName:bean执行..postProcessBeforeInstantiation 方法
执行构造函数
执行 postProcessAfterInitialization...
调用 getName 方法
被替换掉啦

三、源码解析

3.1 postProcessAfterInstantiation,postProcessBeforeInstantiation

InstantiationAwareBeanPostProcessor 的方法 postProcessBeforeInstantiation ,从名字就可以看出 在创建Bean 时候调用(而且是 创建之前),具体源码可以定位到 AbstractAutowireCapableBeanFactory#createBean 的 方法里面,部分源码如下,其实逻辑相对简单:
有一个点需要 注意;如果有了代理类,就不走下面的逻辑了,直接返回

 try {// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.// 这里便是 提前  给 BeanPostProcessors  返回一个代理类 ,这里便是调用的地方了Object bean = resolveBeforeInstantiation(beanName, mbdToUse);// 这里也是 关键,如果 bean  不为空,说明有了代理类,就不用走下面的// doCreateBean 方法了if (bean != null) {return bean;}}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}try {// 如果上面没有代理 类,那就走这里的创建分支Object beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) {logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;}catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {// A previously detected exception with proper bean creation context already,// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.throw ex;}

具体的resolveBeforeInstantiation 如下:

 protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {Object bean = null;if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {// Make sure bean class is actually resolved at this point.if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {Class<?> targetType = determineTargetType(beanName, mbd);if (targetType != null) {// 从这里也可以看出 先运行的applyBeanPostProcessorsBeforeInstantiation 方法bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);if (bean != null) {// 如果不为空,有代理类,那就再运行初始化之后的操作bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);}}}mbd.beforeInstantiationResolved = (bean != null);}return bean;}protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {// 进到这里,已经完全OK了,这里就是运行的InstantiationAwareBeanPostProcessor  类的//postProcessBeforeInstantiation 方法InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);if (result != null) {return result;}}}return null;}public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;// 这里 也是设计得比较好的,这里运行的是 所有的 BeanPostProcessor 的postProcessAfterInitialization 方法for (BeanPostProcessor processor : getBeanPostProcessors()) {Object current = processor.postProcessAfterInitialization(result, beanName);if (current == null) {return result;}result = current;}return result;}

3.2 postProcessProperties(重要)

主要分析这个方法, 个人感觉这个方法比 上面两个方法 重(fu)要(za)

3.2.1 postProcessProperties 运行时机

postProcessProperties 方法 运行的 时机 ,主要是 在 填充属性的时候, 就是 AbstractAutowireCapableBeanFactory#populateBean 这个里面 调用的, 主要是在填充属性之前 再进行相关的操作.

InstantiationAwareBeanPostProcessor # postProcessProperties 方法被实现的了也比较多, 这里主要讲两个 :

  • CommonAnnotationBeanPostProcessor : 主要 注册带有 @Resource 注解的 属性
  • AutowiredAnnotationBeanPostProcessor : 主要解决 带有 @Autowired,@Value,@Lookup,@Inject 注解的属性

3.2.2 CommonAnnotationBeanPostProcessor # postProcessProperties

首先 我们 可以看到,CommonAnnotationBeanPostProcessor 有一个 static 静态块,将需要解析的注解 都先加载进来

接下来主要分析 postProcessProperties 方法

 @Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {/**这里主要的逻辑 就是 找到 对应的带有注解的Metadata ,然后注入进去主要逻辑在下面*/InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);}catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);}return pvs;}

分析 findResourceMetadata 类

private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {// 用 beanName 作为 缓存的Key ,没有beanName 使用 类名String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());// 首先从 缓存里面获取,是否已经存在InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);// needsRefresh 其实就是判断 是否为null ,为null 就重新创建, 这里是一个 double-checkif (InjectionMetadata.needsRefresh(metadata, clazz)) {synchronized (this.injectionMetadataCache) {metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {// 如果不为null ,先清除一下if (metadata != null) {metadata.clear(pvs);}// 这里开始创建,并放入缓存,详细在下面分析metadata = buildResourceMetadata(clazz);this.injectionMetadataCache.put(cacheKey, metadata);}}}return metadata;}

分析 buildResourceMetadata 类 ,虽然这个方法代码比较长,其实逻辑 特别简单
就是 对 字段,方法 遍历,看是否带有 @WebServiceRef, @Resource,@EJB 注解 ,如果带有 就对 对应的注解 进行解析, 然后 再就放入list

这里涉及一个 桥接 方法 , 详细见 Spring源码解析之- BridgeMethodResolver详解

private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {// 这里过滤一下 spring 自己内部类, 像以 java. 开头的, java. 开头的一般都是 jdk自己的 类.if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {return InjectionMetadata.EMPTY;}List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;// 首先这里是一个循环, 主要是可能当前类 有继承的情况,需要 迭代对 父类也进行处理do {final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();/**这里 首先是对字段 进行处理调用了ReflectionUtils, 就是 通过回调 方法,进行 校验判断如果 field 上面有 @WebServiceRef, @Resource,@EJB 注解 ,就放入list注意的是,不能放在 静态字段上面,不然报错*/ReflectionUtils.doWithLocalFields(targetClass, field -> {if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");}currElements.add(new WebServiceRefElement(field, field, null));}else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@EJB annotation is not supported on static fields");}currElements.add(new EjbRefElement(field, field, null));}else if (field.isAnnotationPresent(Resource.class)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static fields");}if (!this.ignoredResourceTypes.contains(field.getType().getName())) {currElements.add(new ResourceElement(field, field, null));}}});/**这里 是对方法 进行处理调用了ReflectionUtils, 就是 通过回调 方法,进行 校验判断如果 方法 上面有 @WebServiceRef, @Resource,@EJB 注解 ,就放入list@WebServiceRef 不能修饰static方法 , 方法只能有一个 参数,一般就是 setter 方法,就是一个参数*/ReflectionUtils.doWithLocalMethods(targetClass, method -> {Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {return;}if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");}if (method.getParameterCount() != 1) {throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);}PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));}else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@EJB annotation is not supported on static methods");}if (method.getParameterCount() != 1) {throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);}PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new EjbRefElement(method, bridgedMethod, pd));}else if (bridgedMethod.isAnnotationPresent(Resource.class)) {if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static methods");}Class<?>[] paramTypes = method.getParameterTypes();if (paramTypes.length != 1) {throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);}if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new ResourceElement(method, bridgedMethod, pd));}}}});/**这里要注意了, 这里是 后面的插在前面,每次插入都是从 index =0 开始插入**/elements.addAll(0, currElements);targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);return InjectionMetadata.forElements(elements, clazz);}

接下来 InjectionMetadata 的inject 方法 ,这里主要是获取 里面的 InjectedElement 列表 ,然后开始挨个注入
这里 主要是 @Resoure 注解,所以 具体的注解逻辑在ResourceElement ,这里主要逻辑在getResourceToInject 下面 详细分析

 public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {// 获取对应的InjectedElement  列表,然后挨个注入for (InjectedElement element : elementsToIterate) {if (logger.isTraceEnabled()) {logger.trace("Processing injected element of bean '" + beanName + "': " + element);}element.inject(target, beanName, pvs);}}}

这里就是 最后的开始注入了,首先判断是否 是懒加载, 如果是 给出一个代理, 如果不是, 通过getResource 获取,里面也是调用 BeanFactory.getBean 的一个过程.

     @Overrideprotected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :getResource(this, requestingBeanName));}

3.2.3 AutowiredAnnotationBeanPostProcessor # postProcessProperties

上面分析了 CommonAnnotationBeanPostProcessor #postProcessProperties 主要是对 @Resource 注解进行 属性注入,
而 AutowiredAnnotationBeanPostProcessor #postProcessProperties 主要是对 @Autowired, @Value ,@Inject 等注解处理

具体的逻辑 和上面差不都 ,这里就不展开介绍了.

四、总结

InstantiationAwareBeanPostProcessor 主要是 在实例化前后作一些增强性的操作,是在 AbstractAutowireCapableBeanFactory#createBean 里面创建之前被触发调用的 , 而 postProcessProperties 方法是在 填充属性的时候,对一些 注解式属性 进行注入.

spring源码解析之---InstantiationAwareBeanPostProcessor解析相关推荐

  1. 深入浅出Spring源码:IOC原理解析(一)

    IOC(Inversion of Control),即控制反转,意思是将对象的创建和依赖关系交给第三方容器处理,我们要用的时候告诉容器我们需要什么然后直接去拿就行了.举个例子,我们有一个工厂,它生产各 ...

  2. Spring源码深度解析(郝佳)-学习-ASM 类字节码解析

    我们在Java字节码文件结构剖析(二)中己经对MyTest35_1这个类的字节码做了完整的解析,今天,我们来看看Spring的ASM技术是如何来解析Java类字节码的.话不多说,先上实例. MyTes ...

  3. Spring源码解析【完整版】--【bilibili地址:https://www.bilibili.com/video/BV1oW41167AV】

    [本文为bilibili视频雷丰阳的Spring源码解析的完整版总结文章,其中文章前面大部分为他人博文的搬运,后面补充了其未总结的部分] 一.Java的注解 1. 注解的概念 注释:用文字描述程序,给 ...

  4. 【Java】【系列篇】【Spring源码解析】【三】【体系】【BeanFactory体系】

    BeanFactory体系 BeanFactory整体结构体系图 顶层接口-BeanFactory 1.1.描述 1.2.方法解析(15个) 1.2.1.属性 1.2.2.获取bean实例 1.2.3 ...

  5. spring源码分析02-spring生命周期源码解析

    spring生命周期流程图: 1.spring扫描 Spring最重要的功能就是帮助程序员创建对象(也就是IOC),而启动Spring就是为创建Bean对象 做准备,所以我们先明白Spring到底是怎 ...

  6. 《Spring源码深度解析 郝佳 第2版》ApplicationContext

    往期博客: <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 <Spring源码深度 ...

  7. 《Spring源码深度解析 郝佳 第2版》bean的加载、循环依赖的解决

    往期博客: <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 往期博客完成了xml文件加载 ...

  8. Spring源码深度解析(郝佳)-学习-源码解析-基于注解注入(二)

    在Spring源码深度解析(郝佳)-学习-源码解析-基于注解bean解析(一)博客中,己经对有注解的类进行了解析,得到了BeanDefinition,但是我们看到属性并没有封装到BeanDefinit ...

  9. Spring源码解析 - AbstractBeanFactory 实现接口与父类分析

    2019独角兽企业重金招聘Python工程师标准>>> 我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegi ...

  10. 我该如何学习spring源码以及解析bean定义的注册

    如何学习spring源码 前言 本文属于spring源码解析的系列文章之一,文章主要是介绍如何学习spring的源码,希望能够最大限度的帮助到有需要的人.文章总体难度不大,但比较繁重,学习时一定要耐住 ...

最新文章

  1. 增加一个dbe连接_pogo pin连接器是如何解决振动的问题?
  2. CNZZ数据专家使用方法?
  3. is running beyond the ‘VIRTUAL‘ memory limit. Current usage: 123.5 MB of 1 GB physical memory used
  4. android textview动态设置,android – 如何动态设置文本到TextView?
  5. mybatis --入门 单表增删改查-curd
  6. 基于TensorFlow.js的JavaScript机器学习
  7. SonarQube中配置c语言/c++语言代码规则插件
  8. 数据结构与算法 汉诺塔问题和列车车厢重排问题
  9. LaTeX符号语法总结
  10. 常见14种手机传感器
  11. python实现zip分卷压缩与解压
  12. LiveGBS GB28181流媒体服务-产品介绍及相关资源
  13. P3D——《Learning Spatio-Temporal Representation with Pseudo-3D Residual Networks》概述
  14. 邮政邮件+邮政邮戳,电子邮件+电子邮戳
  15. vue——js实现图片/文件的拖拽上传(复制粘贴就能用,还有优化空间)
  16. outlook2007自动追加签名和日期
  17. 第四章 用户目录,SIP和 Verto
  18. EmEditor 设置行号和标尺
  19. 狂神Java面试题总结:基础及语法169道
  20. 牛客网NOIP赛前集训营-提高组(第七场)C-洞穴

热门文章

  1. 策略模式探究(二)多个门禁对接使用策略模式
  2. 图的深度优先遍历方式 Python
  3. java超级玛丽代码_java版超级玛丽源代码
  4. html 数据交互动画,H5交互动画创作平台推荐:爆米兔和木疙瘩
  5. 高等数学教材上册复习
  6. 如何在matlab中打开图片
  7. 计算机二级宝典百度云,计算机二级宝典
  8. 如何使用移动端后台管理数据
  9. SVN版本管理的回滚(SmartSVN)
  10. 在线键盘按键检测工具