【Spring】IoC容器 - 依赖来源
前言
上一篇文章已经学习了【依赖注入】相关的知识,这里详细的介绍一下【依赖来源】。
依赖来源
我们把依赖来源分为依赖查找的来源和依赖注入的来源分别讨论。
依赖查找的来源
1. Spring BeanDefinition
这里的Spring BeanDefinition又可以细分为:
- 1.1 客户端自建的Spring Bean:
如 UserService等等bean - 1.2 Spring容器启动时辅助的bean,可以称为Spring内建bean:
如 org.springframework.context.annotation.internalAutowiredAnnotationProcessor
这里的Spring内建bean是在AnnotationConfigUtils.registerAnnotationConfigProcessors()静态方法中所注册的,即注册到BeanDefinitionMap中。 
点击查看[源码片段]
	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
            //省略。。。
		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
            //省略。。。
	}

2. 单例对象
单例对象是指Spring容器启动时创建的一些单例bean,如容器中的Environment对象:
这里的Spring内建bean是在AbstractApplicationContext.prepareBeanFactory()中,添加到BeanFactory的。
点击查看[源码片段]
	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
                //省略...
		// Register default environment beans.
		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
		}
                //省略...
	}
其中1.2【Spring内建bean】以及2【单例对象】中的Spring内建bean都是在 依赖查找 章节中介绍的【Spring内建依赖】中的依赖。

依赖注入的来源
1. Spring BeanDefinition
同上述【依赖查找的来源】。
2. 单例对象
同上述【依赖查找的来源】。
3. 非Spring容器管理对象(ResolvableDependency、非推广对象、游离对象)
这里的【非Spring容器管理对象】是在单例对象那个方法里面一起注册的:
AbstractApplicationContext.prepareBeanFactory()中:
点击查看[源码片段]
	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
                //省略...
		// BeanFactory interface not registered as resolvable type in a plain factory.
		// MessageSource registered (and found for autowiring) as a bean.
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);
                //省略...
	}
那这几个游离对象是怎么运用的呢?
在上一章依赖注入章节中的源码分析中,我们知道在解析注入的依赖的时候:有一个方法DefaultListableBeanFactory.doResolveDependency,
在该方法中,调用了:DefaultListableBeanFactory.findAutowireCandidates(),内部会处理我们这4个特殊的游离对象的注入。
这也就是为什么Spring框架中,我们可以直接用如下代码注入BeanFactory或者ApplicationContext:
@Autowired
ApplicationContext applicationContext;
4. 外部化配置
因为到此为止还没有系统整理以及学习到外部化配置章节,并且基于学习者都是有spring基础的前提下,下例默认在META-INF/default.properties中拥有部分配置。
点击查看[示例代码]
@Configuration
@PropertySource(value = "META-INF/default.properties",encoding="UTF-8")
public class ExternalConfigurationDependencySourceDemo {
    @Value("${user.id:-1}")
    private Long id;
    @Value("${usr.name}")
    private String name;
    @Value("${user.resource:classpath://default.properties}")
    private Resource resource;
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册 Configuration Class(配置类) -> Spring Bean
        applicationContext.register(ExternalConfigurationDependencySourceDemo.class);
        // 启动 Spring 应用上下文
        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();
    }
}
依赖查找的来源 VS 依赖注入的来源
点击查看[示例代码]
public class DependencySourceDemo {
    // 注入在 postProcessProperties 方法执行,早于 setter注入,也早于 @PostConstruct
    @Autowired
    private BeanFactory beanFactory;
    @Autowired
    private ResourceLoader resourceLoader;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    @PostConstruct
    public void initByInjection() {
        System.out.println("beanFactory == applicationContext " + (beanFactory == applicationContext));
        System.out.println("beanFactory == applicationContext.getBeanFactory() " + (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(ApplicationContext.class);
        getBean(ResourceLoader.class);
        getBean(ApplicationEventPublisher.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);
        // 启动 Spring 应用上下文
        applicationContext.refresh();
        // 依赖查找 DependencySourceDemo Bean
        DependencySourceDemo demo = applicationContext.getBean(DependencySourceDemo.class);
        // 显示地关闭 Spring 应用上下文
        applicationContext.close();
    }
运行结果:
beanFactory == applicationContext false
beanFactory == applicationContext.getBeanFactory() true
resourceLoader == applicationContext true
ApplicationEventPublisher == applicationContext true
当前类型org.springframework.beans.factory.BeanFactory 无法在 BeanFactory 中查找!
当前类型org.springframework.context.ApplicationContext 无法在 BeanFactory 中查找!
当前类型org.springframework.core.io.ResourceLoader 无法在 BeanFactory 中查找!
当前类型org.springframework.context.ApplicationEventPublisher 无法在 BeanFactory 中查找!

这里补充一下上图缺少的[外部化配置]的特点:
类型:非常规的Spring对象依赖来源
有无生命周期管理:无
能否延迟初始化:不能
能否依赖查找:不能
Spring BeanDefinition 注册的源码位置
BeanDefinitionRegistry
DefaultListableBeanFactory#registerBeanDefinition
BeanDefinitionBuilder
单例对象注册到Spring IOC 容器的过程 以及 在依赖查找过程中的所充当的角色
单例对象注册到Spring IOC 容器的过程
SingletonBeanRegistry
DefaultListableBeanFactory#registerSingleton
在依赖查找过程中的所充当的角色:
AbstractBeanFactory#getBean(java.lang.String)
非Spring容器管理对象的应用
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,必须在refresh()方法之后执行,但是又必须在aitowired(bean初始化)之前执行
            beanFactory.registerResolvableDependency(String.class, "Hello,World");
        });
        // 启动 Spring 应用上下文
        applicationContext.refresh();
        // 显示地关闭 Spring 应用上下文
        applicationContext.close();
    }
}
注:游离对象可以被应用人员随意注册到spring中,但是需要保证游离对象只能被依赖注入,不能被依赖查找。
外部化配置在spring启动过程中的源码位置
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
DefaultListableBeanFactory#doResolveDependency源码片段
                        //下面的type在上例中第一个获取到java.lang.Long
			Class<?> type = descriptor.getDependencyType();
                        //下面的getAutowireCandidateResolver()方法会得到一个ContextAnnotationAutowireCandidateResolver解析类
                        //该解析类会在下面的getSuggestedValue()方法中解析value的值,此时还是占位符
			Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
			if (value != null) {
				if (value instanceof String) {
                                        //这里会把@Value注解的默认值解析出来
					String strVal = resolveEmbeddedValue((String) value);
ContextAnnotationAutowireCandidateResolver.super.getSuggestedValue(DependencyDescriptor descriptor)源码:
	public Object getSuggestedValue(DependencyDescriptor descriptor) {
		Object value = findValue(descriptor.getAnnotations());
		if (value == null) {
			MethodParameter methodParam = descriptor.getMethodParameter();
			if (methodParam != null) {
				value = findValue(methodParam.getMethodAnnotations());
			}
		}
		return value;
	}
												
											【Spring】IoC容器 - 依赖来源的更多相关文章
- Ioc容器依赖注入-Spring 源码系列(2)
		
Ioc容器依赖注入-Spring 源码系列(2) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostPr ...
 - Spring IOC 容器源码分析 - 循环依赖的解决办法
		
1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...
 - Spring源码之IOC容器创建、BeanDefinition加载和注册和IOC容器依赖注入
		
总结 在SpringApplication#createApplicationContext()执行时创建IOC容器,默认DefaultListableBeanFactory 在AbstractApp ...
 - Spring IOC 容器源码分析系列文章导读
		
1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...
 - 造轮子:实现一个简易的 Spring IoC 容器
		
作者:DeppWang.原文地址 我通过实现一个简易的 Spring IoC 容器,算是入门了 Spring 框架.本文是对实现过程的一个总结提炼,需要配合源码阅读,源码地址. 结合本文和源码,你应该 ...
 - Spring IoC容器的初始化过程
		
Spring IoC容器的初始化包括 BeanDefinition的Resource定位.载入和注册 这三个基本的过程.IoC容器的初始化过程不包含Bean依赖注入的实现.Bean依赖的注入一般会发生 ...
 - TinyFrame续篇:整合Spring IOC实现依赖注入
		
上一篇主要讲解了如何搭建基于CodeFirst的ORM,并且在章节末我们获取了上下文对象的实例:BookContext.这节主要承接上一篇,来讲解如何整合Spring IOC容器实现控制反转,依赖注入 ...
 - 对Spring IoC容器实现的结构分析
		
本文的目标:从实现的角度来认识SpringIoC容器. 观察的角度:从外部接口,内部实现,组成部分,执行过程四个方面来认识SpringIoC容器. 本文的风格:首先列出SpringIoC的外部接口及内 ...
 - 解读Spring Ioc容器设计图
		
在Spring Ioc容器的设计中,有俩个主要的容器系列:一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器最基本的功能:另外一个是ApplicationContext应用上下 ...
 
随机推荐
- 从零开始实现简单 RPC 框架 8:网络通信之 Request-Response 模型
			
Netty 在服务端与客户端的网络通信中,使用的是异步双向通信(双工)的方式,即客户端和服务端可以相互主动发请求给对方,发消息后不会同步等响应.这样就会有一下问题: 如何识别消息是请求还是响应? 请求 ...
 - 性能测试工具JMeter 基础(七)—— 测试元件: 逻辑控制器之if逻辑控制器
			
逻辑控制器线程组指定了其取样器执行的逻辑条件.顺序,并且执行顺序是按照位置顺序从上至下执行的 if逻辑控制器(If Controller) 在逻辑控制器中可设置条件,当条件满足的时候才会被执行 一共有 ...
 - 性能测试工具JMeter 基础(五)—— 测试元件: 测试计划
			
测试计划的定义: 测试计划是测试脚本的容器,定义了要执行什么.怎么执行对测试做总体的设置,且都是从线程组开始执行 在测试计划中可自定义用户变量(User Defined Variables),可通过A ...
 - NRF52832空中升级DFU
			
Secure DFU环境搭建 升级原理,加密原理在此不做描述,详情参考http://www.cnblogs.com/iini/p/9314246.html 1.工具一览 gcc-arm-none-ea ...
 - Python - 面向对象编程 - 魔术方法(双下划线方法)
			
什么是魔术方法 在Python中,所有以 __ 双下划线包起来的方法,都统称为 Magic Method 魔术方法,也叫双下划线方法 有哪些重要的魔术方法? __new__ https://www.c ...
 - 处理器核、Core、处理器、CPU区别&&指令集架构与微架构的区别&&32位与64位指令集架构说明
			
1.处理器核.Core.处理器.CPU的区别 严格来说"处理器核"和" Core "是指处理器内部最核心的部分,是真正的处理器内核:而"处理器&quo ...
 - FastJson之autotype bypass
			
FastJson之autotype bypass 在1.2.25版本之后,添加了checkAutoType方法.在方法中引入了白名单(AutoType).黑名单(denyList)和autoTypeS ...
 - 修改statefulset 有些不允许直接修改
			
1.比如修改一个storageClassName: hostpath 会报错 意思是除了 'replicas', 'template', 'updateStrategy' 其他部分都是不可以 ...
 - PHP垃圾回收机制的一些浅薄理解
			
相信只要入门学习过一点开发的同学都知道,不管任何编程语言,一个变量都会保存在内存中.其实,我们这些开发者就是在来回不停地操纵内存,相应地,我们如果一直增加新的变量,内存就会一直增加,如果没有一个好的机 ...
 - echsop设置伪静态
			
1.后台商店设置-基本设置-URL重写开启 2.修改httpd.conf文件 AllowOverride None 改为 AllowOverride AllLoadModule rewrite_mod ...