对Spring源码的学习:二

目录

SpringBean实例化流程

Spring的后处理器

Bean工厂后处理器


SpringBean实例化流程

Spring容器在进行初始化时,会将xml配置的<bean>的信息封装成一个BeanDefinition对象,所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,Spring框架在对该Map进行遍历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为sinaletonObiects的Map集合中,当调用getBean方法时则最终从该Map集合中取出Bean实例对象返回

对该方法进行断点观察

因此我们可以总结如下流程图

Spring的后处理器

Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:

  • BeanFactoryPostProcessor:Bean工厂后外理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行。
  • BeanPostProcessor:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。

Bean工厂后处理器

我们需要实现BeanFactoryPostProcessor接口,然后重写其方法,该方法中的参数实际上就是Spring容器,我们可以通过该容器获取BeanDefinition信息,然后对这些信息进行修改,在下面代码中,我们修改了UserService的全路径,这会导致在实例化Bean时通过反射拿到的实际上时UserDao类。

public class MyBeanFactoryProcessor implements BeanFactoryPostProcessor {
    @Override
    //在Map加载完成后执行
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //beanFactory无法一次获取全部的Map信息,只能通过key获取
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
        System.out.println("当前的Bean的全路径"+beanDefinition.getBeanClassName());
        beanDefinition.setBeanClassName("com.zmt.dao.impl.UserDaoImpl");
    }
}

配置完成不代表生效,我们需要将该类交给Spring管理,然后由Spring来回调该方法,因此需要修改xml文件

<beans>
  <bean id="userService" class="com.zmt.service.impl.UserServiceImpl"></bean>

  <bean id="userDao" class="com.zmt.dao.impl.UserDaoImpl"></bean>
  <!-- 添加为Bean对象 -->
  <bean class="com.zmt.processor.MyBeanFactoryProcessor"></bean>
</beans>

测试代码如下

public class ApplicationContextTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Object userService =  context.getBean("userService");
        System.out.println(userService);
    }
}

运行结果如下

当前的Bean的全路径com.zmt.service.impl.UserServiceImpl

com.zmt.dao.impl.UserDaoImpl@5649fd9b

如果我们需要向Map中添加BeanDefinition数据,在创建完BeanDefinition之后无法将其注册到Map中,需要实现该接口的子接口BeanDefinitionRegistryPostProcessor。下面我们创建一个PersonDao接口以及实现类,但是不在xml文件中定义,通过Bean工厂后处理器添加在Map当中,测试能否getBean时获取到对应类

<beans>
  <bean id="userService" class="com.zmt.service.impl.UserServiceImpl"></bean>

  <bean id="userDao" class="com.zmt.dao.impl.UserDaoImpl"></bean>

  <bean class="com.zmt.processor.MyBeanFactoryRegistryProcessor"></bean>
</beans>
public class MyBeanFactoryRegistryProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        //设置全路径
        beanDefinition.setBeanClassName("com.zmt.dao.impl.PersonDaoImpl");
        //添加到Map中
        beanDefinitionRegistry.registerBeanDefinition("personDao",beanDefinition);
    }

    //该方式是父接口中的方法,我们可以不编写业务代码
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

测试代码

public class ApplicationContextTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        PersonDaoImpl personDao = context.getBean(PersonDaoImpl.class);
        System.out.println(personDao);
    }
}
/**
* 运行结果如下
* com.zmt.dao.impl.PersonDaoImpl@64bfbc86
*/

此时,我们可能存在一个疑惑,那就是如果配置了多个Bean工厂后处理器,那么执行顺序应该是什么?接下来我们对其进行测试

public class MyBeanFactoryProcessor implements BeanFactoryPostProcessor {
    @Override
    //在Map加载完成后执行
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //beanFactory无法一次获取全部的Map信息,只能通过key获取
        System.out.println("执行MyBeanFactoryProcessor中的postProcessBeanFactory方法");
    }
}

public class MyBeanFactoryRegistryProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        System.out.println("执行MyBeanFactoryRegistryProcessor中的postProcessBeanDefinitionRegistry方法");
    }


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("执行MyBeanFactoryRegistryProcessor中的postProcessBeanFactory方法");

    }
}

执行结果如下

执行MyBeanFactoryRegistryProcessor中的postProcessBeanDefinitionRegistry方法

执行MyBeanFactoryRegistryProcessor中的postProcessBeanFactory方法

执行MyBeanFactoryProcessor中的postProcessBeanFactory方法

由此我们可以得出结论,优先执行BeanFactoryProcessor的子类独有方法,再执行子类中继承来的父类方法,最后执行BeanFactory中的方法。

因此,我们可以总结Bean工厂后处理器的执行时机在Spring启动流程中如下

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023.12.6 关于 Spring Boot 事务的基本概念

目录 事务基本概念 前置准备 Spring Boot 事务使用 编程式事务 声明式事务 Transactional 注解参数说明 Transational 对异常的处理 解决方案一 解决方案二 Transactional 的工作原理 面试题 Spring Boot 事务失效的场景有那些&#xff1f; 事务基本概念 事务指一…

基于深度学习的遥感图像变化差异可视化系统

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义 遥感图像变化差异可视化是遥感图像处理和分析的重要研究领域之一。随着遥感技术的快速发展和遥感数据的广泛应用&#xff0c;遥感图像的获取和处理变得越来越容易…

Javaweb | Servlet编程

目录: 1.认识Servlet2.编写Servlet3.Servlet的运行机制4.Servlet的生命周期4.1 Servlet生命周期图init()方法doGet()doPost()service()destroy()方法 5.解决“控制台”打印中文乱码问题6.Servlet 和 JSP内置对象 (常用对象)获得out对象获得request 和 response对象获得session对…

KeePass开源密码管理器

KeePass开源密码管理器 KeePass 是一款免费的开源密码管理器&#xff0c;KeePass 将密码存储为一个数据库&#xff0c;而这个数据库由一个主密码或密码文件锁住&#xff0c;也就是说我们只需要记住一个主密码&#xff0c;或使用一个密码文件&#xff0c;就可以解开这个数据库&a…

每日一题:LeetCode-11.盛水最多的容器

每日一题系列&#xff08;day 13&#xff09; 前言&#xff1a; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f50e…

服务器安装JDK17 版本显示JDK8

服务器之前安装的是JDK8&#xff0c;后面升级JDK17后&#xff0c;发现执行 java -vsrsion 显示的是此时我的环境变量已经换成了JAVA17的路径 输入&#xff1a; vim /etc/profile 解决办法&#xff1a; 1.更新自己环境变量 bash export JAVA_HOME/usr/local/jdk-17.0.7 …

【科普】什么是电子印章? PS抠的印章能用吗?

各类扣章教程一搜一大堆&#xff0c;说明大家对于电子印章使用需求很高。不过要谨记&#xff0c;不要随便抠印章用于公文、证明书、合同协议、收据发票等电子文件&#xff0c;否则可能会吃牢饭。 单是一张电子化的图片是不具备合法性的。那有的人就要问了&#xff0c;我见到的…

读书笔记-《数据结构与算法》-摘要4[插入排序]

插入排序 核心&#xff1a;通过构建有序序列&#xff0c;对于未排序序列&#xff0c;在已排序序列中从后向前扫描(对于单向链表则只能从前往后遍历)&#xff0c;找到相应位置并插入。实现上通常使用in-place排序(需用到O(1)的额外空间) 从第一个元素开始&#xff0c;该元素可…

【微服务】springboot整合quartz使用详解

目录 一、前言 二、quartz介绍 2.1 quartz概述 2.2 quartz优缺点 2.3 quartz核心概念 2.3.1 Scheduler 2.3.2 Trigger 2.3.3 Job 2.3.4 JobDetail 2.4 Quartz作业存储类型 2.5 适用场景 三、Cron表达式 3.1 Cron表达式语法 3.2 Cron表达式各元素说明 3.3 Cron表达…

封装PoiExcelUtils

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 为什么要封装PoiExcelU…

Openfire CVE-2023-32315(metasploit版)

step1&#xff1a;用docker搭建环境 Step2&#xff1a;docker查看映射端口 Step3&#xff1a;打开mysql和apache2&#xff0c;访问特定端口&#xff0c;然后靶标应用。 Step4&#xff1a;用metasploit进行攻击&#xff1a; 首先&#xff0c;打开metasploit&#xff0c;然…

Java (JDK 21) 调用 OpenCV (4.8.0)

Java 调用 OpenCV 一.OpenCV 下载和安装二.创建 Java Maven 项目三.其他测试 一.OpenCV 下载和安装 Open CV 官网 可以下载编译好的包&#xff0c;也可以下载源码自行编译 双击安装 opencv-4.8.0-windows.exe 默认为当前目录 安装即解压缩 根据系统位数选择 将 x64 目录下 op…

数据结构 | 查漏补缺之求叶子结点,分离链接法、最小生成树、DFS、BFS

求叶子结点的个数 参考博文&#xff1a; 树中的叶子结点的个数 计算方法_求树的叶子节点个数-CSDN博客 分离链接法 参考博文 数据结构和算法——哈希查找冲突处理方法&#xff08;开放地址法-线性探测、平方探测、双散列探测、再散列&#xff0c;分离链接法&#xff09;_线性…

Linux存储软件栈到底有多复杂,存储软件栈全景概览

从今天开始&#xff0c;我们将介绍一下Linux的存储软件栈&#xff0c;也称为IO栈。今天我们先从整体上了解一下Linux的整个IO栈&#xff0c;后续再深入介绍每个组件。我们很难说清楚Linux的存储软件栈到底有多复杂&#xff0c;但thomas-krenn的一张图可能可以给大家一个比较直观…

堆的相关时间复杂度计算(C语言)

目录 前言 建堆的时间复杂度 向上调整建堆的时间复杂度 向下调整建堆的时间复杂度 维护堆的时间复杂度 top K问题的时间复杂度 前言 在前面的三篇文章中我们成功的实现了堆排序的升降序以及基于堆的top K问题&#xff0c;现在我们来解决它们的时间复杂度问题 建堆的时间…

查看Linux的Ubuntu的版本

我的Ubuntu版本是 Jammy x86_64&#xff0c;即 Ubuntu 22.04.3 LTS&#xff0c;代号为"Jammy Jellyfish"&#xff0c;架构是 x86_64&#xff08;64位&#xff09;。

UEC++ 探索虚幻5笔记 day11

虚幻5.2.1探索 项目目录探索 C工程一定不能是中文路径&#xff0c;中文项目名&#xff0c;最好全部不要用中文&#xff0c;蓝图项目可以是中文浅浅创建一个空项目&#xff0c;讲解一下之前UE4没有讲解的项目目录文件的分布组成 .vs&#xff1a;文件夹一般是项目编译缓存文件夹…

linux安装mysql5.7(一遍过)

之前安装的时候遇到了很多问题&#xff0c;浪费了一些时间。整理出这份教程&#xff0c;照着做基本一遍过。 这是安装包: 链接&#xff1a;https://pan.baidu.com/s/1gBuQBjA4R5qRYZKPKN3uXw?pwd1nuz 1.下载安装包&#xff0c;上传到linux。我这里就放到downloads目录下面…

游戏盾的防御原理以及为什么程序类型更适合接入游戏盾。

游戏盾是一种专门用于游戏服务器的安全防护服务&#xff0c;旨在抵御各种网络攻击。它的原理主要包括以下几个方面&#xff1a; 流量清洗和过滤&#xff1a;游戏盾会对进入游戏服务器的流量进行实时监测、分析和过滤。它通过识别恶意流量和攻击流量&#xff0c;过滤掉其中的攻击…

多种混沌映射初始化种群MATLAB代码(开源代码)

五种混沌映射改进鲸鱼算法&#xff08;自行切换&#xff09;&#xff1a; clc clear close allSearchAgents_no30; % Number of search agents %种群数量Function_nameF1; % Name of the test function that can be from F1 to F23 (Table 1,2,3 in the paper) %使用方程的名…