@Autowired
和@Resource
都是用于Spring框架中实现依赖注入的注解,它们可以帮助我们自动地将Bean注入到需要的地方,减少硬编码的依赖。下面我会通过代码示例详细解释它们的用途以及在不同匹配规则下的区别。
首先,我们先了解一下在依赖注入场景下类型和名称的含义。
在依赖注入的场景下,考虑一个简单的例子,假设我们正在构建一个音乐播放器应用,这个应用能够播放不同类型的音频文件。为了简化,我们关注播放MP3文件的功能。
// 定义一个播放器接口,这是类型
public interface AudioPlayer {
void play();
}
// 实现MP3播放器,这也是一个类型,具体实现了AudioPlayer接口
@Service("mp3Player") // 注意这里的名称"mp3Player"
public class MP3Player implements AudioPlayer {
@Override
public void play() {
System.out.println("Playing MP3 file");
}
}
// 用户界面类,需要注入一个播放器来播放音乐
@Service
public class MusicUI {
// 使用@Autowired按类型注入,但如果有多个类型匹配的Bean,需要额外指定名称
@Autowired
private AudioPlayer audioPlayer; // 这是类型
// 或者使用@Resource按名称注入
// @Resource(name = "mp3Player") // 这里是名称
// private AudioPlayer audioPlayer;
public void startPlaying() {
audioPlayer.play();
}
}
解释
-
类型(Type):在上述代码中,
AudioPlayer
接口是一个类型,它定义了播放器应具备的播放功能。MP3Player
类也是一个类型,因为它实现了AudioPlayer
接口,是具体播放MP3文件的实现类。在MusicUI
类中,字段audioPlayer
声明为类型AudioPlayer
,表明它期望注入任何实现了AudioPlayer
接口的Bean。 -
名称(Name):在
MP3Player
类的定义中,@Service("mp3Player")
注解为这个Bean指定了名称"mp3Player"。这个名称是用来唯一标识Spring容器中这个具体Bean的标识符。如果我们在MusicUI
类中使用@Resource(name = "mp3Player")
,那么这里"mp3Player"就是我们通过名称指定的注入目标,明确告知Spring应该注入名称为"mp3Player"的Bean。
因此,在这个例子中,“类型”指的是接口AudioPlayer
和其实现类MP3Player
所代表的抽象和具体功能分类,而“名称”则是具体Bean实例在Spring容器中的唯一标识,如"mp3Player"。
@Autowired
@Autowired
是Spring框架提供的注解,主要用于根据类型进行依赖注入。
如果存在多个相同类型的Bean,它会尝试按名称匹配(如果Bean的名称与字段或属性名称相同),或者需要使用@Qualifier
来指定具体Bean的名称。
代码示例:
假设我们有以下两个实现类:
@Service
public class ServiceA implements MyService {}
@Service
public class ServiceB implements MyService {}
按类型注入:
@Service
public class ClientService {
@Autowired
private MyService myService;
// 默认情况下,如果只有一个MyService的实现,将按类型注入。
//如果有多个实现,这里会报错,除非使用@Qualifier指定具体实现。
}
按名称注入(配合@Qualifier):
@Service
public class ClientService {
@Autowired
@Qualifier("ServiceA")
private MyService myService;
// 明确指定注入ServiceA
}
@Resource
@Resource
是Java EE规范的一部分,但Spring也支持使用它进行依赖注入。
它默认按照名称进行匹配,如果未指定名称,则使用字段名或setter方法名(去除set前缀)作为Bean的名称进行查找。如果找不到按名称匹配的Bean,它才会尝试按类型进行匹配。
代码示例:
假设我们有同样的ServiceA
和ServiceB
,但是使用@Resource
进行注入:
按名称注入(默认):
@Service
public class ClientService {
@Resource
private MyService myService;
// 假设有一个Bean的名称为myService,将按名称注入。
//如果没有按名称匹配的Bean,则尝试按类型注入。
}
-
Bean的命名:如果在Spring容器中确实存在一个名称为
myService
的Bean,并且这个Bean是MyService
接口的一个实现(比如ServiceA
或ServiceB
被命名为myService
),那么@Resource
将能够按名称成功注入这个Bean,不会报错。 -
类型兼容性:如果按名称没有找到匹配的Bean,
@Resource
会尝试按类型匹配。如果容器中只有一个实现了MyService
接口的Bean,即使它没有匹配的名称,按类型匹配也能成功,也不会报错。 -
多个类型匹配的Bean:如果按名称没有匹配到Bean,且存在多个实现了
MyService
接口的Bean(如ServiceA
和ServiceB
),而没有明确指定名称,理论上这可能导致不确定的行为或错误,因为@Resource
的文档并未明确指出在此种情况下会如何处理。
但实际上,Spring在处理@Resource
时,如果找不到按名称匹配的Bean,通常会尝试按类型匹配,并且如果存在多个类型匹配的Bean且没有进一步指示(如@Primary
注解来标识首选Bean),在某些版本的Spring中可能会抛出异常,因为无法确定要注入哪个Bean。
指定名称注入:
@Service
public class ClientService {
@Resource(name = "ServiceA")
private MyService serviceA;
// 明确指定注入名为ServiceA的Bean
}
不同匹配规则的区别
- 默认匹配策略:
@Autowired
默认按类型匹配,而@Resource
默认按名称匹配。 - 名称匹配优先级:
@Resource
更倾向于使用名称匹配,即使不显式指定name属性,它也会尝试使用字段名或setter方法名作为Bean名称。而@Autowired
没有这样的默认行为,必须通过@Qualifier
来指定名称。 - 灵活性:
@Autowired
配合@Qualifier
使用时,既可以实现按类型也可以实现按名称注入,提供了较高的灵活性。而@Resource
虽然也可以指定name属性来改变默认行为,但其默认的按名称匹配逻辑在某些情况下可能更加直观。 - 兼容性:
@Resource
因为是Java EE规范的一部分,所以在非Spring框架的Java EE环境中也能使用,具有更好的跨框架兼容性。
综上所述,选择@Autowired
还是@Resource
取决于具体的需求,包括是否需要跨框架兼容性、是否更倾向于名称匹配的直观性、以及对灵活性的需求等。