Spring Bean生命周期之(1)BeanDefinition

news2024/11/26 2:51:22

文章目录

  • 1 BeanDefinition
    • 1.1 Spring Bean 读取解析配置信息
      • 1.1.1 XML 配置的处理主要使用的事例
      • 1.1.2 注解 BeanDefinition 解析示例
    • 1.2 spring bean 注册阶段
    • 1.3 Spring BeanDefinition 合并阶段
      • 1.3.1 BeanDefinition 合并示例
      • 1.3.2 源码分析

1 BeanDefinition

spring bean创建过程 依赖 BeanDefinition 中的信息处理bean的生产。BeanDefinitionSpring Framework 中定义 Bean 的配置元信息接口
在这里插入图片描述
在处理配置文件生成BeanDefinition主要经过:Spring Bean 读取解析配置信息、spring bean 注册阶段、Spring BeanDefinition 合并阶段

1.1 Spring Bean 读取解析配置信息

spring bean的配置信息分为:

  • 面向资源
    • XML配置的处理主要使用 :XmlBeanDefinitionReader
    • Properties 资源配置:PropertiesBeanDefinitionReader
  • 面向注解
    • 面向注解 BeanDefinition 解析: AnnotatedBeanDefinitionReader

1.1.1 XML 配置的处理主要使用的事例

这里主要基于xml中的构造方法处理:

public class UserHolder {
 
    private User user;
 
    public UserHolder() {
    }
 
    public UserHolder(User user) {
        this.user = user;
    }
 
    public User getUser() {
        return user;
    }
 
    public void setUser(User user) {
        this.user = user;
    }
 
    @Override
    public String toString() {
        return "UserHolder{" +
                "user=" + user +
                '}';
    }
}
/**
 * 基于 XML 资源的依赖 Constructor 注入示例
 */
public class XmlDependencyConstructorInjectionDemo {
 
    public static void main(String[] args) {
 
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
 
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
 
        String xmlResourcePath = "classpath:/META-INF/dependency-constructor-injection.xml";
        // 加载 XML 资源,解析并且生成 BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
        // 依赖查找并且创建 Bean
        UserHolder userHolder = beanFactory.getBean(UserHolder.class);
        System.out.println(userHolder);
 
    }
}

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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
 
    <bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User">
        <property name="id" value="1"/>
        <property name="name" value="测试"/>
        <property name="city" value="test"/>
        <property name="workCities" value="BEIJING,HANGZHOU"/>
        <property name="lifeCities">
            <list>
                <value>BEIJING</value>
                <value>SHANGHAI</value>
            </list>
        </property>
        <property name="configFileLocation" value="classpath:/META-INF/user-config.properties"/>
    </bean>
 
    <bean id="superUser" class="org.geekbang.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user"
          primary="true">
        <property name="address" value="杭州"/>
    </bean>
 
 
    <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder">
        <constructor-arg name="user" ref="superUser" />
    </bean>
 
</beans>

1.1.2 注解 BeanDefinition 解析示例

使用AnnotatedBeanDefinitionReader 来处理注解 的类生成BeanDefinition 并注入到容器中

public class AnnotatedBeanDefinitionParsingDemo {
 
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 基于 Java 注解的 AnnotatedBeanDefinitionReader 的实现
        AnnotatedBeanDefinitionReader beanDefinitionReader = new AnnotatedBeanDefinitionReader(beanFactory);
        int beanDefinitionCountBefore = beanFactory.getBeanDefinitionCount();
        // 注册当前类(非 @Component class)
        beanDefinitionReader.register(AnnotatedBeanDefinitionParsingDemo.class);
        int beanDefinitionCountAfter = beanFactory.getBeanDefinitionCount();
        int beanDefinitionCount = beanDefinitionCountAfter - beanDefinitionCountBefore;
        System.out.println("已增加加载 BeanDefinition 数量:" + beanDefinitionCount);
        // 普通的 Class 作为 Component 注册到 Spring IoC 容器后,通常 Bean 名称为 annotatedBeanDefinitionParsingDemo
        // Bean 名称生成来自于 BeanNameGenerator,注解实现 AnnotationBeanNameGenerator
        AnnotatedBeanDefinitionParsingDemo demo = beanFactory.getBean("annotatedBeanDefinitionParsingDemo",
                AnnotatedBeanDefinitionParsingDemo.class);
        System.out.println(demo);
    }
}

1.2 spring bean 注册阶段

在步骤一中生成的 BeanDefinition 需要注入到容器中
BeanDefinition 注册接口:BeanDefinitionRegistry
注册BeanDefinition其实就是把 BeanDefinition信息放入map中

注册到一个map中

private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);

1.3 Spring BeanDefinition 合并阶段

在合并阶段主要解决Bean继承时子类合并父类公共属性问题:

可以把 父BeanDefinition的信息合并到子BeanDefinition中;合并中的BeanDefinition主要依靠下面三个子类实现的

  • ChildBeanDefinition:要指定父类。实例化的时候一定要是 父BeanDefinition,永远只能作为一个子BeanDefinition
  • RootBeanDefinition: spring 2.5的首选 一般表示。 作为父beanDefinition出现也可以作为普遍的bd 但是不能设置父beanDefinition即 不能作为子bd。
  • GenericBeanDefinition:spring 2.5 以后出现的 常用的 可以 替换 ChildBeanDefinition 但是不能替换 RootBeanDefinition。可以完成RootBeanDefinitionChildBeanDefinition 两种的功能。

合并过程一般是把 GenericBeanDefinition的处理合并成 RootBeanDefinition

1.3.1 BeanDefinition 合并示例

public class MergedBeanDefinitionDemo {
 
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 基于 XML 资源 BeanDefinitionReader 实现
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        String location = "META-INF/dependency-lookup-context.xml";
        // 基于 ClassPath 加载 XML 资源
        Resource resource = new ClassPathResource(location);
        // 指定字符编码 UTF-8
        EncodedResource encodedResource = new EncodedResource(resource, "UTF-8");
        int beanNumbers = beanDefinitionReader.loadBeanDefinitions(encodedResource);
        System.out.println("已加载 BeanDefinition 数量:" + beanNumbers);
        // 通过 Bean Id 和类型进行依赖查找
        User user = beanFactory.getBean("user", User.class);
        System.out.println(user);
 
        User superUser = beanFactory.getBean("superUser", User.class);
        System.out.println(superUser);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
 
<!--    <context:annotation-config/>-->
 
<!--    <context:component-scan base-package="org.acme" />-->
 
    <!-- Root BeanDefinition 不需要合并,不存在 parent -->
    <!-- 普通 beanDefinition GenericBeanDefinition -->
    <!-- 经过合并后 GenericBeanDefinition 变成 RootBeanDefinition -->
    <bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User">
        <property name="id" value="1"/>
        <property name="name" value="小马哥"/>
        <property name="city" value="HANGZHOU"/>
        <property name="workCities" value="BEIJING,HANGZHOU"/>
        <property name="lifeCities">
            <list>
                <value>BEIJING</value>
                <value>SHANGHAI</value>
            </list>
        </property>
        <property name="configFileLocation" value="classpath:/META-INF/user-config.properties"/>
    </bean>
 
    <!-- 普通 beanDefinition GenericBeanDefinition -->
    <!-- 合并后 GenericBeanDefinition 变成 RootBeanDefinition,并且覆盖 parent 相关配置-->
    <!-- primary = true , 增加了一个 address 属性 -->
    <bean id="superUser" class="org.geekbang.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user"
          primary="true">
        <property name="address" value="杭州"/>
    </bean>
 
    <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
        <property name="targetBeanName" value="user"/>
    </bean>
 
</beans>

执行结果。我们可以看到 SuperUser中包括的 其父类 User的相关属性

已加载 BeanDefinition 数量:3
User{id=1, name='小马哥', city=HANGZHOU, workCities=[BEIJING, HANGZHOU], lifeCities=[BEIJING, SHANGHAI], configFileLocation=class path resource [META-INF/user-config.properties], company=null, context=null, contextAsText='null', beanName='user'}
SuperUser{address='杭州'} User{id=1, name='小马哥', city=HANGZHOU, workCities=[BEIJING, HANGZHOU], lifeCities=[BEIJING, SHANGHAI], configFileLocation=class path resource [META-INF/user-config.properties], company=null, context=null, contextAsText='null', beanName='superUser'}

1.3.2 源码分析

合并 BeanDefinition 操作在 AbstractBeanFactory中发doGetBean()方法中的
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

protected RootBeanDefinition getMergedBeanDefinition(
			String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
			throws BeanDefinitionStoreException {
 
		synchronized (this.mergedBeanDefinitions) {
			RootBeanDefinition mbd = null;
 
			// Check with full lock now in order to enforce the same merged instance.
			if (containingBd == null) {
				mbd = this.mergedBeanDefinitions.get(beanName);
			}
 
			if (mbd == null) {
				if (bd.getParentName() == null) {
					// Use copy of given root bean definition.
					if (bd instanceof RootBeanDefinition) {
						mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
					}
					else {
						mbd = new RootBeanDefinition(bd);
					}
				}
				else {
					// Child bean definition: needs to be merged with parent.
					BeanDefinition pbd;
					try {
						String parentBeanName = transformedBeanName(bd.getParentName());
						if (!beanName.equals(parentBeanName)) {
							//TODO 这里这次在去合并父类的 父类 这里使用递归的处理
							pbd = getMergedBeanDefinition(parentBeanName);
						}
						else {
							BeanFactory parent = getParentBeanFactory();
							if (parent instanceof ConfigurableBeanFactory) {
								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
							else {
								throw new NoSuchBeanDefinitionException(parentBeanName,
										"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
										"': cannot be resolved without an AbstractBeanFactory parent");
							}
						}
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
								"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
					}
					// Deep copy with overridden values.
					//最终的返回对象 合并后的对象
					mbd = new RootBeanDefinition(pbd);
					mbd.overrideFrom(bd);
				}
 
				// Set default singleton scope, if not configured before.
				if (!StringUtils.hasLength(mbd.getScope())) {
					mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
				}
 
				// A bean contained in a non-singleton bean cannot be a singleton itself.
				// Let's correct this on the fly here, since this might be the result of
				// parent-child merging for the outer bean, in which case the original inner bean
				// definition will not have inherited the merged outer bean's singleton status.
				if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
					mbd.setScope(containingBd.getScope());
				}
 
				// Cache the merged bean definition for the time being
				// (it might still get re-merged later on in order to pick up metadata changes)
				if (containingBd == null && isCacheBeanMetadata()) {
					this.mergedBeanDefinitions.put(beanName, mbd);
				}
			}
 
			return mbd;
		}
	}

这一节我们简单的介绍了 spring BeanDefinition的处理过程。

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

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

相关文章

机器学习——线性回归篇

基本概念 什么是回归预测&#xff1f;什么是分类预测&#xff1f; 模型输入变量预测结果应用回归预测实值离散一个连续值域上的任意值预测值的分布情况分类预测实值离散两个或多个分类值将输入变量分类到不同类别 思考一个问题&#xff1a;分类问题是否可以转变为回归问题&am…

百度商业AI技术创新大赛启动-63万元奖金!

随着生成式AI在全球范围的热议&#xff0c;AIGC前沿技术也在快速迭代&#xff0c;正如百度CEO李彦宏所说“人工智能发生了方向性改变&#xff0c;从辨别式AI走向生成式AI&#xff0c;生成式AI会带来极大的效率提升”。而这一领域的发展&#xff0c;将推动AI产品应用深化&#x…

两个震子如何调整发送信号时间差来调整波束

ΔΦ 2π/λdsinθ 这个式子中 d是振子的距离不变 λ是波长可以根据频率知道...所以要控制θ 就要控制两个振子的相位差 也就是控制振子a与振子b的时间差就能控制θ 30mhz的带宽与相位偏差的影响 ΔΦ 2π/λdsinθ 频率900hmz 波长300/9000.333 频率930hmz 波长300/9300…

t-SNE(t-stochastic neighourhood embedding) 数据降维及可视化

文章目录 算法原理示例一示例二 算法原理 t-SNE 的基本思想是将高维数据映射到低维空间&#xff0c;同时保留数据间的局部结构。具体而言&#xff0c;给定一个高维数据集 X { x 1 , … , x n } \mathbf{X}\{\mathbf{x}_1,\dots,\mathbf{x}_n\} X{x1​,…,xn​}&#xff0c;其…

macm1环境下IDEA项目切换jdk版本

macm1环境下IDEA项目切换jdk版本 本文目录 macm1环境下IDEA项目切换jdk版本背景需求下载对应版本jdk配置IDEA中指定项目的JDK版本配置 Project Structure配置 Settings 可能报错同时安装JDK8和JDK17并切换 背景需求 项目基于 springboot3.0.7Nacos2.2.3seata1.6.1 由spring官…

TensorFlow2安装备忘

系统配置 Ubuntu18.04 Anaconda4.9.2 Cuda10.0Cudnn7.6.5 CPU版本 执行pip install tensorflow默认安装的是CPU版本&#xff0c;并且安装后会有较多import依赖问题&#xff0c;部分记录如下&#xff0c;基本都属于"No module named xxxx"。 1.找不到wrapt 通过…

【23】SCI易中期刊推荐——神经网络科学及机器人学(中科院3区)

💖💖>>>加勒比海带,QQ2479200884<<<💖💖 🍀🍀>>>【YOLO魔法搭配&论文投稿咨询】<<<🍀🍀 ✨✨>>>学习交流 | 温澜潮生 | 合作共赢 | 共同进步<<<✨✨ 📚📚>>>人工智能 | 计算机视觉…

面试官:useEffect和useLayoutEffect有什么区别?

您好&#xff0c;如果喜欢我的文章&#xff0c;可以关注我的公众号「量子前端」&#xff0c;将不定期关注推送前端好文~ Effect数据结构 顾名思义&#xff0c;React底层在函数式组件的Fiber节点设计中带入了hooks链表的概念&#xff08;memorizedState&#xff09;&#xff0…

博弈论——巴什博弈(C++)

博弈论&#xff08;C&#xff09; 前言例题&#xff1a;拍卖会题目描述输入输出格式输入格式&#xff1a;输出格式&#xff1a; 输入输出样例输入样例#1&#xff1a;输出样例#1&#xff1a; 例题的解&#xff1a;巴什博奕&#xff08;Bash Game&#xff09;&#xff1a;代码&am…

CEPH部署

//存储类型 块存储 一对一&#xff0c;只能被一个主机挂载使用&#xff0c;数据以块为单位进行存储&#xff0c;典型代表&#xff1a;硬盘 文件存储 一对多&#xff0c;能被多个主机同时挂载使用&#xff0c;数据以文件的形式存储的&#xff08;元数据和实际数据是分开存储…

【30】核心易中期刊推荐——人工智能图像处理

🚀🚀🚀NEW!!!核心易中期刊推荐栏目来啦 ~ 📚🍀 核心期刊在国内的应用范围非常广,核心期刊发表论文是国内很多作者晋升的硬性要求,并且在国内属于顶尖论文发表,具有很高的学术价值。在中文核心目录体系中,权威代表有CSSCI、CSCD和北大核心。其中,中文期刊的数…

javascript基础二十三:说说 JavaScript 中内存泄漏的几种情况

一、是什么 内存泄漏&#xff08;Memory leak&#xff09;是在计算机科学中&#xff0c;由于疏忽或错误造成程序未能释放已经不再使用的内存 并非指内存在物理上的消失&#xff0c;而是应用程序分配某段内存后&#xff0c;由于设计错误&#xff0c;导致在释放该段内存之前就失…

【数学建模】期末样题(2021年真题)

一、&#xff08;10分&#xff09;某乳制品厂计划生产A、B、C三种酸奶。已知生产单位重量的A需要加工设备3小时&#xff0c;原料甲1千克&#xff0c;原料乙0.2千克&#xff1b;生产单位重量的B需要加工设备5小时&#xff0c;原料甲1千克&#xff0c;原料乙0.3千克&#xff1b;生…

若依之权限处理

若依之权限处理 若依前后端不分离版本使用的是shiro进行权限控制&#xff0c;本文主要是对shiro在若依中的使用进行分析。 RBAC权限模型 RBAC是指基于角色的访问控制。其基本思想是&#xff0c;对系统的各种权限不是直接授予具体的用户&#xff0c;而是在用户集合与权限集合…

【论文总结】Drifuzz: Harvesting Bugs in Device Drivers from Golden Seeds

Drifuzz:从金种子中收获设备驱动程序中的漏洞 摘要: 现代计算机中的外围硬件通常被认为是安全的且不带恶意&#xff0c;并且设备驱动程序以信任从硬件输入的方式实现。然而&#xff0c;最近的漏洞攻击&#xff08;如Broadpwn&#xff09;已经证明攻击者可以通过易受攻击的外围…

Map复习(JDK1.8)

首先考虑的问题任然是高并发的安全问题&#xff1a; HashMap是一个不安全的&#xff0c;多线程会导致数据不一致。 Hashtable是安全的&#xff0c;但是速度比较慢&#xff0c;只要是安全的就是一性能为代价换来的&#xff0c;加锁开锁需要时间&#xff0c;加锁是只能一个线程访…

服务注册与发现总结

文章目录 概要一、服务注册与发现技术要点1.1、服务注册/下线1.1.1、代理注册1.1.2、客户端注册 1.2、健康检查&#xff08;续约&#xff09;1.2.1、主动检测1.2.2、被动检测 1.3、服务发现1.3.1、代理发现1.3.2、客户端发现 1.4、服务变更通知1.4.1、主动通知1.4.2、被动通知 …

linux系统vim编辑器的使用

前言&#xff1a; 前面我们就说过在linux系统下一切都是文件&#xff0c;也可以说在日常使用linux系统过程中使用频率最高工具&#xff0c;基本没有之一&#xff0c;今天就来详情介绍一下我们在日常使用vim编辑器的过程中一些常见的指令&#xff0c;博主始终遵循好记心不如烂笔…

Excel排序

Excel排序 写在前面&#xff1a; Excel是我们最常用的办公工具之一&#xff0c;其中排序是最基础和最重要的一项&#xff0c;如下介绍了两种排序方式&#xff0c;有需要者可以参照这些步骤完成相关任务~ 一、排序方法1 使用Excel自带的排序功能&#xff0c;具体步骤如下&…

pandas菜鸟速学-series

一、pandas是什么 一个基于numpy的数据处理数据分析的工具。 特点&#xff1a; Pandas 的主要数据结构是 Series &#xff08;一维数据&#xff09;与 DataFrame&#xff08;二维数据&#xff09;&#xff0c;这两种数据结构足以处理金融、统计、社会科学、工程等领域里的大…