spring源码-bean的后置处理器

news2024/11/18 15:47:50

 

BeanPostProcessor

BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初始化前后)会回调BeanPostProcessor中定义的两个方法。

BeanPostProcessor的源码如下:

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

其中

方法

描述

postProcessBeforeInitialization

方法会在每一个bean对象的初始化方法调用之前回调;

postProcessAfterInitialization

方法会在每个bean对象的初始化方法调用之后被回调。具体执行时期可以参考Spring中Bean的生命周期源码探究。

查看BeanPostProcessor源码,可以看到它两个方法的参数都相同,其中第一个参数Object bean表示当前正在初始化的bean对象。此外两个方法都返回Object类型的实例,返回值既可以是将入参Object bean原封不动的返回出去,也可以对当前bean进行包装再返回。

InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor继承自BeanPostProcessor 是spring非常重要的拓展接口,代表这bean的一段生命周期: 实例化(Instantiation)

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

    @Nullable
    default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    @Nullable
    default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
            throws BeansException {

        return null;
    }

    @Deprecated
    @Nullable
    default PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {

        return pvs;
    }

}

方法

描述

postProcessBeforeInstantiation

最先执行的方法,它在目标对象实例化之前调用,该方法的返回值类型是Object,我们可以返回任何类型的值。由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用;否则按照正常的流程走

postProcessAfterInstantiation

在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是null。因为它的返回值是决定要不要调用postProcessProperties方法的其中一个因素(因为还有一个因素是mbd.getDependencyCheck());如果该方法返回false,并且不需要check,那么postProcessProperties就会被忽略不执行;如果返回true,postProcessProperties就会被执行

postProcessProperties

对属性值进行修改,如果postProcessAfterInstantiation方法返回false,该方法可能不会被调用。可以在该方法内对属性值进行修改

测试:

@Component
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
   /**
    * BeanPostProcessor接口中的方法
    * 在Bean的自定义初始化方法之前执行
    * Bean对象已经存在了
    */
   @Override
   public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      // TODO Auto-generated method stub
      System.out.println(">>postProcessBeforeInitialization");
      return bean;
   }

   /**
    * BeanPostProcessor接口中的方法
    * 在Bean的自定义初始化方法执行完成之后执行
    * Bean对象已经存在了
    */
   @Override
   public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      System.out.println("<<postProcessAfterInitialization");
      return bean;
   }

   /**
    * InstantiationAwareBeanPostProcessor中自定义的方法
    * 在方法实例化之前执行  Bean对象还没有
    */
   @Override
   public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
      System.out.println("--->postProcessBeforeInstantiation");
      return null;
   }

   /**
    * InstantiationAwareBeanPostProcessor中自定义的方法
    * 在方法实例化之后执行  Bean对象已经创建出来了
    */
   @Override
   public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
      System.out.println("<---postProcessAfterInstantiation");
      return true;
   }

   /**
    * InstantiationAwareBeanPostProcessor中自定义的方法
    * 可以用来修改Bean中属性的内容
    * 取代过时的postProcessPropertyValues方法
    */
   @Override
   public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
      System.out.println("<---postProcessProperties--->");
      return pvs;
   }
}

输出:

Task :spring-demo:RunApp.main()
--->postProcessBeforeInstantiation
<---postProcessAfterInstantiation
<---postProcessProperties--->
postProcessBeforeInitialization
<<postProcessAfterInitialization

SmartInstantiationAwareBeanPostProcessor

public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
   @Nullable
   default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
      return null;
   }
   @Nullable
   default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
         throws BeansException {
      return null;
   }
   default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
      return bean;
   }
}

方法

描述

predictBeanType

predictBeanType:该触发点发生在postProcessBeforeInstantiation之前,这个方法用于预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)时当通过bean的名字无法得到bean类型信息时就调用该回调方法来决定类型信息

determineCandidateConstructors

该触发点发生在postProcessBeforeInstantiation之后,用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表。用户可以扩展这个点,来自定义选择相应的构造器来实例化这个bean。

getEarlyBeanReference

getEarlyBeanReference:该触发点发生在postProcessAfterInstantiation之后,当有循环依赖的场景,当bean实例化好之后,为了防止有循环依赖,会提前暴露回调方法,用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发。

测试:

package com.sqz.ioc.beanpostprocess;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.beans.BeansException;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;
@Component
public class MySmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {


   /**
    * 预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null
    * @param beanClass
    * @param beanName
    * @return
    * @throws BeansException
    */
   @Override
   public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
      System.out.println("MySmartInstantiationAwareBeanPostProcessor.predictBeanType,beanName:"+beanName);
      return null;
   }

   /**
    * 选择合适的构造器,比如目标对象有多个构造器,在这里可以进行一些定制化,选择合适的构造器
    *
    * @param beanClass : beanClass参数表示目标实例的类型
    * @param beanName : beanName是目标实例在Spring容器中的name
    * @return  : 返回值是个构造器数组,如果返回null,会执行下一个PostProcessor的determineCandidateConstructors方法;否则选取该PostProcessor选择的构造器
    * @throws BeansException
    */
   @Override
   public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
      System.out.println("MySmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors,beanName:"+beanName);
      return null;
   }


   /**
    *  获得提前暴露的bean引用,主要用于解决循环引用的问题
    *  (只有单例对象才会调用此方法)
    * @param bean
    * @param beanName
    * @return
    * @throws BeansException
    */
   @Override
   public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
      System.out.println("MySmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference,beanName:"+beanName);
      return null;
   }

}

MergedBeanDefinitionPostProcessor

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {

   /**
    * Post-process the given merged bean definition for the specified bean.
    * @param beanDefinition the merged bean definition for the bean
    * @param beanType the actual type of the managed bean instance
    * @param beanName the name of the bean
    * @see AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
    */
   void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);

   /**
    * A notification that the bean definition for the specified name has been reset,
    * and that this post-processor should clear any metadata for the affected bean.
    * <p>The default implementation is empty.
    * @param beanName the name of the bean
    * @since 5.1
    * @see DefaultListableBeanFactory#resetBeanDefinition
    */
   default void resetBeanDefinition(String beanName) {
   }

}

方法

描述

postProcessMergedBeanDefinition

创建完 bean 实例之后,填充属性之前调用

一般做注解扫描

resetBeanDefinition

DefaultListableBeanFactory # registerBeanDefinition调用

一般做移除缓存

 DefaultListableBeanFactory # registerBeanDefinition


public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {
   // S1 : 会对 BeanDefinition 进行校验 , 主要是MethodOverrides和FactoryMethodName不能同时存在
   // -- 工厂方法必须创建具体的 Bean 实例 , 而 methodOverrides 会创建代理类且进行增强
   // -- 也就是说 工厂需要实例 , 不能是代理类
   if (beanDefinition instanceof AbstractBeanDefinition) {
       ((AbstractBeanDefinition) beanDefinition).validate();
   }
   // S2 : 判断 BeanDefinition 是否已经存在
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   if (existingDefinition != null) {
      // 是否允许同名Bean重写 , 因为此处已经存在一个了 , 不能重写则直接异常 
      if (!isAllowBeanDefinitionOverriding()) {
         throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
      }else if (existingDefinition.getRole() < beanDefinition.getRole()) {
          // 角色比较 ,只打日志 
          // ROLE_APPLICATION / ROLE_SUPPORT / ROLE_INFRASTRUCTURE
      }else if (!beanDefinition.equals(existingDefinition)) {
          // 判断是否为同一对象
      }
      // 以上主要是打log , 这里如果允许覆盖则直接覆盖了
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   else {
      // 判断该Bean是否已经开始初始化
      if (hasBeanCreationStarted()) {
         // 如果已经开始 , 需要对 Map 上锁后再处理
         synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);
            // 省略一些更新操作
         }
      }
      else {
         // 没有加载时的载入
         this.beanDefinitionMap.put(beanName, beanDefinition);
         this.beanDefinitionNames.add(beanName);
         removeManualSingletonName(beanName);
      }
      this.frozenBeanDefinitionNames = null;
   }
   // 如果Bean已经存在或已经开始加载了 , 这个时候时需要进行销毁操作的
   if (existingDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);
   }
}
protected void resetBeanDefinition(String beanName) {
   // S1 : 如果已经创建 ,需要清空 Merge BeanDefinition
   clearMergedBeanDefinition(beanName);
   // S2 : 销毁 Bean
   destroySingleton(beanName);
   // S3 : 调用 PostProcessors 重置处理器进行处理
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
      if (processor instanceof MergedBeanDefinitionPostProcessor) {
         ((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName);
      }
   }
   // S4 : 如果该 BeanDefinition 是某个BeanDefinition 的Parent , 则需要同步处理
   for (String bdName : this.beanDefinitionNames) {
      if (!beanName.equals(bdName)) {
         BeanDefinition bd = this.beanDefinitionMap.get(bdName);
         if (bd != null && beanName.equals(bd.getParentName())) {
            resetBeanDefinition(bdName);
         }
      }
   }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/82460.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FFU安装条件及八大步骤详解

FFU安装条件及八大步骤(FFU过滤器) FFU安装的条件、环境及安装人员的服饰要求: 温 度&#xff1a; 5℃~45℃ 相对湿度&#xff1a;不大于85%(35℃) 大气压力&#xff1a;86Kpa~106Kpa zui大尘埃浓度&#xff1a;3500粒/升(尘埃粒径不大于0.5um) 电 源&#xff1a;220V 50…

详解 TS 中的子类型兼容性

简介 在写 TypeScript 代码时经常遇到类型检查不通过的问题&#xff0c;这些问题根据编译器给出的错误提示以及修改建议多数可以快速修复。本文讲解的内容是编译器进行类型检查时的兼容性相关检查规则&#xff0c;这些规则在 TypeScript 语言背后默默发挥作用。不了解这些规则…

艾美捷寡聚脱氧核苷酸CpG ODN:ODN 1668

艾美捷CpG ODN&#xff1a;ODN 1668&#xff1a;具有硫代磷酸酯骨架的CpG寡脱氧核苷酸&#xff08;B型&#xff09;。小鼠TLR9&#xff08;Toll样受体9&#xff09;的特异性配体。 艾美捷Enzo CpG ODN&#xff1a;ODN 1668详细信息&#xff1a; 序列&#xff1a;5-tccatgtgtcc…

Akka 学习(九)Akka Cluster

参考文章 Gitter Chat&#xff0c;Akka 在线交流平台Akka Forums&#xff0c;Akka 论坛Akka in GitHub&#xff0c;Akka 开源项目仓库Akka Official Website&#xff0c;Akka 官网Akka Java API&#xff0c;Akka 应用程序编程接口《Akka入门与实践》 [加]Jason Goodwin&#x…

AI创业时代!这9个方向有钱途;AIGC再添霸榜应用Lensa;美团SemEval2022冠军方法分享;医学图像处理工具箱… | ShowMeAI资讯日报

&#x1f440;日报合辑 | &#x1f3a1;AI应用与工具大全 | &#x1f514;公众号资料下载 | &#x1f369;韩信子 &#x1f4e2; 2023年9个有前途的 AI 创业方向 https://www.aiplusinfo.com/blog/9-promising-artificial-intelligence-startup-ideas-for-2023/ 2022年&#x…

央视广告怎么做,做好央视广告的关键点有哪些

企业品牌打算投放央视广告&#xff0c;首先想到的是如何做才更有效果&#xff0c;往往本身就比较困惑。公司应该投入多少资金&#xff1f;应选用何种广告投放方案&#xff1f;有时公司内部的观点也是不同的。尤其是面对广告公司提供的众多方案&#xff0c;让负责人看着都头疼&a…

Tos-GPR-AMC,117961-27-0,标记肽Tos-GPR-7-氨基-4-甲基香豆素

胰蛋白酶和凝血酶的荧光底物 Tos-GPR-AMC, fluorogenic substrate for trypsin and thrombin. Kinetic data for Thrombin: kcat 180, Km 310 uM. 编号: 190611中文名称: 标记肽Tos-GPR-7-氨基-4-甲基香豆素英文名: Tos-Gly-Pro-Arg-AMCCAS号: 117961-27-0单字母: Tos-GPR-AM…

代码随想录训练营第6天

专题&#xff1a;哈希表 题目&#xff1a;有效的字母异位词 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 例如 输入: s "anagram", t "nagaram" 输出: true 说明: 你可以假设字符串只包含小写字母。 题目理解&…

av_interleaved_write_frame():Broken pipe

使用FFmpeg命令rtsp推流到Docker中MediaServer服务器&#xff0c;15秒timeout一到就会打印错误信息&#xff0c;如下图&#xff1a; av_interleaved_write_frame():Broken pipe 命令中指定rtsp传输方式为tcp&#xff08;-rtsp_transport tcp &#xff09;&#xff0c;虽然不会…

第二证券|锂电起火风险完美解决?美科学家研发新型超高盐度电解质

现在&#xff0c;研讨人员正致力于以各种方式改善电池技能&#xff0c;其中最重要的是处理可燃性问题。 美国科学家们近期研宣布了一种新的电解质配方&#xff0c;就能够以一种十分有出路的方式处理这一问题&#xff0c;依托额定的盐含量来防止有问题发生的化学反应。 众所周知…

[附源码]JAVA毕业设计疫情期间高校师生外出请假管理系统录屏(系统+LW)

[附源码]JAVA毕业设计疫情期间高校师生外出请假管理系统录屏&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&am…

婚礼策划预约小程序开发,拓展客户人群

婚礼对于所有人来说都是非常重要的一件事&#xff0c;是人生中最值得回忆的美好场景。在经济水平的不断提高下&#xff0c;人们在结婚之前都要找专业团队对婚礼进行策划&#xff0c;让婚礼更加完美&#xff0c;正因如此&#xff0c;婚礼策划行业得到了迅速发展。但是从众多策划…

Zabbix-proxy安装(zabbix 6.0LTS)

关于zabbix 系列的博文我已经很久没有更新了&#xff0c;主要原因是最近太忙了。如果读者诸君有认真阅读我前面的文章并亲自实践过的话&#xff0c;相信你肯定会大有收获的。到目前为止zabbix 的基础知识在我的博文中基本已经全都涉及到了&#xff0c;至于如何运用&#xff0c;…

赛狐ERP11月新功能汇总 | 13大场景,超90项功能升级

赛狐ERP月度《产品升级》栏目来啦&#xff01; 11月我们加紧迭代了运营、进销存、财务、报表等多个维度的功能内容&#xff0c;聚焦卖家关注的需求要点&#xff0c;进行了13大业务场景&#xff0c;超90项功能升级。赛狐ERP将不断优化产品细节&#xff0c;提升卖家体验&#xf…

CH36X系列接口芯片Linux系统开发库说明

一、概述 ch36x_lib库是Linux系统下用于开发CH36X系列设备CH365/CH367/CH368等芯片应用的软件接口库&#xff0c;此接口库主要提供以下功能&#xff1a; 设备打开/关闭 获取芯片类型/驱动版本 获取IO基地址/Memory基地址/中断号 配置空间读写&#xff08;支持单字节/双字节…

基于java+springboot+mybatis+vue+mysql的体育场馆运营系统

项目介绍 在系统流程分析当中调查分析它是比较重要的环节&#xff0c;因为在这个系统当中它都涉及到每个环节的业务流程&#xff0c;所以从计算机毕业设计SpringBootVue选题推荐—体育场馆运营系统的设计的整体设计上要保证各个信息的正确输入和输出以及对数据储存的完整&…

linux下JDK安装

先下载离线安装包&#xff0c;我将安装包直接放到/root/softPackages目录下&#xff08;需要安装包可私聊&#xff09; 1.卸载已有OpenJDK(如果有) 查找已经安装的OpenJDK包 rpm -qa | grep java 如果有查出结果&#xff0c;直接yum -y remove卸载即可&#xff1a; 例如&a…

MobileNetV1作为CenterNet的Backbone尝试

1、CenterNet对于Backbone的要求是&#xff1a; 输入为512*512&#xff0c;输出为&#xff1a;heatmap&#xff08;128*128*C&#xff09;、wh&#xff08;128*128*2&#xff09;、reg&#xff08;128*128*2&#xff09;。 2、原生的MobileNet V1输出&#xff1a; 最后一层通…

JSP:使用 sitemesh/decorator装饰器装饰jsp页面(原理及详细配置)

摘要&#xff1a;首先这个Decorator解释一下这个单词&#xff1a;“装饰器”&#xff0c;我觉得其实可以这样理解&#xff0c;他就像我们用到的Frame&#xff0c;他把每个页面共有的东西提炼了出来&#xff0c;也可能我们也会用各种各样的include标签&#xff0c;将我们的常用页…

关于使用C语言实现负值和非负值进行左右划分,奇数和偶数进行左右划分

目录 1.负值和非负值左右划分 2.偶数和奇数的划分 数据结构之折半查找&#xff08;递归和非递归&#xff09;&#xff0c;插值查找和斐波那契查找 归并排序求逆序对&#xff08;C语言&#xff09; 1.负值和非负值左右划分 要求&#xff1a;使用尽可能少的时间将一组连续的序…