Springboot扩展点之InstantiationAwareBeanPostProcessor

news2024/11/28 14:43:29

前言

前面介绍了Springboot的扩展点之BeanPostProcessor,再来介绍另一个扩展点InstantiationAwareBeanPostProcessor就容易多了。因为InstantiationAwareBeanPostProcessor也属于Bean级的后置处理器,还继于BeanPostProcessor,因此InstantiationAwareBeanPostProcessor除了可以实现BeanPostProcessor的扩展外,又额外增加了三个扩展点,这篇文章也是围绕这三个扩展点展示,主要介绍InstantiationAwareBeanPostProcessor扩展接口的功能特性、实现方式、工作原理、和应用场景,BeanPostProcessor扩展部分可以翻看前面的文章Springboot的扩展点之BeanPostProcessor

功能特性

1、虽然InstantiationAwareBeanPostProcessor继承于BeanPostProcessor,但是InstantiationAwareBeanPostProcessor的执行时机要稍早于BeanPostProcessor;

2、InstantiationAwareBeanPostProcessor有三个扩展方法,分别是:postProcessBeforeInstantiation()、postProcessAfterInstantiation()、postProcessProperties();

3、postProcessBeforeInstantiation()在Spring中Bean实例化前触发执行;

4、postProcessAfterInstantiation()、postProcessProperties()在Spring中Bean实例化后,属性注入前触发执行;

5、InstantiationAwareBeanPostProcessor继承于BeanPostProcessor的postProcessBeforeInitialization()和postProcessAfterInitialization()则在Spring中Bean实例化、属性注入完成后触发执行;

6、postProcessBeforeInstantiation()扩展点可以自定义个性化的Bean来替换掉目标Bean,需要注意的是替换掉目标Bean后,postProcessAfterInstantiation()会执行,其他的扩展点将不再触发;

7、postProcessAfterInstantiation()的返回值为布尔类型,如果返回值为true,则第三个扩展点postProcessProperties()会继续执行;如果返回值为false,则第三个扩展点postProcessProperties()将不再执行;

8、postProcessProperties()扩展点可以在目标Bean实例化后,属性注入前,对要注入的属性值内容进行更改,以替换掉原来的属性值;

实现方式

总的来说InstantiationAwareBeanPostProcessor扩展点的实现方式很简单,实现接口,重写相应的方法实现扩展逻辑,并用@Component注解标记实现类,其余的由Spring自动完成。下面通过示例逐步详细介绍:

1、定义一个Controller类(ExampleController),通过setter方法注入属性ExampService;

@RestController
@RequestMapping("/example")
@Slf4j
public class ExampleController {
    private String creator="gaox";
    private ExampleService exampleService;
    @Autowired
    public void setExampleService(ExampleService exampleService) {
        this.exampleService = exampleService;
        log.info("----ExampleController内的exampleService属性被注入");
    }
    public void setCreator(String creator) {
        this.creator = creator;
        log.info("----ExampleController内的creator属性被注入");
    }
    public String getCreator() {
    return creator;
    }
    public ExampleController() {
        log.info("----ExampleController无参构造方法被执行");
    }
}

2、定义ExampleService类,以便作为引用属性注入到ExampleController中;

@Service
@Slf4j
public class ExampleService {
    public ExampleService() {
        log.info("----ExampleService无参构造方法被执行");
    }
    public void test(){
        System.out.println("test");
    }
}

3、定义InstantiationAwareBeanPostProcessor接口的实现类示例类MyInstantiationAwareBeanPostProcessor,并重写InstantiationAwareBeanPostProcessor接口的三个扩展方法和继承于BeanPostProcessor接口的两个扩展方法;

@Component
@Slf4j
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @SneakyThrows
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("exampleController")) {
            log.info("----postProcessBeforeInstantiation被执行:" + beanName);
            return null;
        }
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("exampleController")) {
            log.info("----postProcessAfterInstantiation被执行:" + beanName);
        }
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (beanName.equals("exampleController")) {
           log.info("----postProcessProperties被执行:" + beanName);
            MutablePropertyValues  mutablePropertyValues=new MutablePropertyValues();
            mutablePropertyValues.addPropertyValue("creator","fanfu");
            pvs=mutablePropertyValues;
        }
        return pvs;
    }
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("exampleController")) {
            log.info("----postProcessBeforeInitialization---" + beanName);
            //如果特定的bean实例化完成后,还未执行InitializingBean.afterPropertiesSet()方法之前,有一些其他操作,可以在这里实现
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("exampleController")) {
            log.info("----postProcessAfterInitialization---" + beanName);
            //如果特定的bean实例化完成,InitializingBean.afterPropertiesSet()方法执行后,有一些其他操作,可以在这里实现
        }
        return bean;
    }
}

4、编写单元测试验证InstantiationAwareBeanPostProcessor接口的功能特性;

@Test
public void test3(){
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");
    ExampleController bean = context.getBean(ExampleController.class);
    Assert.isTrue("fanfu".equals(bean.getCreator()),"属性替换失败");
    log.info("----"+bean.getCreator());

}

执行结果如下:

工作原理

注册时机

1、因为InstantiationAwareBeanPostProcessor接口继承于BeanPostProcessor接口,所以InstantiationAwareBeanPostProcessor接口的实现类的注册时机和BeanPostProcessor是一致的,因此很快就找到了InstantiationAwareBeanPostProcessor接口的实现类的注册入口,即org.springframework.context.support.AbstractApplicationContext#refresh--->registerBeanPostProcessors;

2、进入到AbstractApplicationContext#registerBeanPostProcessors方法内,会发现这段代码很干净,即依赖于PostProcessorRegistrationDelegate类的registerBeanPostProcessors()方法;

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

3、进入到PostProcessorRegistrationDelegate类的registerBeanPostProcessors()方法又是另一番洞天:大致是可以分为四步,第一步,获取所有实现BeanPostProcessor接口的实现类的名称,实现方式示例中的MyBeanPostProcessors就在其中;

第二步,提前注册BeanPostProcessorChecker,主要用途是用于Bean创建过程中的日志信息打印记录;

第三步,就是把所有的BeanPostProcessor接口的实现类,按照是否实现PriorityOrdered接口、是否实现Ordered接口、其他,分为三组;

最后一步内容很长,不过很简单,即按第二步分成的三类,依次注册,具体的顺序是:1:实现PriorityOrdered接口BeanPostProcessor接口的实现类、2:实现实现Ordered接口BeanPostProcessor接口的实现类、3:其他的BeanPostProcessor接口的实现类,其中MyInstantiationAwareBeanPostProcessor属于第三类;

总结,InstantiationAwareBeanPostProcessor接口的实现类的注册,其本质是把接口的实现类注入到Spring容器的一个集合里存起来,具体的注册逻辑在PostProcessorRegistrationDelegate#registerBeanPostProcessors()。

执行时机

从实现方式的示例的单元测试执行结果可以看出,InstantiationAwareBeanPostProcessor接口继承于BeanPostProcessor的postProcessBeforeInitialization()和postProcessAfterInitialization()扩展点的执行时机是在Bean(ExampleController)实例化、属性注入完成后触发执行的,其具体的工作原理可以参考Springboot的扩展点之BeanPostProcessor,下面就InstantiationAwareBeanPostProcessor接口的另外三个扩展点的执行时机进行分析。

1、从实现方式示例中,可知InstantiationAwareBeanPostProcessor的第一个扩展点postProcessBeforeInstantiation()是在Eean实例化前触发执行的,所以寻找其执行时机的第一步就要找到Bean实例化的入口,通过debug很快找到了这个位置 ,AbstractApplicationContext#refresh--->finishBeanFactoryInitialization();

2、进入到finishBeanFactoryInitialization()方法,会发现ExampleController这个Bean的实例化是在

DefaultListableBeanFactory#preInstantiateSingletons方法;

3、这里实际被调用的是DefaultListableBeanFactory#preInstantiateSingletons方法,进入到这个方法内,又调用了AbstractBeanFactory#getBean(),从这才算真正Bean的获取或创建的入品口口。这里大致介绍一下getBean()的逻辑:当获取某一个bean时,先查询缓存确定是否存在,若存在,则直接返回,若不存在,则开始创建Bean,若Bean内依赖了另外一个Bean,则是上述过程的一个递归。

4、进入到AbstractBeanFactory#getBean方法,发现又调用了AbstractBeanFactory#doGetBean

-->DefaultSingletonBeanRegistry#getSingleton()

-->AbstractAutowireCapableBeanFactory#createBean();

这里需要注意一下,用到了lambda表达式,即把下面的函数式接口的具体实现作为DefaultSingletonBeanRegistry#getSingleton()的第二个形参singletonFactory

传进去,在DefaultSingletonBeanRegistry#getSingleton()内,执行到singletonFactory.getObject()时触发下面lambda表达式内的createBean();(lambda表达式的应用在Spirng的源码中应用很广泛,所以对这块不太熟悉的小伙伴可以着重研究一下)

() -> {
   try {
      return createBean(beanName, mbd, args);
   }
   catch (BeansException ex) {
      destroySingleton(beanName);
      throw ex;
   }
}

postProcessBeforeInstantiation()

5、源码分析本身并不难,难的是需要耐心,分析这么多了,实际上还未走到进入到InstantiationAwareBeanPostProcessor的扩展点,但是已经很接近了。进入到AbstractAutowireCapableBeanFactory#createBean()

--->AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation(),定睛一看第一个扩展点到了,具体且看下面的注释内容:

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
   Object bean = null;
   if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
      // 检查bean是不是合成的Bean,
      //InstantiationAwareBeanPostProcessor接口的实现类是否己注册
      if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
           //查询出目标Bean的Class实例
           Class<?> targetType = determineTargetType(beanName, mbd);
         if (targetType != null) {
             //开始执行第一个扩展方法postProcessBeforeInstantiation
            bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
            if (bean != null) {
               bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
            }
         }
      }
      mbd.beforeInstantiationResolved = (bean != null);
   }
   return bean;
}

具体的扩展点postProcessBeforeInstantiation()的执行也是朴实无华:查询出所有接口的实现类,然后循环执行;只一需要注意的是扩展点处的返回值处理,这个时候目标Bean(ExampleController)未实例化,这时可以自定义一个Bean对象作为返回值把目标Bean替换掉,那么后续的postProcessBeforeInitialization扩展点还会触发执行,而其他扩展点将会跳过不再执行;什么意思呢?简单点理解就是正常情况下Spring管理目标Bean会一步一步的实例化、属性注入、初始化,但你想要实现点个性化的东西,Spring就把这个Bean实例化及以后的权利交给你了。

protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof InstantiationAwareBeanPostProcessor) {
         InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
         Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
         if (result != null) {
            return result;
         }
      }
   }
   return null;
}

postProcessAfterInstantiation()

6、从AbstractAutowireCapableBeanFactory#createBean()

--->AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation()完成第一个扩展点的执行,继续往下走就是AbstractAutowireCapableBeanFactory#doCreateBean了,要找到第二个、第三个扩展点都在这里面;

7、进入到AbstractAutowireCapableBeanFactory#doCreateBean方法,如下图,instanceWrapper.getWrappedInstance()完成了目标Bean(ExampleController)的示例化,但是属性exampleService还未注入;

继续往下走,调用populateBean(),完成对已实例化的目标Bean(ExampleController)的属性注入;如图进入到populateBean()就开始了第二个扩展点postProcessAfterInstantiation()方法的执行;这里要注意一下返回值的类型为布尔类型:如果返回true,则会继续执行第三个扩展点;如果返回fase,则第三个扩展点不会执行,直接return跳出了当前方法;

postProcessProperties()

还是在AbstractAutowireCapableBeanFactory#populateBean方法中,第二个扩展点执行过且返回值为true,接着往下就会执行到第三个扩展点postProcessProperties();

第三个扩展点要着重说一下,第三个扩展触发执行的时候Bean已经实例化,但是未完成属性注入,但是这里可以在里替换掉即将注入的属性。在实现方式的示例代码ExampleController类中,定义了一个String类型的属性creator,默认值为"gaox",在MyInstantiationAwareBeanPostProcessor#postProcessProperties中,把“gaox”替换为“fanfu”,再看单元测试的执行结果,默认值“gaox”被替换成了“fanfu”;

应用场景

其实了解了InstantiationAwareBeanPostProcessorr的功能特性、实现方式和工作原理,在遇到类似的业务需求的时候都可以应用这些扩展点,这里举一个平时开发中经用到但是应该没有注意到的一个Spring内部经典实现AutowiredAnnotationBeanPostProcessor。

通过的UML类图,可以看到AutowiredAnnotationBeanPostProcessor继承于InstantiationAwareBeanPostProcessorAdapter,而InstantiationAwareBeanPostProcessorAdapter又实现了SmartInstantiationAwareBeanPostProcessor接口,SmartInstantiationAwareBeanPostProcessor接口又继承了InstantiationAwareBeanPostProcessor。

AutowiredAnnotationBeanPostProcessor又实现了InstantiationAwareBeanPostProcessor的postProcessProperties();实现方式中示例代码ExampleController类的exampleService属性用@Autowired注解标记后,实际的注入实现逻辑就在AutowiredAnnotationBeanPostProcessor的postProcessProperties()内,有兴趣的小伙伴可以继续深入下去探寻一翻。

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
   InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
   try {
      metadata.inject(bean, beanName, pvs);
   }
   catch (BeanCreationException ex) {
      throw ex;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
   }
   return pvs;
}

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

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

相关文章

vue-cli3创建Vue项目

文章目录前言一、使用vue-cli3创建项目1.检查当前vue的版本2.下载并安装Vue-cli33.使用命令行创建项目二、关于配置前言 本文讲解了如何使用vue-cli3创建属于自己的Vue项目&#xff0c;如果本文对你有所帮助请三连支持博主&#xff0c;你的支持是我更新的动力。 下面案例可供…

【C++】类与对象(上)

文章目录一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装①访问限定符②封装五、类的作用域六、类的实例化七、类对象模型①如何计算类对象大小②类对象的存储方式③结构体中内存对齐规则八、this指针①this指针的引出②this指针的特性一、面…

XCP实战系列介绍07-使用ASAP2 Editor生成A2l文件详解

本文框架 1.概述2. A2L文件编辑及生成2.1 新建项目工程2.2 加载elf文件2.3 A2L文件的项目属性配置2.4 DAQ事件的设定2.5 添加观察量2.6 添加标定量2.7 编译生成A2l1.概述 在前面一篇文章《看了就会的XCP协议介绍》中详细介绍了XCP的协议,在《XCP实战系列介绍01-测量与标定底层…

JavaScript 类继承

JavaScript 类继承 JavaScript 类继承使用 extends 关键字。 继承允许我们依据另一个类来定义一个类&#xff0c;这使得创建和维护一个应用程序变得更容易。 super() 方法用于调用父类的构造函数。 当创建一个类时&#xff0c;您不需要重新编写新的数据成员和成员函数&…

synchronized和ReentrantLock之间的区别

synchronized和ReentrantLock的区别 synchronized是一个关键字&#xff0c;是JVM内部实现的&#xff1b;ReentrantLock是标准库的一个类&#xff0c;是JVM外部基于Java实现的。synchronized在申请锁失败时会死等&#xff1b;ReentrantLock可以通过tryLock的方式等待一段时间就…

偏微分方程约束下的优化控制问题(PDE-constrained optimal control problems)

优化控制问题介绍 优化控制问题的数学形式 {min⁡(y(x),u(x))∈YUJ(y(x),u(x)),s.t. F(y(x),u(x))0in Ω,and u(x)∈Uad,\left\{\begin{aligned} &\min _{(y(\mathbf{x}), u(\mathbf{x})) \in Y \times U} J(y(\mathbf{x}), u(\mathbf{x}) ),\\ &\text { s.t. } \ \…

Python列表的元素比较

在用python处理多个列表元素时&#xff0c;需要处理的信息一般比较多且杂。这时运用Python列表元素比较的方法&#xff0c;就能快速处理列表信息&#xff0c;能更轻松访问每个元素。1 问题如何运用Python列表的元素比较解决问题。2 方法方法一 for循环&#xff0c;此情况是list…

Spring 事务【隔离级别与传播机制】

Spring 事务【隔离级别与传播机制】&#x1f34e;一.事务隔离级别&#x1f352;1.1 事务特性回顾&#x1f352;1.2 事务的隔离级别(5种)&#x1f352;1.3 事务隔离级别的设置&#x1f34e;二.Spring 事务传播机制&#x1f352;2.1 Spring 事务传播机制的作用&#x1f352;2.2 事…

描述性统计图表——箱线图

文章目录1、分位数1.1、分位数的概念1.2、特殊分位数1.3、分位数的计算2、箱线图2.1、概念2.2、作图步骤2.3、箱线图的作用2.3.1、定位异常值2.3.2、偏态和尾重2.3.3、数据的形状1、分位数 1.1、分位数的概念 分位数&#xff08;Quantile&#xff09;&#xff0c;亦称分位点&a…

红黑树简析

一. 概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长出俩倍&#xff0c;因而是…

java 自定义json解析注解 复杂json解析

java 自定义json解析注解 复杂json解析 工具类 目录java 自定义json解析注解 复杂json解析 工具类1.背景2、需求-各式各样的json一、一星难度json【json对象中不分层】二、二星难度json【json对象中出现层级】三、三星难度json【json对象中存在数组】四、四星难度json【json对象…

【c++之于c的优化 - 下】

前言 一、inline 概念 以inline修饰的函数叫做内联函数&#xff0c;编译时C编译器会在调用内联函数的地方展开&#xff0c;没有函数调用建立栈帧的开销&#xff0c;内联函数提升程序运行的效率。 如果在上述函数前增加inline关键字将其改成内联函数&#xff0c;在编译期间编译…

契约锁与200多家软件厂商实现集成应用,实现更多业务电子签需求

在中大型组织的数字化建设中&#xff0c;电子签章需求紧紧围绕业务展开&#xff0c;实现电子签章与管理软件集成应用已经成为普遍需求。 契约锁数字可信产品拥有200多家管理软件厂商集成对接经验&#xff0c;覆盖ERP、OA、专项业务系统、移动APP应用、低代码平台、BPM流程、小程…

Vue - 使用Lodash实现防抖和节流

GitHub Demo 地址 在线预览 Lodash 官网 参考&#xff1a; Vue 防抖节流 详细介绍 vue 优雅的实现防抖和节流 防抖、节流的介绍 js防抖和节流的实现原理及应用场景 js防抖节流应用场景及写法 JS中的防抖 什么的防抖和节流&#xff1f; 函数节流&#xff08;throttle&#x…

论文翻译:2022_Phase-Aware Deep Speech Enhancement: It’s All About The Frame Length

摘要 虽然相位感知语音处理近年来受到越来越多的关注&#xff0c;但大多数帧长约为32 ms的窄带STFT方法显示出相位对整体性能的影响相当有限。与此同时&#xff0c;现代基于深度神经网络(DNN)的方法&#xff0c;如Conv-TasNet&#xff0c;隐式修改幅度和相位&#xff0c;在非常…

CUDA虚拟内存管理

CUDA中的虚拟内存管理 文章目录CUDA中的虚拟内存管理1. Introduction2. Query for support3. Allocating Physical Memory3.1. Shareable Memory Allocations3.2. Memory Type3.2.1. Compressible Memory4. Reserving a Virtual Address Range5. Virtual Aliasing Support6. Ma…

自动化测试实战篇(6)jmeter实现脚本录制,抓取接口信息

Jmeter中脚本录制&#xff0c;是一个非常方便找到接口内容的一种工具&#xff0c;不用想fiddler抓包定位接口信息速度不够快 设置代理服务器 这里以谷歌浏览器为例子 打开您的计算机的代理设置 把代理服务器打开这里就以127.0.0.1和8080端口为例子&#xff0c;这个需要记住…

ChatGPT背后的经济账

ChatGPT能否取代Google、百度这样的传统搜索引擎&#xff1f;为什么中国不能很快做出ChatGPT&#xff1f;当前&#xff0c;对这些问题的探讨大多囿于大型语言模型&#xff08;LLM&#xff09;的技术可行性&#xff0c;忽略或者非常粗糙地估计了实现这些目标背后的经济成本&…

((蓝桥杯 刷题全集)【备战(蓝桥杯)算法竞赛-第4天(搜索与图论-下 专题)】( 从头开始重新做题,记录备战竞赛路上的每一道题 )距离蓝桥杯还有63天

&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6; 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&a…

差分隐私学习笔记

2021网络空间安全西湖学术论坛线上报告中介绍了差分隐私过去发展&#xff0c;目前现状以及未来研究方向。博主对这个报告进行了介绍与总结。总结中提到学习差分隐私最重要的环节是&#xff1a; 了解差分隐私的基本机制&#xff1a;拉普拉斯机制、指数机制和高斯机制差分隐私的组…