Spring 源码阅读(三)-自动注入
上篇文章已经大致了解了依赖出入跟方法注入的内容。这一篇主要来解决上篇文章遗留下来的问题----注入模型
什么是自动注入?
首先搞明白什么是自动注入,自动注入也可以叫做自动装配。
前言
在看下面的内容之前,我们先要对自动注入及精确注入有一个大概的了解,所谓精确注入就是指,我们通过构造函数或者setter方法指定了我们对象之间的依赖,也就是我们上篇文章中讲到的依赖注入,然后Spring根据我们指定的依赖关系,精确的给我们完成了注入。那么自动注入是什么?我们看下面一段代码
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="auto" class="com.bloom.spring.bean.AutoService" autowire="byType">
</bean>
<bean id="xmlService" class="com.bloom.spring.bean.XMLService"/>
</beans>
package com.bloom.spring.bean;
/**
* @program: bloom
* @description:
* @author: hao.yu
* @create: 2020-04-20 10:16
**/
public class XMLService {
public XMLService(){
System.out.println("XMLService init");
}
}
package com.bloom.spring.bean;
/**
* @program: bloom
* @description:
* @author: hao.yu
* @create: 2020-04-20 10:16
**/
public class AutoService {
XMLService xmlService;
public void setXmlService(XMLService xmlService) {
System.out.println("注入XMLService" + xmlService);
this.xmlService = xmlService;
}
}
package com.bloom.spring;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @program: bloom
* @description:
* @author: hao.yu
* @create: 2020-04-20 10:20
**/
public class XMLTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(cc.getBean("auto"));
}
}
输出结果
XMLService init
注入XMLServicecom.bloom.spring.bean.XMLService@2f333739
com.bloom.spring.bean.AutoService@2e5c649
这里在AutoService的标签中新增了一个属性autowire="byType",那么这个属性是什么意思呢?为什么加这个属性就能帮完成注入呢?
1 自动注入
The Spring container can autowire relationships between collaborating beans. You can let Spring resolve collaborators (other beans) automatically for your bean by inspecting the contents of the ApplicationContext. Autowiring has the following advantages:
- Autowiring can significantly reduce the need to specify properties or constructor arguments. (Other mechanisms such as a bean template discussed elsewhere in this chapter are also valuable in this regard.)
- Autowiring can update a configuration as your objects evolve. For example, if you need to add a dependency to a class, that dependency can be satisfied automatically without you needing to modify the configuration. Thus autowiring can be especially useful during development, without negating the option of switching to explicit wiring when the code base becomes more stable.
上面是Spring 官网的对于自动注入的介绍.
1.1 自动注入的优点
Spring可以自动注入相互协调bean之间的依赖关系,自动注入有两个好处
- 自动注入能够显著的减我们指定的属性或者构造函数参数的必要;
- 自动装配可以随着对象的演化更新配置。例如,如果需要向类添加依赖项,则可以自动满足该依赖项,而不需要修改配置。因此,自动装配在开发过程中特别有用,但是当我们的代码库变的稳定时,自动装配也不会影响我们将装配方式切换到精确注入(这个词是我根据官网阅读加自己理解翻译过来的,也就是官网中的(explicit wiring)
1.2 注入模型
接下来,官网介绍了四种自动注入模型,如图:
-
no
这是目前Spring默认的注入模型,也可以说默认情况下面Spring是关闭了自动注入功能的,必须需要我们通过setter方法或者构造函数完成依赖注入,而且Spring也不推荐修改默认配置。所以从实际开发上来说自动注入这种注入方式在日常开发中是基本用不到的;
-
byName
以这种方式完成自动注入,需要满足以下两个条件
- 提供setter方法
- 如果注入的属性为xxx,那么setter方法的名称必须为setXxx,也就是说,命名必须要规范
在找不到对应的bean的情况下,Spring不会报错,只是不会完成自动注入
测试代码
//记得需要将配置信息修改为:<bean id="auto" class="com.bloom.spring.bean.AutoService" autowire="byName"/>
public class AutoService {
XMLService xmlService;
/**
* setXmlService,Spring会根据XMLService到容器中找对应名称的bean,找到了就完成注入
*/
public void setXmlService(XMLService xmlService) {
System.out.println("注入XMLService" + xmlService);
this.xmlService = xmlService;
}
}
如果在参数不规范的情况下面,Spring是不能完成注入的
package com.bloom.spring.bean;
/**
* @program: bloom
* @description:
* @author: hao.yu
* @create: 2020-04-20 10:16
**/
public class AutoService {
XMLService xmlService;
// Service 也被Spring 容器管理
Service service;
public void setXmlService(XMLService xmlService,Service service) {
System.out.println("注入XMLService" + xmlService);
this.xmlService = xmlService;
}
}
本以为这种情况Spring会注入xmlService,service为null,实际测试过程中发现这个set方法根本不会被调用,说明Spring在选择方法时,还对参数进行了校验,byName这种注入模型下,参数只能是我们待注入的类型且只能有一个。
- byType
测试代码跟之前唯一不同的就是修改配置autowire="byType",这里测试三种异常情况
- 找不到合适类型的bean,发现不报异常,同时不进行注入;
- 找到了多个合适类型的bean,Spring会直接报错
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.bloom.spring.bean.XMLService' available: expected single matching bean but found 2: xmlService,xmlService2 - set方法中有多个参数,切两个参数都能找到唯一一个类型符合的bean,不报异常,也不进行注入
- setter方法命名不规范,将set方法仅仅命名为
set,像这样public void set(XmlService xmlService),这种情况下Spring也不会进行注入
总结可以发现,对于这两种模型,其实实际上底层都是依赖setter方法来完成注入的,兵器对于setter方法的命名有一定要求。
- 不要有多个参数
- 命名应该为setXxx,Xxx为需要注入的属性xxx
- constructor
当使用这种注入模型时,Spring会根据构造函数查找有没有对应参数名称的bean,有的话完成注入(跟前文的byName差不多),如果根据名称没找到,那么它会再根据类型进行查找,如果根据类型还是没找到,就会报错。
1.3 Spring 自动注入的缺陷
这里不得不说一句,Spring官网在这一章节有三分之二的内容是在说自定注入的缺陷以及如何将一个类从自动注入中排除,结合默认情况下自动注入是关闭的(默认注入模型为no),可以说明,在实际使用情况中,Spring是非常不推荐我们开启自动注入这种模型的。从官网中总结自动注入有以下几个缺陷:
- 精确注入会覆盖自动注入。并且我们不能注入基本数据类型,字符串,Class类型(这些数据的数组也不行)。而且这是Spring故意这样设计的
- 自动注入不如精确注入准确。而且我们在使用自动注入时,对象之间的依赖关系不明确
- 对于一些为Spring容器生成文档的工具,无法获取依赖关系
- 容器中的多个bean定义可能会与自动注入的setter方法或构造函数参数指定的类型匹配。对于数组、集合或映射实例,这可能不会产生什么问题。但是,对于期望单个值的依赖项,我们无法随意确定到底有谁进行注入。如果没有唯一的bean定义可用,则会抛出异常。
1.4 如何将Bean从自动注入中排除
这里主要用到autowire-candidate这个属性,我们要将其设置为false,这里需要注意以下几点:
- 这个设置只对类型注入生效。这也很好理解,例如我们告诉Spring要自动注入一个
xmlService,同时我们又在xmlService的配置中将其从自动注入中排除,这就是自相矛盾的。所以在byName的注入模型下,Spring直接忽略了autowire-candidate这个属性。 autowire-candidate=false这个属性代表的是,这个bean不作为候选bean注入到别的bean中,而不是说这个bean不能接受别的bean的注入。例如在我们上面的例子中我们对AutoService进行了如下配置:
<bean id="auto" class="com.bloom.spring.bean.AutoService" autowire="byType" autowire-candidate="false">
</bean>
代表的是这个bean不会被注入到别的bean中,但是xmlService任何会被注入到AutoService中
另外需要说明的是,对于自动注入,一般我们直接在顶级的标签中进行全局设置,如下:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"
<!--在这里进行配置-->
default-autowire="byName">
2 自动注入与精准注入的总结

- 从关注的点上来看,自动注入是针对的整个对象,或者一整批对象。比如如果将
xmlService这个bean的注入模型设置为byName,Spring会为去寻找所有符合要求的名字(通过set方法)bean并注入到autoService中。而精确注入这种方式,是我们针对对象中的某个属性,比如我们在autoService中的xmlService这个属性字段上添加了@AutoWired注解,代表我们要精确的注入xmlService这个属性。而方法注入主要是基于方法对对象进行注入。
- 通常所说byName,byType跟在前文提到的注入模型中的
byName,byType是完全不一样的。通常我们说的byName,byType是Spring寻找bean的手段。比如,当我们注入模型为constructor时,Spring会先通过名称找对符合要求的bean,这种通过名称寻找对应的bean的方式我们可以称为byName。可以将一次注入分为两个阶段,首先是寻找符合要求的bean,其次再是将符合要求的bean注入。也可以画图如下:


浙公网安备 33010602011771号