本文简述这三个Spring应用里常用的注解区别。
@Resources
官方文档里对@Resources的说明:
The @Resource annotation is part of the JSR-250 annotation collection and is packaged with Jakarta EE.
什么是JSR-250呢?访问这个链接:https://jcp.org/en/jsr/detail
里面有很多PDF可以下载:
打开一看,其实就是Java支持的注解的文档。这些文档是最权威的:
文档里介绍,@Resources对Bean的注入按照如下的优先级进行:
- Match by Name
- Match by Type
- Match by Qualifier
Match by Name
我们来看看Match by name的例子。下面的代码试图通过Match by Name注入一个名称为namedFile的Bean:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceNameType.class)
public class FieldResourceInjectionIntegrationTest {
@Resource(name="namedFile")
private File defaultFile;
@Test
public void givenResourceAnnotation_WhenOnField_ThenDependencyValid(){
assertNotNull(defaultFile);
assertEquals("namedFile.txt", defaultFile.getName());
}
}
Bean的定义在如下代码里:
@Configuration
public class ApplicationContextTestResourceNameType {
@Bean(name="namedFile")
public File namedFile() {
File namedFile = new File("namedFile.txt");
return namedFile;
}
}
运行时,申明Bean依赖处的@Resource的Name属性和Bean定义处@Bean的Name属性值一致,Match by Name测试通过。
Match by Type
将使用bean的消费者代码里@Resource注解的name属性去掉,使其变成下面这样:
@Resource
private File defaultFile;
测试仍然通过,是因为Match by Name的探测机制执行失败后,进行下一轮Match by Type的探测,这一轮成功了。
Match by Qualifier
定义两个Bean:
@Configuration
public class ApplicationContextTestResourceQualifier {
@Bean(name="defaultFile")
public File defaultFile() {
File defaultFile = new File("defaultFile.txt");
return defaultFile;
}
@Bean(name="namedFile")
public File namedFile() {
File namedFile = new File("namedFile.txt");
return namedFile;
}
}
测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceQualifier.class)
public class QualifierResourceInjectionIntegrationTest {
@Resource
private File dependency1;
@Resource
private File dependency2;
@Test
public void givenResourceAnnotation_WhenField_ThenDependency1Valid(){
assertNotNull(dependency1);
assertEquals("defaultFile.txt", dependency1.getName());
}
@Test
public void givenResourceQualifier_WhenField_ThenDependency2Valid(){
assertNotNull(dependency2);
assertEquals("namedFile.txt", dependency2.getName());
}
}
这一次执行失败,遇到异常org.springframework.beans.factory.NoUniqueBeanDefinitionException.
原因是因为我们的测试代码里,没有指定注入Bean的名称,因此Spring的Match by Name探测失败,进行Match by Type时,探测到两个类型一样的Bean,Spring框架不知道注入哪一个,所以就报异常了。
避免这个异常也很容易,使用@Qualifier.代码如下:
@Resource
@Qualifier("defaultFile")
private File dependency1;
@Resource
@Qualifier("namedFile")
private File dependency2;
@Inject
这个注解定义在JSR-330里,文档链接:
注入的优先级:
- Match by Type
- Match by Qualifier
- Match by Name
Match by Type
注意@Inject注入的最高优先级方式为Match by Type,而非@Resource的Match by Name.
任意定义一个待注入的Component:
@Component
public class ArbitraryDependency {
private final String label = "Arbitrary Dependency";
public String toString() {
return label;
}
}
使用@Inject注入:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestInjectType.class)
public class FieldInjectIntegrationTest {
@Inject
private ArbitraryDependency fieldInjectDependency;
@Test
public void givenInjectAnnotation_WhenOnField_ThenValidDependency(){
assertNotNull(fieldInjectDependency);
assertEquals("Arbitrary Dependency",
fieldInjectDependency.toString());
}
}
Match by Qualifier
定义一个新的待注入组件:
public class AnotherArbitraryDependency extends ArbitraryDependency {
private final String label = "Another Arbitrary Dependency";
public String toString() {
return label;
}
}
测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestInjectQualifier.class)
public class FieldQualifierInjectIntegrationTest {
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
@Test
public void givenInjectQualifier_WhenOnField_ThenDefaultFileValid(){
assertNotNull(defaultDependency);
assertEquals("Arbitrary Dependency",
defaultDependency.toString());
}
@Test
public void givenInjectQualifier_WhenOnField_ThenNamedFileValid(){
assertNotNull(defaultDependency);
assertEquals("Another Arbitrary Dependency",
namedDependency.toString());
}
}
和之前@Resource的第一次试图通过Match by Type注入一样失败,遇到异常:NoUniqueBeanDefinitionException
利用@Qualifier避免这个异常:
@Inject
@Qualifier("defaultFile")
private ArbitraryDependency defaultDependency;
@Inject
@Qualifier("namedFile")
private ArbitraryDependency namedDependency;
Match by Name
public class YetAnotherArbitraryDependency extends ArbitraryDependency {
private final String label = "Yet Another Arbitrary Dependency";
public String toString() {
return label;
}
}
消费者代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestInjectName.class
)
public class FieldByNameInjectIntegrationTest {
@Inject
@Named("yetAnotherFieldInjectDependency")
private ArbitraryDependency yetAnotherFieldInjectDependency;
@Test
public void givenInjectQualifier_WhenSetOnField_ThenDependencyValid(){
assertNotNull(yetAnotherFieldInjectDependency);
assertEquals("Yet Another Arbitrary Dependency",
yetAnotherFieldInjectDependency.toString());
}
}
application context代码:
@Configuration
public class ApplicationContextTestInjectName {
@Bean
public ArbitraryDependency yetAnotherFieldInjectDependency() {
ArbitraryDependency yetAnotherFieldInjectDependency =
new YetAnotherArbitraryDependency();
return yetAnotherFieldInjectDependency;
}
}
测试通过
@Autowired
这个注解和@Inject的用法一致,唯一区别就是@Autowired 属于Spring框架提供的注解。例子略。