彻底搞明白Spring中的自动装配和Autowired注解的使用

彻底搞明白Spring中的自动装配和Autowired注解的使用

本文主要介绍了对Spring中的自动组装和自动连接注释的使用的全面理解。边肖认为这很好。现在分享给大家,给大家一个参考。来和边肖一起看看吧。

一、自动装配

当Spring组装Bean属性时,有时很明显需要组装一个Bean对指定属性的引用。例如,如果在我们的应用程序上下文中只有一个org . mybatis . spring . SqlSessionFactoryBean类型的Bean,那么任何依赖于sqlsessionfactoryBean的其他Bean都需要这个Bean。毕竟这里只有一个SqlSessionFactoryBean的Bean。

为了处理这种清晰的组装场景,Spring提供了自动装配。与其显式组装Bean属性,为什么不让Spring识别可以自动组装的场景。

当谈到自动组装Bean依赖项时,Spring有许多方法来处理它。因此,Spring提供了四种自动组装策略。

公共接口AutowireCapableBeanFactory{

//不需要自动装配

int AUTOWIRE _ NO=0;

//按名称自动组装bean属性

int AUTOWIRE _ BY _ NAME=1;

//按类型自动组装bean属性

int AUTOWIRE _ BY _ TYPE=2;

//根据构造函数自动组装

int auto wire _ CONSTRUCTOR=3;

//过时的方法,Spring3.0以后不再支持

@已弃用

int auto wire _ auto detect=4;

}

这些策略在Spring AutowireCapableBeanFactory的接口中定义。其中AUTOWIRE_AUTODETECT被标记为过时方法,Spring3.0以后不再支持。

1、byName

意味着与bean的属性同名的其他bean被自动组装成bean的相应属性。这听起来可能有点拗口。让我们看一个例子。

首先在User的Bean中有一个属性Role myRole,然后创建一个Role的Bean。如果它的名字是myRole,那么byName可以用在User中进行自动组装。

公共类用户{

私人角色myRole

}

公共类角色{

私有字符串id;

私有字符串名称;

}

以上是Bean的定义,接下来看配置文件。

bean id= my role class= com . view scenes . net supervisor . entity . role

属性名=id 值= 1001 /属性

属性=名称值=管理员/属性

/bean

bean id= user class= com . view scenes . net supervisor . entity . user auto wire= by name /bean

如上所述,只要属性的名称和Bean的名称能够对应,那么就可以在用户的Bean中使用byName进行自动组装。那么,如果属性名不对应呢?

2、byType

可以,如果不使用属性名对应,也可以选择使用类型自动组装。意味着与bean的属性类型相同的其他bean被自动组装成bean的相应属性。

bean class= com . view scenes . net supervisor . entity . role

属性名=id 值= 1001 /属性

属性=名称值=管理员/属性

/bean

bean id= user class= com . view scenes . net supervisor . entity . user auto wire= by type /bean

在上面的例子中,如果使用byType,可以省略角色Bean的ID。

3、constructor

也就是说,与bean构造函数参数同类型的其他Bean被自动组装成Bean构造函数的相应参数。注意,具有相同类型的其他bean表明,它是在查找参数时由bean的类型决定的。

构造函数中的参数类型是Role。

公共类用户{

私人角色角色;

公共用户(角色role) {

this.role=role

}

}

bean id= user class= com . view scenes . net supervisor . entity . user auto wire= constructor /bean

4、autodetect

首先,它会尝试使用构造函数进行自动组装,如果失败,则尝试使用byType。但在Spring3.0之后已经标记为@Deprecated。

5、默认自动装配

默认情况下,default-autowire属性设置为none,表示所有bean都不使用自动装配,除非在bean上配置了autowire属性。

如果您需要为所有Bean配置相同的autowire属性,有一种方法可以简化此操作。

在根元素Beans上添加属性default-autowire=byType。

beans默认值-autowire=byType

弹簧自动装配的优势不言而喻。但实际上不建议在Spring XML配置文件中自动组装,作者认为最大的缺点在于不确定性。或者除非你对整个Spring应用中的所有豆子了如指掌,随着豆子的增多和关系复杂度的增加,情况可能会很糟糕。

第二,自动连线

从Spring2.5开始,支持注释来自动组装Bean属性。它允许更细粒度的自动组装,我们可以有选择地标记某个属性来对其应用自动组装。

Spring支持几种不同的自动装配注释。

Spring自带的@Autowired注释。

JSR-330的@Inject注释。

@ JSR-250资源评论。

今天,我们只关注自动连线注释。其解析和注入过程请参考作者的Spring source系列文章。Spring源代码分析(2) bean实例化和IOC依赖注入

使用@Autowired非常简单,只需在需要注入的属性中添加注释即可。

@自动连线

UserService用户服务;

但是,在使用时有几点需要注意。

1.命令的

默认情况下,它具有强制契约的特征,其标记的属性必须是可组装的。如果没有Bean可以组装到由Autowired标记的属性或参数中,那么您将会看到nosuchbeandidefinitionexception的异常消息。

公共对象doResolveDependency(dependency descriptor描述符,字符串beanName,

SetString autowiredBeanNames,TypeConverter typeConverter)抛出bean异常{

//查找Bean

MapString,Object matching beans=findAutowireCandidates(bean name,type,descriptor);

//如果获取的Bean集合为空并且是必需的,则引发异常。

if (matchingBeans.isEmpty()) {

if (descriptor.isRequired()) {

raisenosuchbean definitionexception(类型,,描述符);

}

返回null

}

}

看到上面的源代码,我们可以得到这些信息。Bean集是否为空无关紧要,key isRequired条件无法成立。然后,如果我们不确定属性是否可以组装,我们可以像这样使用Autowired。

@自动连线(必需=假)

UserService用户服务;

2、装配策略

记得有个面试问题曾经问过:Autowired自动组装是什么策略?

在这个问题上,我们不能一概而论。不能简单的按类型或者按名字来说。但可以肯定的是,默认是按照类型自动组装的,也就是byType。

按默认类型装配。

关键点是findAutowireCandidates。

受保护的MapString,Object findAutowireCandidates(

字符串beanName,Class。requiredType,DependencyDescriptor描述符){

//获取给定类型的所有bean名称,这实际上是循环所有beanName并获取其实例。

//然后用isTypeMatch方法确定。

string[]candidateNames=beanfactoryutils . beannamesfortype including ancestors(

this,requiredType,true,descriptor . iseager());

MapString,Object result=new LinkedHashMapString,Object(candidatenames . length);

//根据返回的beanName,获取其实例返回。

for(String candidateName:candidateNames){

如果(!isSelfReference(beanName,candidateName)isAutowireCandidate(candidateName,descriptor)) {

result.put(candidateName,get bean(candidateName));

}

}

返回结果;

}

按名字集合

可以看出,它返回了一个列表,表明根据类型匹配可能会查询到多个实例。应该组装哪个实例?我看了一些文章,你可以添加评论来避免它。比如@ qulifer、@Primary等。其实有一个简单的方法。

例如,根据UserService接口类型组装它的实现类。UserService接口有几个实现类,包括UserServiceImpl和UserServiceImpl2。然后当我们注入时,我们可以将属性名定义为Bean实现类的名称。

@自动连线

UserService UserServiceImpl2

在这种情况下,Spring将根据byName进行组装。首先,如果发现该类型的多个实例,Spring已经做出了判断。

公共对象doResolveDependency(dependency descriptor描述符,字符串beanName,

SetString autowiredBeanNames,TypeConverter typeConverter)抛出豆异常{

//按照类型查找豆实例

MapString,对象匹配bean=findAutowireCandidates(bean名称,类型,描述符);

//如果豆集合为空,且是必需的成立就抛出异常

if (matchingBeans.isEmpty()) {

if (descriptor.isRequired()) {

raisenosuchbean定义异常(类型,,描述符);

}

返回空

}

//如果查找的豆实例大于一个

if (matchingBeans.size() 1) {

//找到最合适的那个,如果没有合适的。也抛出异常

string主bean name=determineAutowireCandidate(匹配bean,描述符);

if (primaryBeanName==null) {

抛出新的nouniquebeandidefinitionexception(类型,匹配项。keyset());

}

if (autowiredBeanNames!=null) {

autowiredbean名称。添加(主bean名称);

}

返回配套豆。get(主bean名称);

}

}

可以看出,如果查到多个实例,确定自动线候选人方法就是关键。它来确定一个合适的豆返回。其中一部分就是按照豆的名称来匹配。

受保护的字符串determineAutowireCandidate(MapString,Object candidateBeans,

依赖描述符描述符){

//循环拿到的豆集合

对于(图EntryString,对象条目:候选beans。条目集()){

字符串candidatebean name=entry。getkey();

对象bean实例=条目。getvalue();

//通过matchesBeanName方法来确定豆集合中的名称是否与属性的名称相同

if(匹配bean名称(candidatebean名称,描述符。getdependency name()){

返回候选人姓名

}

}

返回空

}

最后我们回到问题上,得到的答案就是:@自动连线默认使用按类型来装配属性,如果匹配到类型的多个实例,再通过绰号来确定豆子。

3、主和优先级

上面我们已经看到了,通过按类型可能会找到多个实例的豆子。然后再通过绰号来确定一个合适的豆子,如果通过名称也确定不了呢?

还是determineAutowireCandidate这个方法,它还有两种方式来确定。

受保护的字符串determineAutowireCandidate(MapString,Object candidateBeans,

依赖描述符描述符){

班级?必需的类型=描述符。getdependencytype();

//通过@主要注解来标识豆

string primary candidate=determinePrimaryCandidate(候选beans,必选类型);

if (primaryCandidate!=null) {

返回初选候选人

}

//通过@优先级(值=0)注解来标识豆值为优先级大小

string priority candidate=determineHighestPriorityCandidate(候选beans,必选类型);

如果(优先级候选人!=null) {

返回优先候选人

}

返回空

}

Primary

它的作用是看豆上是否包含@主要注解,如果包含就返回。当然了,你不能把多个豆都设置为@初级,不然你会得到nouniquebeandidefinitionexception这个异常。

受保护的字符串determinePrimaryCandidate(MapString,Object candidateBeans,Class?requiredType) {

String primaryBeanName=null

对于(图EntryString,对象条目:候选beans。条目集()){

字符串candidatebean name=entry。getkey();

对象bean实例=条目。getvalue();

if (isPrimary(candidateBeanName,beanInstance)) {

if (primaryBeanName!=null) {

布尔candidateLocal=包含bean定义(candidatebean名称);

布尔主本地=包含bean定义(主bean名称);

if(candidateLocal primaryLocal){

抛出新的nouniquebeandidefinitionexception(必需类型,candidateBeans.size(),

在候选项中找到多个"主”豆子:“candidatebeans。keyset()";

}

else if (candidateLocal) {

主bean名称=candidatebean名称;

}

}

否则{

主bean名称=candidatebean名称;

}

}

}

返回primaryBeanName

}

优先

你也可以在豆上配置@优先级注解,它有个(同国际组织)国际组织类型的属性值,可以配置优先级大小。数字越小的,就被优先匹配。同样的,你也不能把多个豆的优先级配置成相同大小的数值,否则nouniquebeandidefinitionexception异常照样出来找你。

受保护的字符串determineHighestPriorityCandidate(MapString,Object candidateBeans,

班级?requiredType) {

string highestprioritybean name=null;

整数highestPriority=null

对于(图EntryString,对象条目:候选beans。条目集()){

字符串candidatebean name=entry。getkey();

对象bean实例=条目。getvalue();

整数候选优先级=get优先级(bean实例);

if (candidatePriority!=null) {

if (highestPriorityBeanName!=null) {

//如果优先级大小相同

如果(候选人优先。等于(最高优先级)){

抛出新的nouniquebeandidefinitionexception(必需类型,candidateBeans.size(),

找到多个具有相同优先级的豆子(“最高优先级")

候选人中: candidatebeans。keyset());

}

else if(候选优先级最高优先级){

highestprioritybean name=candidatebean name;

最高优先级=候选优先级;

}

}

否则{

highestprioritybean name=candidatebean name;

最高优先级=候选优先级;

}

}

}

返回highestPriorityBeanName

}

最后,有一点需要注意106 .优先事项的包在javax。注释。优先级;如果想使用它还要引入一个坐标。

属国

groupIdjavax.annotation/groupId

artifactId javax。注释-API/artifactId

版本1.2/版本

/依赖关系

三、总结

本章节重点阐述了春天中的自动装配的几种策略,又通过源码分析了自动连线注解的使用方式。

在Spring3.0之后,有效的自动装配策略分为按类型、按名称、构造函数三种方式。注解自动连线默认使用按类型来自动装配,如果存在类型的多个实例就尝试使用绰号匹配,如果通过绰号也确定不了,可以通过主要的和优先注解来确定。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

彻底搞明白Spring中的自动装配和Autowired注解的使用