你不知道的Spring的依赖的查找和注入的来源

news2025/8/2 10:18:38

1.写在前面

前面的博客我们已经介绍完了spring的依赖的查找和注入的方式,这篇博客我们主要介绍下spring的依赖的查找和注入的来源。

2.依赖查找的来源

查找来源

在这里插入图片描述

Spring 內建 BeanDefintion

在这里插入图片描述

Spring 內建单例对象

在这里插入图片描述

上面的各种依赖都是在spring的生命周期的过程中,初始化的,这儿不做过多的赘述,后面的博客我们在详细的介绍。

3.依赖注入的来源

注入来源

在这里插入图片描述

具体的代码如下:

package org.learn.spring.dependency.source;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.io.ResourceLoader;

import javax.annotation.PostConstruct;

// 依赖来源示例
// BeanFactory.class ResourceLoader.class ApplicationEventPublisher.class ApplicationContext.class 不能用于我们的getBean
public class DependencySourceDemo {

    // 注入在 postProcessProperties 方法执行,早于 setter注入 也早于生命周期回调函数@PostConstruct
    @Autowired
    private BeanFactory beanFactory;

    @Autowired
    private ResourceLoader resourceLoader;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void init() {
        System.out.println("beanFactory == applicationContext :" + (beanFactory == applicationContext));
        System.out.println("beanFactory == applicationContext.getAutowireCapableBeanFactory() :" + (beanFactory == applicationContext.getAutowireCapableBeanFactory()));
        System.out.println("resourceLoader == applicationContext :" + (resourceLoader == applicationContext));
        System.out.println("applicationEventPublisher == applicationContext :" + (applicationEventPublisher == applicationContext));
    }

    @PostConstruct
    public void initByLookUp() {
        getBean(BeanFactory.class);
        getBean(ResourceLoader.class);
        getBean(ApplicationEventPublisher.class);
        getBean(ApplicationContext.class);
    }

    private <T> T getBean(Class<T> beanType) {
        try {
            return beanFactory.getBean(beanType);
        } catch (NoSuchBeanDefinitionException e) {
            System.err.println("当前类型:" + beanType.getName() + "无法在 BeanFactory 中查找!");
        }
        return null;
    }

    public static void main(String[] args) {
        // 创建BeanFactory的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册Configuration Class 配置类 -> Spring Bean
        applicationContext.register(DependencySourceDemo.class);

        // 启动应用上下文
        applicationContext.refresh();

        // 依赖查找 DependencySourceDemo Bean
        DependencySourceDemo demo = applicationContext.getBean(DependencySourceDemo.class);


        // 显示的关闭spring应用上下文
        applicationContext.close();
    }
}

运行的结果如下:

在这里插入图片描述

这儿的对象我们称之为spring的非托管对象,那么是怎么注册的。具体的如下:

在这里插入图片描述

4.Spring容器管理和游离对象

依赖对象

在这里插入图片描述

5.Spring BeanDefinition 作为依赖来源

要素

  • 元数据: BeanDefinition
  • 注册: BeanDefinitionRegistry#registerBeanDefinition
  • 类型: 延迟和非延迟
  • 顺序: Bean 生命周期顺序按照注册顺序

6.单例对象作为依赖来源

要素

  • 来源: 外部普通 Java 对象( 不一定是 POJO)
  • 注册: SingletonBeanRegistry#registerSingleton

限制

  • 无生命周期管理
  • 无法实现延迟初始化 Bean

7.非 Spring 容器管理对象作为依赖来源

要素

  • 注册: ConfigurableListableBeanFactory#registerResolvableDependency

限制

  • 无生命周期管理
  • 无法实现延迟初始化 Bean
  • 无法通过依赖查找

具体的代码如下:

package org.learn.spring.dependency.source;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.annotation.PostConstruct;

// ResolvableDependency 作为依赖来源
// 这儿的依赖的注入只能是类型的依赖的注入
public class ResolvableDependencySourceDemo {

    @Autowired
    private String value;

    @PostConstruct
    public void init(){
        System.out.println(value);
    }

    public static void main(String[] args) {
        // 创建BeanFactory的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册Configuration Class 配置类 -> Spring Bean
        applicationContext.register(ResolvableDependencySourceDemo.class);

        applicationContext.addBeanFactoryPostProcessor(beanFactory -> {
            // 注册 Resolvable Dependency
            beanFactory.registerResolvableDependency(String.class, "Hello,World");
        });

        // 启动应用上下文
        applicationContext.refresh();
        

        // 显示的关闭spring应用上下文
        applicationContext.close();
    }
}

8.外部化配置作为依赖来源

要素

  • 类型: 非常规 Spring 对象依赖来源

限制

  • 无生命周期管理
  • 无法实现延迟初始化 Bean
  • 无法通过依赖查找

具体的代码如下

user.id=1
usr.name=甘雨
user.resource=classpath:/META-INF/default.properties
package org.learn.spring.dependency.source;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.Resource;

import javax.swing.text.StyleConstants;

// 外部化配置作为依赖的来源
@PropertySource(value = "classpath:/META-INF/default.properties",encoding = "UTF-8")
@Configuration
public class ExternalConfigurationDependencySourceDemo {

    @Value("${user.id:-1}")
    private Long id;

    @Value("${usr.name:}")
    private String name;

    @Value("${user.resource:classpath:/META-INF/default.properties}")
    private Resource resource;

    public static void main(String[] args) {
        // 创建BeanFactory的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册Configuration Class 配置类 -> Spring Bean
        applicationContext.register(ExternalConfigurationDependencySourceDemo.class);

        // 启动应用上下文
        applicationContext.refresh();

        // 依赖查找 ExternalConfigurationDependencySourceDemo Bean
        ExternalConfigurationDependencySourceDemo demo = applicationContext.getBean(ExternalConfigurationDependencySourceDemo.class);

        System.out.println("demo.id = " + demo.id);
        System.out.println("demo.name = " + demo.name);
        System.out.println("demo.resource = " + demo.resource);

        // 显示的关闭spring应用上下文
        applicationContext.close();
    }
}

9.再来简单的看一下相应的源码

注册BeanDefinition,具体的代码如下:

	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    // 我们这儿所有的BeanDefinition的类型都是AbstractBeanDefinition AbstractBeanDefinition是所有的BeanDefinition的超类
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
        // 这儿会校验BeanDefinition,这儿我不会做过多的赘述,后面有专门的博客来做介绍
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

    // 先查找对应的BeanDefinition是否存在
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		if (existingDefinition != null) {
      // 如果存在的话,看看这个BeanDefinition是否可以被覆盖,如果可以被覆盖,就重新存入Map中去,springFramework默认是true,是可以被覆盖,在springboot这个值被被改成了false,这儿就会抛出异常。
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (logger.isInfoEnabled()) {
					logger.info("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							existingDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(existingDefinition)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
      // 判断当前的BeanDefinition是否在创建中
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
          // 将对应的key为BeanName value的值为BeanDefinition存放到beanDefinitionMap 中去
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
          // 将对应的BeanName的Map进行更新
					this.beanDefinitionNames = updatedDefinitions;
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
        // 如果没有创建就将key为BeanName value的值为BeanDefinition存放到beanDefinitionMap 中去
				this.beanDefinitionMap.put(beanName, beanDefinition);
        // 将对应的BeanName存到beanDefinitionNames的List中去
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (existingDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}

从上面的的代码我们可以知道BeanDefinition是存在beanDefinitionMap的Map中去,key 是beanName,valueBeanDefinition。同时将beanName存到beanDefinitionNames的List中去。

注册单例对象,具体的代码如下:

	@Override
	public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
    // 走来调用的是父类的方法
		super.registerSingleton(beanName, singletonObject);
		updateManualSingletonNames(set -> set.add(beanName), set -> !this.beanDefinitionMap.containsKey(beanName));
		clearByTypeCache();
	}

可以看到我们走来调用的是父类的方法org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerSingleton

具体的代码如下:

	@Override
	public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
		Assert.notNull(beanName, "Bean name must not be null");
		Assert.notNull(singletonObject, "Singleton object must not be null");
		synchronized (this.singletonObjects) {
      // 先获取对应的单例对象,如果存在直接抛出异常
			Object oldObject = this.singletonObjects.get(beanName);
			if (oldObject != null) {
				throw new IllegalStateException("Could not register object [" + singletonObject +
						"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
			}
			addSingleton(beanName, singletonObject);
		}
	}
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
      // 将这个单例的对象存到对应Map中去,key为beanName value为singletonObject
			this.singletonObjects.put(beanName, singletonObject);
      // 从singletonFactories中移出beanName
			this.singletonFactories.remove(beanName);
      // 从earlySingletonObjects中移出beanName
			this.earlySingletonObjects.remove(beanName);
      // 从registeredSingletons中添加beanName
			this.registeredSingletons.add(beanName);
		}
	}

从上面的代码我们可以知道单例的对象是添加singletonObjectsMap中keybeanNamevalue是这个单例的对象。同时beanName也会存到registeredSingletons中去。

ResolvableDependency最后是不受spring管理的对象,注册的过程如下:

	@Override
	public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {
		Assert.notNull(dependencyType, "Dependency type must not be null");
		if (autowiredValue != null) {
      // 是ObjectFactory 直接抛出异常
			if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
				throw new IllegalArgumentException("Value [" + autowiredValue +
						"] does not implement specified dependency type [" + dependencyType.getName() + "]");
			}
      // 将对应的对象存到resolvableDependencies Map中去 key为dependencyType value为autowiredValue
			this.resolvableDependencies.put(dependencyType, autowiredValue);
		}
	}

上面的代码就是将这个对象存到resolvableDependencies存到Map中去,key为dependencyType value为autowiredValue。上面的就是所有的依赖的注入的来源。而依赖的查找只会BeanDefinition的和singletonObjects这两个,这部分源码后面在介绍。

10.面试题

10.1 注入和查找的依赖来源是否相同?

否, 依赖查找的来源仅限于 Spring BeanDefinition 以及单例对象, 而依赖注入的来源还包括 Resolvable Dependency 以及@Value 所标注的外部化配置

10.2 单例对象能在 IoC 容器启动后注册吗?

可以的, 单例对象的注册与 BeanDefinition 不同, BeanDefinition会被 ConfigurableListableBeanFactory#freezeConfiguration() 方法影响, 从而冻结注册, 单例对象则没有这个限制。

10.3 Spring 依赖注入的来源有哪些?

  • Spring BeanDefinition
  • 单例对象
  • Resolvable Dependency
  • @Value 外部化配置

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

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

相关文章

干掉可恶的弹窗广告——windows系统

一、背景 许多国产软件为了生计&#xff0c;内嵌了无数广告。有些弹窗广告是可以在软件的设置里关闭的&#xff0c;但有些是流氓类型&#xff0c;无法设置。 1、有些是右下角弹窗&#xff0c;不提供关闭的设置&#xff08;能在图片上显示广告来源已经是比较良心了&#xff09…

金融机器学习:数据集划分与baseline模型

原创文章第115篇&#xff0c;专注“个人成长与财富自由、世界运作的逻辑&#xff0c; AI量化投资”。 在模型训练过程中&#xff0c;过拟合&#xff08;overfitting&#xff09;是非常常见的现象。所谓的overfitting&#xff0c;就是在训练集上表现很好&#xff0c;但是测试集…

学弟:功能测试转测试开发容易吗?

最近看到后台留言问&#xff1a;功能测试转测试开发容易吗&#xff1f; 从这个问题&#xff0c;我能读出一些信息如下&#xff1a; 不知道你从事测试工作多久了&#xff0c;可以看出您特别羡慕测试开发工程师&#xff1b;你可能一直从事功能测试工作&#xff0c;工作模式或大…

关于对 DeferWindowPos 的理解

DeferWindowPos 这个 API 的目标是&#xff1a;同一时间&#xff0c;移动多个子窗口。这在一定程度上减少了窗户移动时进行的重绘工作量。 修改我们之前的例子代码&#xff0c;如下图所示&#xff1a; >> 请移步至 topomel.com 查看图片 << 请注意&#xff0c;我…

基于蚁群算法的时延Petri网(ACOTPN)路径规划算法(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

ImmunoChemistry艾美捷抗体涂层缓冲液解决方案

使用ImmunoChemistry艾美捷ICT的抗体涂层缓冲液来节省试剂&#xff0c;增强特定信号&#xff0c;并延长涂层板的保质期。抗体涂层缓冲液最大限度地将抗体吸附到聚苯乙烯板上&#xff0c;并稳定抗体的三维结构以获得最佳性能。 ImmunoChemistry抗体涂层缓冲液&#xff0c;5X通过…

Debian11.5安装Podman并以多容器方式搭建LEMP环境

起因&#xff1a;之前在CentOS系统时代&#xff0c;写过一篇采用docker搭建LEMP环境的博客。现在时过境迁&#xff0c;CentOS系统&#xff0c;CoreOS系统&#xff0c;Docker&#xff0c;Podman管理工具的地位和应用&#xff0c;都有了极大变化&#xff0c;而我也转到了更为自由…

3d-face-reconstruction比较

摘要&#xff1a;比较近3年&#xff0c;6篇顶会3d-face-reconstruction重建效果。 1:Deep3D **发表时间:**2020 成就&#xff1a; 1&#xff09;在REALY和REALY (side-view)两个Benchmark上取得 State-of-the-art。 2&#xff09;官方github上成绩&#xff1a; 3DMM&#xf…

【学习笔记27】JavaScript字符串方法的练习

JavaScript字符串方法的练习一、统计字符串中每个字符出现的次数(以对象的形式记录)1、拿到字符串所有的值2、累加统计次数3、小优化二、反转字符串三、替换违禁词1、需求:将字符串中的6666, 替换为 "****"2、需求: 批量替换违禁词 [6666, 777]四、查询字符串1、模拟…

安全自动化企业网络架构 (毕设分享)

一、前言&#xff1a; 前段时间完成了自己的毕设项目——安全自动化企业网络架构。总的来说&#xff0c;该项目是一个对自己的挑战&#xff0c;其中涉及到Kubernetes容器云的搭建以及安全加固&#xff0c;DevOps CI/CD部署容器化监控平台&#xff0c;Django自动化运维平台开发…

CSS3------盒模型

盒模型 每个标签在浏览器上都被渲染成一个矩形个盒子&#xff0c;这个盒子有一个标准都组成结构&#xff0c;我们称为标准盒模型 盒模型构成 <!DOCTYPE html> <html lang"zh-CN"> <head> <meta charset"UTF-8"> <meta name&qu…

【MM小贴士】物料主数据的中止与后继(2)

【日常吐槽】项目要上线了&#xff0c;70w的物料主数据导完了&#xff0c;有一部分小伙伴被隔离了&#xff0c;没人陪我玩&#xff0c;所以再补充几点日常blog。 很久以前发票了一篇博客&#xff0c;是关于物料主数据的中止与后继&#xff0c; 前期的blog可以参考【MM小贴士】…

Euler diagram

An Euler diagram (/ˈɔɪlər/, OY-lər) is a diagrammatic means of representing sets and their relationships. They are particularly useful for explaining complex hierarchies and overlapping definitions. They are similar to another set diagramming techniqu…

使用css形变实现一个立方体

关于 Nuxt.js 2016 年 10 月 25 日&#xff0c;zeit.co 背后的团队对外发布了 Next.js &#xff0c;一个 React 的服务端渲染应用框架。几小时后&#xff0c;与 Next.js 异曲同工&#xff0c;一个基于 Vue.js 的服务端渲染应用框架应运而生&#xff0c;我们称之为&#xff1a;…

速卖通跨境智星,速卖通合理补单技法

1、测评的用途   提升店铺信誉等级和好评&#xff0c;最重要的是提升产品权重和搜索排名&#xff0c;因为如果产品没有曝光&#xff0c;就没有流量&#xff0c;当然当有了权重和搜索排名&#xff0c;好评也是必不可少的。 2、爆款打造   很多新人在测评的时候会关注店铺排名…

Git版本控制工具使用

文章目录1. CICD系统构成和流程1.1 CICD来源及概念1.2 Git版本控制系统2. Git操作和使用git config (基本配置操作)git clone <repo URL\> (创建仓库|拷贝已有仓库)git branch (分支相关操作- 创建|查看|删除)git checkout (操作文件和分支)git add/commit (提交和修改-保…

Unity导入URDF模型(turtlebot3 waffle pi为例)

本篇文章介绍Unity下如何导入机器人的URDF模型&#xff0c;主要参考官方教程&#xff1a;https://github.com/Unity-Technologies/Unity-Robotics-Hub/blob/main/tutorials/urdf_importer/urdf_tutorial.md   关于导入的机器人模型选择了turtlebot3 waffle pi作为示例 1.Uni…

Sa-Token

介绍 类似于Security的认证授权的解决方案&#xff0c;但是使用起来非常方便&#xff0c;1.支持登录认证&#xff0c;授权权限验证&#xff0c;踢人&#xff1b;2.支持自定义Token&#xff0c;并且能够结合Redis完成前后端分离认证方案&#xff1b;3.支持单点登录&#xff08;…

柯桥成人英语培训机构哪家好,新陈代谢到底是什么?

新陈代谢到底是什么? Metabolism is a combination of biochemical processes that your body uses to convert food into energy. These metabolic processes include breathing, eating and digesting food, the delivery of nutrients to your cells through the blood, th…

【Linux】(四)VS Code远程开发方式-实验室服务器使用VS Code远程开发

VS code 方式系列文章一、服务器情况简介1.1服务器及用户1.2 cuda1.3 conda环境二、VS code连接使用说明2.1 下载VS code2.2 配置2.3 调试文件附录&#xff1a;VS code调试复杂配置公共数据集系列文章 &#xff08;一&#xff09;服务器初次配置及安装vncserver &#xff08;二…