Spring中常用的DI标签
Posted 哦...
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring中常用的DI标签相关的知识,希望对你有一定的参考价值。
一、简单说明
@Autowire、@Qualifier、@Resource和@Inject这四个标签都可以在Spring中通过字段(Field injection)或setter完成依赖对象的注入(DI)工作。只不过四个标签在所属阵营和对注入内容所起的作用各有不同。
首先从标签所属阵营来看:
@Resource和@Injection属于Java的注解序列,位于扩展包javax.annotation。而@Autowire和@Qualifier这两个标签是隶属于Spring的。
接下来具体看一下几个标签的用法
二 @Resource注解
@Resource基于JSR-250标准,属于J2EE扩展包。
该注解对于依赖内容注入的判别步骤是:
- by Name 匹配
- by Type 匹配
- by Qualifier 匹配
无论@Resource是添加在filed还是setter,判定注入内容时均采用相同的判别步骤。
@Resource(name="namedFile")
private File defaultFile;
这是一个添加在Field上的@Resource注解,@Resource的name属性"nameFile"。
当@Resource显式指定name属性后,只能按照名称(byName)装配。换句话说注入defaultFile的Bean只能是配置类中@Bean(name="namedFile")或者Bean配置文件中id为namedFile的:
@Configuration
public class ApplicationContextTestResourceNameType
@Bean(name="namedFile")
public File namedFile()
File namedFile = new File("namedFile.txt");
return namedFile;
如果在上下文中无法通过名字匹配到相应的Bean,会抛出org.springframework.beans.factory.NoSuchBeanDefinitionException异常。
public class MethodResourceInjectionIntegrationTest
private File defaultFile;
@Resource(name="namedFile")
protected void setDefaultFile(File defaultFile)
this.defaultFile = defaultFile;
这是一个添加在setter的@Resource注解。@Resource的name属性被赋值为nameFile,而@Resource显式指定name属性后,只能按照名称(byName)装配。如果无法在上下文中通过名字匹配到相应的Bean,同样会抛出org.springframework.beans.factory.NoSuchBeanDefinitionException异常。
如果未显示指定@Resource注解的name属性,则name属性会使用默认值,默认值规则是:
1. 当注解使用在字段(Field)上时,默认值是字段的名称。
@Resource //未指定name,默认name取将要被注入的字段名,即student
private Student student;
2. 当注解使用在setter方法上时,默认值是属性的名称。
@Resource //setStudent说明属性的名称是student,也就是此时@Resource的默认name值
public void setStudent(Student student)
this.student = student;
这时依然优先按照名称(byName)的默认值进行装配。但如果没有匹配内容时,会改为按照类型(byType)装配。
例如,现在的bean配置文件是
<bean id="student1" class="com.piglite.mydomain.Student">
<property name="name" value="TOM"></property>
<property name="age" value="23"></property>
</bean>
byName的话,没有bean的id是student,但是byType可以匹配,所以属性student依然可以被注入成功。
虽然@Resource和@Qualifier注解属于不同的阵营,但是在Spring中这两个标签也可以配合使用。@Qualifier标签可以通过添加“条件”,从众多可以匹配的内容中遴选出最合适的进行装配。
假如现在的配置类是:
@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;
现在需要注入的内容是:
@Resource
private File dependency1;
@Resource
private File dependency2;
此时第一个@Resource的默认name是dependency1,但是没有符合要求的Bean,于是开始按类型匹配,然而配置类中defaultFile和namedFile均是可注入的File类型。这下Spring不知道该选择谁作为dependency1的注入内容,于是只能抛出org.springframework.beans.factory.NoUniqueBeanDefinitionException异常。
这时就需要使用@Qualifier标签来添加额外的说明,以便从多个可匹配的内容中遴选出最合适的。
@Resource
@Qualifier("defaultFile")
private File dependency1;
@Resource
@Qualifier("namedFile")
private File dependency2;
@Qualifier注解清除了所有注入时可能引起模糊的障碍。
同样,@Resource与@Qualifier也可以应用于setter:
@Resource
@Qualifier("namedFile")
public void setArbDependency(File arbDependency)
this.arbDependency = arbDependency;
@Resource
@Qualifier("defaultFile")
public void setAnotherArbDependency(File anotherArbDependency)
this.anotherArbDependency = anotherArbDependency;
再结合配置类中的定义,可以很清晰的判定出依赖项需要注入的内容:
arbDependency注入File("namedFile.txt"),而anotherArbDependency注入File("defaultFile.txt")。
三、@Inject注解
@Inject 注释属于 JSR-330 注释集合。为了正常访问@Inject 注释,必须将 javax.inject 库声明为 Gradle 或 Maven 依赖项。
此注解的匹配路径,按优先级列出:
- by Type 匹配
- by Qualifier 匹配
- by Name 匹配
无论是将@Inject注解应用于Field还是setter,都是遵循一致的匹配路径。
假设现在有一个可以被装配的组件
@Component
public class ArbitraryDependency
private final String label = "Arbitrary Dependency";
public String toString()
return label;
然后是应用于Filed的@Inject注解:
@Inject
private ArbitraryDependency fieldInjectDependency;
@Inject 注释的默认行为是按类型解析依赖项,这就意味着即使依赖项的名称与组件名称不同,只要在应用程序上下文中类型匹配,依赖项就可以被注入。
使用配置类作为注入内容也是可以的:
@Bean
public ArbitraryDependency injectDependency()
ArbitraryDependency injectDependency = new ArbitraryDependency();
return injectDependency;
在@Inject时可能遇到的难题是“如果某个依赖项的类型有多个实现,该选取谁作为依赖项的注入内容呢?”
假设ArbitraryDependency现在有一个子类:
@Component
public class AnotherArbitraryDependency extends ArbitraryDependency
private final String label = "Another Arbitrary Dependency";
public String toString()
return label;
而现在的依赖项是:
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
显然现在的窘境是@Inject标签无法解决的,只能再次抛出NoUniqueBeanDefinitionException异常。我们必须提供更多的线索才能解决注入适当内容的问题。
此时可以再次使用@Qualifier注解,解决从多个可匹配内容中遴选出最合适的问题。将@Qualifer与 @Inject 注释一起使用,这就是代码块现在的样子:
@Inject
@Qualifier("defaultFile")
private ArbitraryDependency defaultDependency;
@Inject
@Qualifier("namedFile")
private ArbitraryDependency namedDependency;
接下来我们再为ArbitraryDependency提供一个子类:
public class YetAnotherArbitraryDependency extends ArbitraryDependency
private final String label = "Yet Another Arbitrary Dependency";
public String toString()
return label;
@Inject还可以与@Name配合,声明需要依赖的bean的名字:
@Inject
@Named("yetAnotherFieldInjectDependency")
private ArbitraryDependency yetAnotherFieldInjectDependency;
很明显,现在依赖项yetAnotherFieldInjectDependency需要类型是ArbitraryDependency而名字是yetAnotherFieldInjectDependency的bean。
配置类:
@Configuration
public class ApplicationContextTestInjectName
@Bean
public ArbitraryDependency yetAnotherFieldInjectDependency()
ArbitraryDependency yetAnotherFieldInjectDependency =
new YetAnotherArbitraryDependency();
return yetAnotherFieldInjectDependency;
显然YetAnotherArbitraryDependency对象将成为yetAnotherFieldInjectDependency依赖项的注入内容。
@Autowired
@Autowired注解的行为类似于@Inject 注解。唯一的区别是@Autowired注解是Spring框架的一部分。@Autowired注解与@Inject注解具有相同的判定路径,按优先顺序列出:
- by Type匹配
- by Qualifier匹配
- by Name匹配
无论是将@Autowired注解应用于Field还是setter,都遵循一致的匹配路径。
@Configuration
public class ApplicationContextTestAutowiredType
@Bean
public ArbitraryDependency autowiredFieldDependency()
ArbitraryDependency autowiredFieldDependency =
new ArbitraryDependency();
return autowiredFieldDependency;
@Autowired
private ArbitraryDependency fieldDependency;
@Autowired注解作用于依赖项fieldDependency,注入的内容只要类型是ArbitraryDependency的bean就可以。恰好配置类里面,虽然bena的名称是autowiredFieldDependency与fieldDependency风马牛不相及,但是autowiredFieldDependency的类型是ArbitraryDependency,秉着byType优先的匹配原则,一个autowiredFieldDependency返回值的对象顺利注入fieldDependency依赖项中。
基于类型匹配优先的特点,困扰@Autowired注解的情况依然会发生在“如果有多个类型的内容都满足依赖项的类型注入需求,哪一个才是适当的注入项?”
更改的配置类如下:
@Configuration
public class ApplicationContextTestAutowiredQualifier
@Bean
public ArbitraryDependency autowiredFieldDependency()
ArbitraryDependency autowiredFieldDependency =
new ArbitraryDependency();
return autowiredFieldDependency;
@Bean
public ArbitraryDependency anotherAutowiredFieldDependency()
ArbitraryDependency anotherAutowiredFieldDependency =
new AnotherArbitraryDependency();
return anotherAutowiredFieldDependency;
修改注入依赖项:
@Autowired
private FieldDependency fieldDependency1;
@Autowired
private FieldDependency fieldDependency2;
@Autowired就不知道如何在配置类的autowiredFieldDependency与anotherAutowiredFieldDependency之间做出抉择了。此时必须有新的条件介入,才能完成注入的甄选,否则只能抛出NoUniqueBeanDefinitionException异常。
解决的方式与前面@Inject注解类似,可以引入@Qualifier增加注入内容名字作为甄选条件:
@Autowired
@Qualifier("autowiredFieldDependency")
private FieldDependency fieldDependency1;
@Autowired
@Qualifier("anotherAutowiredFieldDependency")
private FieldDependency fieldDependency2;
有了@Qualifier,就可以甄别适当的注入内容了。
fieldDependency1被注入autowiredFieldDependency方法的返回值,而fieldDependency2被注入anotherAutowiredFieldDependency方法的返回值。
最后看一下@Autowired如何按照名称进行匹配。@Autowired不能像@Inject注解那样与@Name注解互相配合,@Autowired注解要完成按名称匹配要与@ComponentScan注解在ApplicationContext中一起使用。
更改配置类:
@Configuration
@ComponentScan(basePackages="com.piglite.dependency")
public class ApplicationContextTestAutowiredName
通过使用@ComponentScan注解,Spring框架必须在应用程序上下文中检测 com.piglite.dependency包中被@Component注解的组件类。
然后有一个组件类:
@Component(value="autowiredFieldDependency")
public class ArbitraryDependency
private final String label = "Arbitrary Dependency";
public String toString()
return label;
@Component的value属性值 autowiredFieldDependency告诉Spring,ArbitraryDependency 类是一个名为 autowiredFieldDependency 的组件,可以用来注入。
依赖项依然是:
@Autowired
private ArbitraryDependency autowiredFieldDependency;
为了让@Autowired注解通过名称解析依赖,@Component组件的名称必须与@Autowired注解的Field的名字一致。
关于@Autowired还有一个小知识点是它的required属性。
按类型装配时,默认情况下必须要求依赖对象必须存在(不存在会报错),但是可以通过required=false属性设置非必须。
@Autowired(required = false)
private Date date ;
@Autowired
@Qualifier("birth")
private Date birthday ;
再来看一些关于@Qualifierr的额外“甄别”用法:
@Qualifier可以与@Component合作,充当组件的名称。
@Component
@Qualifer("autowiredFieldDependency")
public class ArbitraryDependency
private final String label = "Arbitrary Dependency";
public String toString()
return label;
@Qualifier可以与@Bean配合,筛选“注入”集合中的内容
@Qualifier
@Bean
public Date d1()
return new Date() ;
@Bean
public Date d2()
return new Date() ;
@Resource
private List<Date> dates = Collections.emptyList() ;
ApplicationContext启动后,dates里面会有两个条目,分别就是d1和d2。
但是现在修改一下字段dates的注解:
@Qualifier
@Bean
public Date d1()
return new Date() ;
@Bean
public Date d2()
return new Date() ;
@Resource
@Qualifier
private List<Date> dates = Collections.emptyList() ;
再次运行后,dates中只注入了一个条目,就是d1。@Qualifier起到了一个让集合只筛选加有@Qualifier注解的Bean才会被收集注入。
现在的问题是,如何选择使用哪个注释以及在什么情况下使用谁的问题。以下是一些建议:
1 通过多态性在应用程序范围内使用单例
如果设计是基于接口或抽象类实现,并且这个行为贯穿整个应用程序中,那么我们可以使用@Inject或@Autowired。
这种方法的好处是,当我们升级应用程序或应用补丁修复错误时,可以换出类,而对整体应用程序影响最小。因为此时主要的默认执行路径是按类型匹配。
2 细粒度的程序行为配置
如果设计使应用程序具有复杂的行为,每个行为都基于不同的接口/抽象类,并且每个实现的用法在应用程序中各不相同,那么我们使用@Resource。在这种情况下,主要默认执行路径是按名称匹配。
3 DI由JEE平台还是Spring平台处理如果使用JEE平台而不是 Spring 注入所有的依赖项,则选择是在 @Resource和@Inject之间。如果要求所有依赖项都由Spring Framework 处理,那么唯一的选择是@Autowired。这些是由标签所属阵营决定的。
以上是关于Spring中常用的DI标签的主要内容,如果未能解决你的问题,请参考以下文章
学习笔记——Spring简介;Spring搭建步骤;Spring的特性;Spring中getBean三种方式;Spring中的标签