彻底搞明白Spring中的自动装配和Autowired
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了彻底搞明白Spring中的自动装配和Autowired相关的知识,希望对你有一定的参考价值。
参考技术A当Spring装配Bean属性时,有时候非常明确,就是需要将某个Bean的引用装配给指定属性。比如,如果我们的应用上下文中只有一个 org.mybatis.spring.SqlSessionFactoryBean 类型的Bean,那么任意一个依赖 SqlSessionFactoryBean 的其他Bean就是需要这个Bean。毕竟这里只有一个 SqlSessionFactoryBean 的Bean。
为了应对这种明确的装配场景,Spring提供了自动装配(autowiring)。与其显式的装配Bean属性,为何不让Spring识别出可以自动装配的场景。
当涉及到自动装配Bean的依赖关系时,Spring有多种处理方式。因此,Spring提供了4种自动装配策略。
Spring在 AutowireCapableBeanFactory 接口中定义了这几种策略。其中, AUTOWIRE_AUTODETECT 被标记为过时方法,在Spring3.0之后已经不再支持。
它的意思是,把与Bean的属性具有相同名字的其他Bean自动装配到Bean的对应属性中。听起来可能比较拗口,我们来看个例子。
首先,在User的Bean中有个属性 Role myRole ,再创建一个Role的Bean,它的名字如果叫myRole,那么在User中就可以使用byName来自动装配。
上面是Bean的定义,再看配置文件。
如上所述,只要属性名称和Bean的名称可以对应,那么在user的Bean中就可以使用byName来自动装配。那么,如果属性名称对应不上呢?
是的,如果不使用属性名称来对应,你也可以选择使用类型来自动装配。它的意思是,把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。
还是上面的例子,如果使用byType,Role Bean的ID都可以省去。
它是说,把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。值的注意的是, 具有相同类型的其他Bean 这句话说明它在查找入参的时候,还是通过Bean的类型来确定。
构造器中入参的类型为Role
它首先会尝试使用constructor进行自动装配,如果失败再尝试使用byType。不过,它在Spring3.0之后已经被标记为 @Deprecated 。
默认情况下,default-autowire属性被设置为none,标示所有的Bean都不使用自动装配,除非Bean上配置了autowire属性。
如果你需要为所有的Bean配置相同的autowire属性,有个办法可以简化这一操作。
在根元素Beans上增加属性 default-autowire="byType" 。
Spring自动装配的优点不言而喻。但是事实上,在Spring XML配置文件里的自动装配并不推荐使用,其中笔者认为最大的缺点在于不确定性。或者除非你对整个Spring应用中的所有Bean的情况了如指掌,不然随着Bean的增多和关系复杂度的上升,情况可能会很糟糕。
从Spring2.5开始,开始支持使用注解来自动装配Bean的属性。它允许更细粒度的自动装配,我们可以选择性的标注某一个属性来对其应用自动装配。
Spring支持几种不同的应用于自动装配的注解。
我们今天只重点关注Autowired注解,关于它的解析和注入过程,请参考笔者Spring源码系列的文章。 Spring源码分析(二)bean的实例化和IOC依赖注入
使用@Autowired很简单,在需要注入的属性加入注解即可。
不过,使用它有几个点需要注意。
默认情况下,它具有强制契约特性,其所标注的属性必须是可装配的。如果没有Bean可以装配到Autowired所标注的属性或参数中,那么你会看到 NoSuchBeanDefinitionException 的异常信息。
看到上面的源码,我们可以得到这一信息,Bean集合为空不要紧,关键 isRequired 条件不能成立,那么,如果我们不确定属性是否可以装配,可以这样来使用Autowired。
我记得曾经有个面试题是这样问的:Autowired是按照什么策略来自动装配的呢?
关于这个问题,不能一概而论,你不能简单的说按照类型或者按照名称。但可以确定的一点的是,它默认是按照类型来自动装配的,即byType。
关键点 findAutowireCandidates 这个方法。
可以看到它返回的是一个列表,那么就表明,按照类型匹配可能会查询到多个实例。到底应该装配哪个实例呢?我看有的文章里说,可以加注解以此规避。比如 @qulifier、@Primary 等,实际还有个简单的办法。
比如,按照UserService接口类型来装配它的实现类。UserService接口有多个实现类,分为 UserServiceImpl、UserServiceImpl2 。那么我们在注入的时候,就可以把属性名称定义为Bean实现类的名称。
这样的话,Spring会按照byName来进行装配。首先,如果查到类型的多个实例,Spring已经做了判断。
可以看出,如果查到多个实例, determineAutowireCandidate 方法就是关键。它来确定一个合适的Bean返回。其中一部分就是按照Bean的名称来匹配。
最后我们回到问题上,得到的答案就是:@Autowired默认使用byType来装配属性,如果匹配到类型的多个实例,再通过byName来确定Bean。
上面我们已经看到了,通过byType可能会找到多个实例的Bean。然后再通过byName来确定一个合适的Bean,如果通过名称也确定不了呢?
还是 determineAutowireCandidate 这个方法,它还有两种方式来确定。
它的作用是看Bean上是否包含@Primary注解,如果包含就返回。当然了,你不能把多个Bean都设置为@Primary,不然你会得到 NoUniqueBeanDefinitionException 这个异常。
你也可以在Bean上配置@Priority注解,它有个int类型的属性value,可以配置优先级大小。数字越小的,就被优先匹配。同样的,你也不能把多个Bean的优先级配置成相同大小的数值,否则 NoUniqueBeanDefinitionException 异常照样出来找你。
最后,有一点需要注意。Priority的包在 javax.annotation.Priority; ,如果想使用它还要引入一个坐标。
本章节重点阐述了Spring中的自动装配的几种策略,又通过源码分析了Autowired注解的使用方式。
在Spring3.0之后,有效的自动装配策略分为 byType、byName、constructor 三种方式。注解Autowired默认使用byType来自动装配,如果存在类型的多个实例就尝试使用byName匹配,如果通过byName也确定不了,可以通过Primary和Priority注解来确定。
Spring入门
文章目录
Spring Beans 自动装配
- 可以使用·
bean
元素来声明 bean 和通过使用 XML 配置文件中的<constructor-arg>和<property>元素来注入 。
- Spring容器可以在不使用和 元素的情况下自动装配相互协作的 bean 之间的关系,这会减少XML配置的数量
- 自动装配模式 Spring 容器为来使用自动装配进行依赖注入
- 可以使用bean元素的autowire属性为一个bean定义指定自动装配模式
- bean的装配模式有以下几种
模式 | 描述 |
---|---|
no | 默认的设置,没有自动装配 |
byName | 由属性名自动装配,Spring容器在看到XML配置文件中bean的自动装配的属性设置为byName,然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。 |
byType | 由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。 |
constructor | 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。 |
autodetect | Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。 |
Spring自动装配的局限性
- 当自动装配始终在同一个项目中使用时,效果是最好的
- 如果不是使用自动装配,可能会使我们使用它来连接只有一个或者两个bean定义
- 自动装配可以显著减少需要指定的属性或者构造器参数
限制 | 描述 |
---|---|
重写的可能性 | 可以使用总是重写自动装配的 和 设置来指定依赖关系。 |
原始数据类型 | 不能自动装配所谓的简单类型包括基本类型,字符串和类。 |
混乱的本质 | 自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。 |
Spring 自动装配byName
- byName模式由属性名称指定自动装配。Spring容器看作beans,在 XML 配置文件中 beans 的 auto-wire 属性设置为 byName,然后,它尝试将它发属性和配置文件定义为相同命名称的beans进行匹配和连接。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。
- 在配置文件中,如果一个Bean定义设置为自动装配byName,并且它包含 spellChecker 属性(即,它有一个 setSpellChecker(…) 方法),那么 Spring 就会查找定义名为 spellChecker 的 bean,并且用它来设置这个属性。而且可以使用·
<property>
标签连接其余的属性
Spring 自动装配byName样例
- 创建一个名称为 SpringExample 的项目,并且在已创建的项目的 src 文件夹中创建一个包 com.tutorialspoint。
- 使用 Add External JARs 选项,添加所需的 Spring 库,
- 在 com.tutorialspoint 包中创建 Java 类 TextEditor,SpellChecker 和 MainApp
- 在 src 文件夹中创建 Beans 的配置文件 Beans.xml。
TextEditor.java 文件的内容:
package com.tutorialspoint;
public class TextEditor
private SpellChecker spellChecker;
private String name;
public void setSpellChecker( SpellChecker spellChecker )
this.spellChecker = spellChecker;
public SpellChecker getSpellChecker()
return spellChecker;
public void setName(String name)
this.name = name;
public String getName()
return name;
public void spellCheck()
spellChecker.checkSpelling();
依赖类文件 SpellChecker.java 的内容:
package com.tutorialspoint;
public class SpellChecker
public SpellChecker()
System.out.println("Inside SpellChecker constructor." );
public void checkSpelling()
System.out.println("Inside checkSpelling." );
MainApp.java 文件的内容:
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp
public static void main(String[] args)
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
不适用自动装配byName的配置文件 Beans.xml 文件
<?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-3.0.xsd">
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<property name="spellChecker" ref="spellChecker" />
<property name="name" value="Generic Text Editor" />
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
</beans>
使用自动装配 “byName”的 XML 配置文件
<?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-3.0.xsd">
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor"
autowire="byName">
<property name="name" value="Generic Text Editor" />
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
</beans>
运行该应用程序,输出结果为
Inside SpellChecker constructor.
Inside checkSpelling.
Spring自动装配byType
- byType模式由属性类型指定自动装配,Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 byType,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的属性,如果找到匹配项,它将注入这些 beans,否则,它将抛出异常
- 在配置文件中,如果一个 bean 定义设置为自动装配 byType,并且它包含 SpellChecker 类型的 spellChecker 属性,那么 Spring 就会查找类型为 SpellChecker 的 bean并且用它来设置这个属性,仍然可以使用 标签连接其余属性。
Spring自动装配byType样例
- 创建一个名称为 SpringExample 的项目,并且在已创建的项目的 src 文件夹中创建一个包 com.tutorialspoint。
- 使用 Add External JARs 选项,添加所需的 Spring 库,
- 在 com.tutorialspoint 包中创建 Java 类 TextEditor,SpellChecker 和 MainApp。
- 在 src 文件夹中创建 Beans 的配置文件 Beans.xml
TextEditor.java 文件的内容:
package com.tutorialspoint;
public class TextEditor
private SpellChecker spellChecker;
private String name;
public void setSpellChecker( SpellChecker spellChecker )
this.spellChecker = spellChecker;
public SpellChecker getSpellChecker()
return spellChecker;
public void setName(String name)
this.name = name;
public String getName()
return name;
public void spellCheck()
spellChecker.checkSpelling();
``
**SpellChecker.java 的内容:**
```c
package com.tutorialspoint;
public class SpellChecker
public SpellChecker()
System.out.println("Inside SpellChecker constructor." );
public void checkSpelling()
System.out.println("Inside checkSpelling." );
MainApp.java 文件的内容:
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp
public static void main(String[] args)
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
正常情况下的配置文件 Beans.xml 文件:
<?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-3.0.xsd">
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<property name="spellChecker" ref="spellChecker" />
<property name="name" value="Generic Text Editor" />
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
</beans>
使用自动装配 “byType”,那么你的 XML 配置文件
<?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-3.0.xsd">
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor"
autowire="byType">
<property name="name" value="Generic Text Editor" />
</bean>
<!-- Definition for spellChecker bean -->
<bean id="SpellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
</beans>
Spring 由构造函数自动装配
- 由构造函数自动装配模式与 byType 非常相似,但它应用于构造器参数。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 constructor。然后,它尝试把它的构造函数的参数与配置文件中 beans 名称中的一个进行匹配和连线。如果找到匹配项,它会注入这些 bean,否则,它会抛出异常
- 在配置文件中,如果一个 bean 定义设置为通过构造函数自动装配,而且它有一个带有 SpellChecker 类型的参数之一的构造函数,那么 Spring 就会查找定义名为 SpellChecker 的 bean,并用它来设置构造函数的参数。但是仍然可以使用
<constructor-arg>
标签连接其余属性。
Spring 由构造函数自动装配
- 创建一个名称为 SpringExample 的项目,并且在已创建的项目的 src 文件夹中创建一个包 com.tutorialspoint。
- 使用 Add External JARs 选项,添加所需的 Spring 库,
- 在 com.tutorialspoint 包中创建 Java 类 TextEditor,SpellChecker 和 MainApp。
- 在 src 文件夹中创建 Beans 的配置文件 Beans.xml
TextEditor.java 文件的内容:
package com.tutorialspoint;
public class TextEditor
private SpellChecker spellChecker;
private String name;
public TextEditor( SpellChecker spellChecker, String name )
this.spellChecker = spellChecker;
this.name = name;
public SpellChecker getSpellChecker()
return spellChecker;
public String getName()
return name;
public void spellCheck()
spellChecker.checkSpelling();
依赖类文件 SpellChecker.java 的内容:
package com.tutorialspoint;
public class SpellChecker
public SpellChecker()
System.out.println("Inside SpellChecker constructor." );
public void checkSpelling()
System.out.println("Inside checkSpelling." );
MainApp.java 文件的内容
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp
public static void main(String[] args)
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
正常情况下的配置文件 Beans.xml 文件:
<?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-3.0.xsd">
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<constructor-arg ref="spellChecker" />
<constructor-arg value="Generic Text Editor"/>
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
</beans>
使用自动装配 “by constructor”,的 XML 配置文件
<?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-3.0.xsd">
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor"
autowire="constructor">
<constructor-arg value="Generic Text Editor"/>
</bean>
<!-- Definition for spellChecker bean -->
<bean id="SpellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
</beans>
高性能云服务器
精品线路独享带宽,毫秒延迟,年中盛惠 1 折起
以上是关于彻底搞明白Spring中的自动装配和Autowired的主要内容,如果未能解决你的问题,请参考以下文章
Spring 基于自动装配的依赖注入详解 [自动装配模式搞不懂?来看就完了!!!][autowire 属性值详解][自动装配的 6 种模式]