前言

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

依赖注入 - 分类

因为自己是基于小马哥的脉络来学习,并且很认可小马哥梳理的分类方式,下面按照小马哥思想为【依赖注入】分类:

  1. Setter方法注入
  2. 构造器注入
  3. 字段注入
  4. 方法注入
  5. 接口回调注入

1. Setter方法注入

1.1 手动模式

1.1.1 XML配置
    <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder">
<property name="user" ref="superUser" />
</bean>

Spring容器里面有另外一个bean,名字是superUser。

这里user bean的注入就是使用的setter注入。可以在set方法上打断点验证。

后续我们会进行源码分析。

1.1.2 Java注解配置
    @Bean
public UserHolder userHolder(User user) {
UserHolder userHolder = new UserHolder();
userHolder.setUser(user);
return userHolder;
}
1.1.3 API配置
    /**
* 为 UserHolder 生成 BeanDefinition
*/
private static BeanDefinition createUserHolderBeanDefinition() {
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
definitionBuilder.addPropertyReference("user", "superUser");
return definitionBuilder.getBeanDefinition();
}

1.2 自动模式

1.2.1 byName
    <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder" autowire="byName">
</bean>

自动装配默认是byType,不是byName

1.2.2 byType
    <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder" autowire="byType">
</bean>

2. 构造器注入

2.1 手动模式

2.1.1 XML配置
    <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder">
<constructor-arg name="user" ref="superUser" />
</bean>

Spring容器里面有另外一个bean,名字是superUser。

2.1.2 Java注解配置
    @Bean
public UserHolder userHolder(User user) {
return new UserHolder(user);
}
2.1.3 API配置

/**
* 为 UserHolder 生成 BeanDefinition
*/
private static BeanDefinition createUserHolderBeanDefinition() {
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
definitionBuilder.addConstructorArgReference("superUser");
return definitionBuilder.getBeanDefinition();
}

2.2 自动模式

2.2.1 constructor
    <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder"
autowire="constructor">
</bean>

这里的UserHolder类中存在如下构造器:

    public UserHolder(User user) {
this.user = user;
}

构造器自动注入的时候会去spring容器中获取type为User的bean,自动注入。

3. 字段注入

字段注入只有手动模式,并且只支持Java注解配置。

@Autowired

@Resource

@Inject(可选)

    @Autowired
private UserHolder userHolder;
@Resource(name = "xxxx")
private UserHolder userHolder2;
@Inject
private UserHolder userHolder2;

4. 方法注入

方法注入只有手动模式,并且只支持Java注解配置。

@Autowired

@Resource

@Inject(可选)

@Bean

    @Autowired
public void init1(UserHolder userHolder) {
this.userHolder = userHolder;
}
@Resource
public void init2(UserHolder userHolder2) {
this.userHolder2 = userHolder2;
}
@Bean
public UserHolder userHolder(User user) {
return new UserHolder(user);
}

项目中有使用到如下的一种方法注入:

@Autowired
public MyClass(Map<String, MyApplicationEvent> eventMap) {
//any logic
}

这里是自己的bean的构造器,参数eventMap会被容器自动构建一个map,所有MyApplicationEvent类型的bean都会被装载,map的key是bean的名字。

5. 接口回调注入Aware

public class AwareInterfaceDependencyInjectionDemo implements BeanFactoryAware, ApplicationContextAware {

    private static BeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    public static void main(String[] args) {

        // 创建 BeanFactory 容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类) -> Spring Bean
context.register(AwareInterfaceDependencyInjectionDemo.class); // 启动 Spring 应用上下文
context.refresh(); System.out.println(beanFactory == context.getBeanFactory());
System.out.println(applicationContext == context); // 显示地关闭 Spring 应用上下文
context.close();
} @Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
AwareInterfaceDependencyInjectionDemo.beanFactory = beanFactory;
} @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
AwareInterfaceDependencyInjectionDemo.applicationContext = applicationContext;
}
}

之前倒是熟悉这个接口项目中也在使用,但是没想到分类居然是依赖注入相关的。



各种注入方式的比较

上面介绍来大部分注入方式,其实在我自己的项目中用得最多的自然就是字段注入。相信大部分Java开发都是,但是熟悉其他注入方式对spring应用的扩展与理解有更好的帮助。

注入方式 适合于
构造器注入 低依赖
Setter 多依赖
字段注入 便利性
方法注入 声明类

依赖注入 - 特殊注入方式说明

基础类型注入

集合类型注入

限定注入

延迟依赖注入

依赖处理过程

依赖处理过程基础知识

入口:org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency

依赖描述符:org.springframework.beans.factory.config.DependencyDescriptor

自动绑定候选对象处理器:org.springframework.beans.factory.support.AutowireCandidateResolver

依赖处理过程解析

我们先来分析一下DefaultListableBeanFactory.resolveDependency()的方法:

点击查看代码
/**
* 参数1:DependencyDescriptor是依赖描述符
* 参数2:requestingBeanName,待注入的bean名称
*/
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
//注入类型如果是Optional,特殊处理。
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
//注入类型如果是ObjectFactory或ObjectProvider,特殊处理。
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
//注入类型如果是javax.inject.Provider,特殊处理。
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
//常规依赖处理,使用[自动绑定候选对象处理器]处理一个事情,先放着。。。
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
//真正的处理依赖在这里
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}

然后是:DefaultListableBeanFactory.doResolveDependency

点击查看代码

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { //处理嵌套注入时候的一个保护点,获取上次注入点(InjectionPoint),
//内部使用的是ThreadLocal去存储InjectionPoint,所以这里获取的是单线程内的上次的注入点
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
//获取描述符的shortcut
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}
//获取注入类型,未深入
Class<?> type = descriptor.getDependencyType();
//获取建议值,未深入
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ?
getMergedBeanDefinition(beanName) : null);
value = evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
try {
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
}
catch (UnsupportedOperationException ex) {
// A custom TypeConverter which does not support TypeDescriptor resolution...
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
}
//判断是不是处理多个bean,比如Map,Array,List的注入就是在这里处理。
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
//重点:根据要注入的描述符,bean的名字,获取可以注入的容器上下文里面可以注入的候选bean
//20210926分析到这里,接下来进入该方法分析
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
} String autowiredBeanName;
Object instanceCandidate; if (matchingBeans.size() > 1) {
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
}
else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
} if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
if (result instanceof NullBean) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
return result;
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}

依赖描述符DependencyDescriptor类定义:

点击查看代码
/**
* Descriptor for a specific dependency that is about to be injected.
* Wraps a constructor parameter, a method parameter or a field,
* allowing unified access to their metadata.
* (一个特定被注入的依赖的描述符。包含一个构造器、方法或者字段。包装构造函数参数、方法参数或字段,允许对其元数据进行统一访问。)
* @author Juergen Hoeller
* @since 2.5
*/
public class DependencyDescriptor extends InjectionPoint implements Serializable {

注入原理

  • @Autowired注入
  • @Inject注入
  • @Resource注入
  • 自定义依赖注入

【Spring】IoC容器 - 依赖注入的更多相关文章

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

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

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

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

  3. SpringBoot启动流程分析(六):IoC容器依赖注入

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

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

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

  5. 【Spring IoC】依赖注入DI(四)

    平常的Java开发中,程序员在某个类中需要依赖其它类的方法.通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理. Spring提出了依赖注入的思想,即依赖类不由程 ...

  6. Spring Ioc和依赖注入

    总结一下近来几天的学习,做个笔记 以下是Spring IoC相关内容: IoC(Inversion of Control):控制反转: 其主要功能可简单概述为:将 用 new 去创建实例对象,转换为让 ...

  7. Spring IOC容器中注入bean

    一.基于schema格式的注入 1.基本的注入方式 (属性注入方式) 根据setXxx()方法进行依赖注入,Spring只会检查是否有setter方法,是否有对应的属性不做要求 <bean id ...

  8. Spring IOC容器创建bean过程浅析

    1. 背景 Spring框架本身非常庞大,源码阅读可以从Spring IOC容器的实现开始一点点了解.然而即便是IOC容器,代码仍然是非常多,短时间内全部精读完并不现实 本文分析比较浅,而完整的IOC ...

  9. Spring IOC源代码具体解释之容器依赖注入

    Spring IOC源代码具体解释之容器依赖注入 上一篇博客中介绍了IOC容器的初始化.通过源代码分析大致了解了IOC容器初始化的一些知识.先简单回想下上篇的内容 加载bean定义文件的过程.这个过程 ...

随机推荐

  1. 操作系统的IO模型

    IO操作根据设备类型一般分为内存IO,网络IO,和磁盘IO.其中内存IO的速度大大快于后两者,计算机的性能瓶颈一般不在于内存IO. 尽管网络IO可通过购买独享带宽和高速网卡来提升速度,可以使用RAID ...

  2. kivy之ProgressBar、ToggleButton实操学习

    之所以将kivy的ProgressBar(进度条)与ToggleButton(切换按钮)作一篇内容来记录学习,是因为这两个内容比较简单,源码内容篇幅也少. 两个功能实例源码均以main.py+prog ...

  3. idea鼠标双击.log日志文件无法打开

    发现只要再mybatis-config.xml的起别名中加<package name="xxx"/>,就会导致Reader entry: ����   1 n乱码,而R ...

  4. SpringCloudAlibaba - 整合 Nacos 实现服务注册与发现

    目录 前言 环境 Nacos是什么? 服务发现原理 搭建 Nacos Server Nacos Server 下载地址 Nacos Server 的版本选择 运行 Nacos Server Nacos ...

  5. 网络IO模型与Reactor模式

    一.三种网络IO模型: 分类: BIO 同步的.阻塞式 IO NIO 同步的.非阻塞式 IO AIO 异步非阻塞式 IO 阻塞和同步的概念: 阻塞:若读写未完成,调用读写的线程一直等待 非阻塞:若读写 ...

  6. python二级 第七套

    第一部分 基本操作 第一题 1.format()  故名思意  就是格式化什么东西.所以你就是将你 需要格式化的东西 放在里面就行了  .   format(s)  对s 有要求 就是 int(s) ...

  7. P3180-[HAOI2016]地图【圆方树,莫队,分块】

    正题 题目链接:https://www.luogu.com.cn/problem/P3180 题目大意 \(n\)个点\(m\)条边的一个仙人掌,有点权. \(Q\)次询问给出\(op,x,y\),封 ...

  8. ❤️❤️新生代农民工爆肝8万字,整理Python编程从入门到实践(建议收藏)已码:8万字❤️❤️

    @ 目录 开发环境搭建 安装 Python 验证是否安装成功 安装Pycharm 配置pycharm 编码规范 基本语法规则 保留字 单行注释 多行注释 行与缩进 多行语句 数据类型 空行 等待用户输 ...

  9. Python3入门系列之-----异常处理

    前言 作为 Python 初学者,在刚学习 Python 编程时,经常会看到一些报错信息,在前面我们没有提及,这章节我们会专门介绍. Python 有两种错误很容易辨认:语法错误和异常. Python ...

  10. css 弹性盒子--“垂直居中”--兼容写法

    使用弹性盒子兼容低端适配有时需要: display:flex 和 display:-webkit-box   display: -webkit-box; -webkit-box-align: cent ...