前言

上一篇文章已经学习了【依赖注入】相关的知识,这里详细的介绍一下【依赖来源】。

依赖来源

我们把依赖来源分为依赖查找的来源和依赖注入的来源分别讨论。

依赖查找的来源

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容器 - 依赖来源的更多相关文章

  1. Ioc容器依赖注入-Spring 源码系列(2)

    Ioc容器依赖注入-Spring 源码系列(2) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostPr ...

  2. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  3. Spring源码之IOC容器创建、BeanDefinition加载和注册和IOC容器依赖注入

    总结 在SpringApplication#createApplicationContext()执行时创建IOC容器,默认DefaultListableBeanFactory 在AbstractApp ...

  4. Spring IOC 容器源码分析系列文章导读

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  5. 造轮子:实现一个简易的 Spring IoC 容器

    作者:DeppWang.原文地址 我通过实现一个简易的 Spring IoC 容器,算是入门了 Spring 框架.本文是对实现过程的一个总结提炼,需要配合源码阅读,源码地址. 结合本文和源码,你应该 ...

  6. Spring IoC容器的初始化过程

    Spring IoC容器的初始化包括 BeanDefinition的Resource定位.载入和注册 这三个基本的过程.IoC容器的初始化过程不包含Bean依赖注入的实现.Bean依赖的注入一般会发生 ...

  7. TinyFrame续篇:整合Spring IOC实现依赖注入

    上一篇主要讲解了如何搭建基于CodeFirst的ORM,并且在章节末我们获取了上下文对象的实例:BookContext.这节主要承接上一篇,来讲解如何整合Spring IOC容器实现控制反转,依赖注入 ...

  8. 对Spring IoC容器实现的结构分析

    本文的目标:从实现的角度来认识SpringIoC容器. 观察的角度:从外部接口,内部实现,组成部分,执行过程四个方面来认识SpringIoC容器. 本文的风格:首先列出SpringIoC的外部接口及内 ...

  9. 解读Spring Ioc容器设计图

    在Spring Ioc容器的设计中,有俩个主要的容器系列:一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器最基本的功能:另外一个是ApplicationContext应用上下 ...

随机推荐

  1. shell循环语句until

    until 条件 do 执行命令 done until 循环与 while 循环在处理方式上刚好相反. 当条件成立的时候,停止循环.

  2. JDK7&JDK9处理异常新特性

    1.JDK7新特性是在 try (定义对象,作用域就是try方法体) 复制一个文件实例: 复制文件的原理: 先从硬盘写出到内存中,创建文件输入流对象 FileInputStream fis; 中间是在 ...

  3. 硕盟 type-c转接头转接口(HDMI+VGA+USB3.0+PD3.0)四合一拓展坞

    硕盟SM-T54是一款 TYPE C转HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞,支持四口同时使用,您可以将含有USB 3.1协议的电脑主机,通过此产品连接到具有HDMI或VGA的显 ...

  4. 海量列式非关系数据库HBase 架构,shell与API

    HBase的特点: 海量存储: 底层基于HDFS存储海量数据 列式存储:HBase表的数据是基于列族进行存储的,一个列族包含若干列 极易扩展:底层依赖HDFS,当磁盘空间不足的时候,只需要动态增加Da ...

  5. 关联数组VS索引数组

    关联数组和常规说的数组类似,它包含标量抄数据,可用索引值来单独选择这些数据,和常规数组不同的是, 关联数组的索引值不是非负的整数而是任意的标量袭.这些标量称为百Keys,可以在以后用于检索数组中的数值 ...

  6. K8s Master当作Node使用的方法

    1.使用下面的命令操作使得master 可以作为node使用 承载pod kubectl taint nodes --all node-role.kubernetes.io/master- 可能会出现 ...

  7. C++ 输入一行未知个数的整数

    C++ 输入一行未知个数的整数 代码: #include<iostream> #include<vector> using namespace std; int main() ...

  8. CodeForce-782B The Meeting Place Cannot Be Changed(高精度二分)

    https://vjudge.net/problem/CodeForces-782B B. The Meeting Place Cannot Be Changed time limit per tes ...

  9. Java Web实现登录验证码(Servlet+jsp)

    1.生成验证码图片(Servlet) import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import j ...

  10. java设计模式,工厂,代理模式等

    javaEE设计模式: 工厂模式:主要分为三种模式: 定义:在基类中定义创建对象的一个接口,让子类决定实例化哪个类.工厂方法让一个类的实例化延迟到子类中进行. 为什么要使用工厂模式: (1) 解耦 : ...