Autowired并不是一个英文词汇,而是auto + wired两个单词的组合。auto在英文中的原意为汽车,其本意是自动的意思,由于汽车可以不用人推、不用马拉自己跑,所以给它起了个auto的名字。auto源于automatic,即为自动的意思。比如汽车的自动变速器简称为AT,这其中的A则是指automatic。wired是wire的被动式,wire本意为电线。比如我们常说的无线wireless ,本意则是没有线的电线。wire加d,可理解为:串在一起的、绑在一起的、连在一起的。
所以Autowired的本意应该是:(将物体)自动连在一起。在计算机术语中,我们称其为:自动装配。
1. 概述
Spring 自2.5版本引用了注解驱动的依赖注入功能。该功能中最核心的注解即是@Autowired。@Autowired 可以轻松的解决对象与对象间的依赖关系,将指定的bean自动的装配到其它需要它的bean中。
本文中,我们首先给出3种使用@Autowired 的方法,然后对使用@Qualifier 解决bean冲突做个简单的介绍。
2. 启用@Autowired 注解
Spring框架默认启用了依赖注入功能,所以在Spring中我们不需要做任何的配置即启用了@Autowired 注解的自动装配功能。
Spring就像一个大管家,管着很多个bean,哪里声明需要什么bean,它就负责提供什么bean。Spring Boot提供的@SpringBootApplication注解的作用之一便是:告知Spring大管家,应该到哪去获取到它管理的bean。
@SpringBootApplication
public class SpringAutowireApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAutowireApplication.class, args);
}
}
上述代码使得Spring在启动时扫描@SpringBootApplication 所在包以及其子包中的所有bean,并将其纳入Spring大管家的管理范围。在术语中,我们又称这个大管为:Spring Application Context(Spring应用上下文)。
3. 使用@Autowired
我们可以在类的属性、setter方法以及构造函数上使用@Autowired 注解。
3.1 在属性上使用@Autowired
@Autowired可以直接应用到类的属性上,即使该属性的类型不是public、同时也没有为其定义setter方法,也不会影响自动装配的功能。
为了演示自动装配功能,我们首先定义一个FooFormatter bean:
@Component
public class FooFormatter {
public String format() {
return "foo";
}
}
接着便可以在属性上应用@Autowired
来完成自动装配:
@Component
public class FooService {
@Autowired
private FooFormatter fooFormatter;
}
此时,Spring将自动为FooService
中的FooFormatter
属性提供fooFormatter
bean。我们也可以认为是自动(auto)将FooFormatter
与FooFormatter
连接(wire)了起来,所以此项功能叫做Autowired。
3.2 在setter方法上使用@Autowired
还可以在setter方法上使用@Autowired,比如3.1中的代码还可改写为:
@Component
public class FooService {
private FooFormatter fooFormatter;
@Autowired
public void setFooFormatter(FooFormatter fooFormatter) {
this.fooFormatter = fooFormatter;
}
}
与在属性上使用@Autowired来完成自动装配相比较,在setter上使用@Autowired没有什么明显的不 。当我们在装配某个属性的同时还希望执行某些逻辑操作时,往往会这么做。
3.3 在构造函数上使用@Autowired
最后,还可以在构造函数上使用@Autowired,这也是官方推荐的一种使用方式,比如3.2的代码还可以改写为:
@Component
public class FooService {
private FooFormatter fooFormatter;
@Autowired
public FooService(FooFormatter fooFormatter) {
this.fooFormatter = fooFormatter;
}
}
值得注意的是,在较新的Spring版本中完全可以省略在构造函数上声明的@Autowired注解。也就是说以下代码同样可以完成自动装配功能:
@Component
public class FooService {
private FooFormatter fooFormatter;
public FooService(FooFormatter fooFormatter) {
this.fooFormatter = fooFormatter;
}
}
4. NoSuchBeanDefinitionException
当声明自动装配的bean不存在时:
public interface BarRepository★ {
}
@Service
public class BarService {
@Autowired
BarRepository barRepository;
}
- ★ 只声明了接口,并对接口进行实现,所以并不存在类型为BarRepository的bean。
在进行单元测试时,Spring将抛出NoSuchBeanDefinitionException:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'barService': Unsatisfied dependency expressed through field 'barRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'club.codedemo.springautowire.section4.BarRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
NoSuchBeanDefinitionException直译为:没有找到相关的bean。根据实际情况不同,解决这个异常的方法分为两种:
第一种是前期我们容易犯的错误,即我们的确需要这样的一个bean,而且该bean也是由我们提供的,但忘记使用@Component 相关注解声明了。解决的方法当然是声明该类型的bean即可。
第二种是该bean可以有,也可以没有。那么此时则可以将@Autowired 的required 属性设置为false :
@Service
public class BarService {
@Autowired(required = false)
BarRepository barRepository;
}
值得注意的是Spring Boot很友好的处理了NoSuchBeanDefinitionException异常,当发生NoSuchBeanDefinitionException异常时,Spring Boot给我们的提示信息如下:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field barRepository in club.codedemo.springautowire.section4.BarService required a bean of type 'club.codedemo.springautowire.section4.BarRepository' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
大体是说:没有找到类型为BarRepository的bean。
5. NoUniqueBeanDefinitionException
默认情况下@Autowired
注解是根据类型来完成自动装配的,在装配的过程中如果同一类型的bean存在多个,则会发生NoUniqueBeanDefinitionException异常。
此时便需要告知Spring大管家如何解决此类问题。
5.1 @Qualifier
qualifier译为限定符、限定语。
@Qualifier注解能够非常好的解决此类问题,比如我们当前有两个实现了Message 接口的bean:
public interface Message {
String hello();
}
@Component
public class FooMessage implements Message {
@Override
public String hello() {
return "foo";
}
}
@Component
public class BarMessage implements Message {
@Override
public String hello() {
return "bar";
}
}
此时若直接使用@Autowired 注解尝试注入Message:
@Service
public class MessageService {
@Autowired
Message message;
}
由于FooMessage 和BarMessage都实现了Message接口,所以Spring容器中现在有两个同样是实现了Message接口的bean,此时若使用@Autowired 尝试注入Message, Spring便不知道应该是注入FooMessage 还是BarMessage,所以发生NoUniqueBeanDefinitionException异常。
NoUniqueBeanDefinitionException的字面意思也很好的说明了这个问题:定义的bean不唯一。
当同一类型存在多个bean时,可以使用@Qualifier 来显示指定bean的名称,从而解决了Spring按类型注入时不知道应该注入哪个bean的尴尬情况。
@Service
public class MessageService {
@Autowired
@Qualifier("fooMessage")
Message message;
}
默认情况下Spring自动为每个bean添加一个唯一的名称,该名称的命名原则为:将类的名称由大驼峰变为小驼峰。所以FooMessage 的bean名为:fooMessage;BarMessage 的bean名为: barMessage。除此以外,我们还可以自定义bean的名称,比如将BarMessage 的bean名称定义为customBarMessage:
@Component("customBarMessage")
public class BarMessage implements Message {
@Qualifier 将根据声明的fooFormatter来完成对Formatter的注入。此时FooService 中被注入的Formatter 为FooFormatter 而非BarFormatter 。
5.2 自定义装配限定器
Spring还允许我们基于Qualifer 自定义用于限定注入特定Bean的注解。
@Qualifier
@Target({
ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER
})
@Retention(RetentionPolicy.RUNTIME)
public @interface MessageType {
}
此时我们将注解应用到FooMessage 上:
@Component
@MessageType
public class FooMessage implements Message {
则可以结合@Autowired 完成限定注入FooMessage :
@Service
public class MessageService {
@Autowired
@MessageType
Message message1;
}
由于自定义的@MessageType 继承了@Qualifier ,所以其也拥有@Qualifier 的特性。当多个相同类型的Bean均使用@MessageType 声明时,也可以使用@MessageType(bean的名称) 来指定自动装配的bean。
5.3 依据属性名称自动装配
Spring在自动装配过程中,当同一类型存在多个bean时,还会继续按属性名称来进行查找,比如:
@Autowired
Message message;
Spring首先查找类型为Message的bean,最终查找到两个:fooMessage以及barMessage;接着将会在找到的两个bean中继续按属性名称message进行查找;最终发现fooMessage以及barMessage均不匹配message,这时候就视为查找失败,抛出异常。
根据上述规律,当某类型存在多个bean时,也可以使用指定属性名称与bean名相匹配的方法来完成限定注入:
@Service
public class MessageService {
@Autowired
Message barMessage;
}
此时将成功注入一个BarMessage的实例。
总结
@Autowired 自动装配注解是Spring的核心注解之一,我们可以使用@Autowired 按接口类型轻松的完成bean的注入,而不必关心具体的实现。是对面向接口编程具体的阐释。在实际的使用过程中,往往会遇到多个bean实现同一接口而产生冲突的情况,此时则需要配合其它方法来告知Spring大管家冲突的解决方案。本文主要对@Autowried 注解的使用方法以及如何解决冲突进行阐述。