Spring use of automatic assembly and annotation Autowired

table of Contents

Spring use of automatic assembly and annotation Autowired

An automatic assembly

1、byName

2、byType

3、constructor

4、autodetect

5, the default automatic assembly

二、Autowired

1, Mandatory

2, assembly strategy

3, the primary priority

Third, the summary


Spring use of automatic assembly and annotation Autowired

An automatic assembly

When assembling Spring Bean properties, sometimes very clear, that you need to assemble a reference to the specified properties of a Bean. For example, if our application context is only one type of org.mybatis.spring.SqlSessionFactoryBean Bean, then rely on any one other Bean SqlSessionFactoryBean is the need for this Bean. After all, there is only one SqlSessionFactoryBean of Bean.

In response to this explicit scene assembly, Spring provides automatic assembly (autowiring). Its explicit assembly Bean property, why not Spring scene is recognized automatically assembled.

When it comes to the automated assembly dependencies Bean, Spring a variety of treatments. Therefore, Spring provides four automatic assembly strategy.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public interface AutowireCapableBeanFactory{

 

 //无需自动装配

 int AUTOWIRE_NO = 0;

 

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

 int AUTOWIRE_BY_NAME = 1;

 

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

 int AUTOWIRE_BY_TYPE = 2;

 

 //按构造器自动装配

 int AUTOWIRE_CONSTRUCTOR = 3;

 

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

 @Deprecated

 int AUTOWIRE_AUTODETECT = 4;

}

Spring in the AutowireCapableBeanFactorydefinition of these types of policy interface. Which AUTOWIRE_AUTODETECTis marked as obsolete method is no longer supported after Spring3.0.

1、byName

It means that the other Bean and Bean attributes with the same name to the corresponding automatic assembly of Bean property. May sound hard to pronounce, we look at an example.

First, there is the User of the Bean attribute Role myRole, and then create a Role Bean, its name if called myRole, then you can use the User byName in to automatic assembly.

?

1

2

3

4

5

6

7

public class User{

 private Role myRole;

}

public class Role {

 private String id;

 private String name;

}

Bean is defined above, look at the configuration file.

?

1

2

3

4

5

6

<bean id="myRole" class="com.viewscenes.netsupervisor.entity.Role">

 <property name="id" value="1001"></property>

 <property name="name" value="管理员"></property>

</bean>

 

<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="byName"></bean>

As described above, as long as the property name and the names may correspond Bean, you can be used to automatically fit byName user of the Bean. So, if the property does not correspond to the name on it?

2、byType

是的,如果不使用属性名称来对应,你也可以选择使用类型来自动装配。它的意思是,把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。

?

1

2

3

4

5

6

<bean class="com.viewscenes.netsupervisor.entity.Role">

 <property name="id" value="1001"></property>

 <property name="name" value="管理员"></property>

</bean>

 

<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="byType"></bean>

还是上面的例子,如果使用byType,Role Bean的ID都可以省去。

3、constructor

它是说,把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。值的注意的是,具有相同类型的其他Bean这句话说明它在查找入参的时候,还是通过Bean的类型来确定。
构造器中入参的类型为Role

?

1

2

3

4

5

6

7

8

9

public class User{

 private Role role;

 

 public User(Role role) {

 this.role = role;

 }

}

 

<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="constructor"></bean>

4、autodetect

它首先会尝试使用constructor进行自动装配,如果失败再尝试使用byType。不过,它在Spring3.0之后已经被标记为@Deprecated。

5、默认自动装配

默认情况下,default-autowire属性被设置为none,标示所有的Bean都不使用自动装配,除非Bean上配置了autowire属性。
如果你需要为所有的Bean配置相同的autowire属性,有个办法可以简化这一操作。

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

?

1

<beans default-autowire="byType">

Spring自动装配的优点不言而喻。但是事实上,在Spring XML配置文件里的自动装配并不推荐使用,其中笔者认为最大的缺点在于不确定性。或者除非你对整个Spring应用中的所有Bean的情况了如指掌,不然随着Bean的增多和关系复杂度的上升,情况可能会很糟糕

二、Autowired

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

Spring支持几种不同的应用于自动装配的注解。

  • Spring自带的@Autowired注解。
  • JSR-330的@Inject注解。
  • JSR-250的@Resource注解。

我们今天只重点关注Autowired注解,关于它的解析和注入过程,请参考笔者Spring源码系列的文章。Spring源码分析(二)bean的实例化和IOC依赖注入

使用@Autowired很简单,在需要注入的属性加入注解即可。

?

1

2

@Autowired

UserService userService;

不过,使用它有几个点需要注意。

1、强制性

默认情况下,它具有强制契约特性,其所标注的属性必须是可装配的。如果没有Bean可以装配到Autowired所标注的属性或参数中,那么你会看到NoSuchBeanDefinitionException的异常信息。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,

  Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {

  

 //查找Bean

 Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

 //如果拿到的Bean集合为空,且isRequired,就抛出异常。

 if (matchingBeans.isEmpty()) {

 if (descriptor.isRequired()) {

  raiseNoSuchBeanDefinitionException(type, "", descriptor);

 }

 return null;

 }

}

看到上面的源码,我们可以得到这一信息,Bean集合为空不要紧,关键isRequired条件不能成立,那么,如果我们不确定属性是否可以装配,可以这样来使用Autowired。

?

1

2

@Autowired(required=false)

UserService userService;

2、装配策略

我记得曾经有个面试题是这样问的:Autowired是按照什么策略来自动装配的呢?
关于这个问题,不能一概而论,你不能简单的说按照类型或者按照名称。但可以确定的一点的是,它默认是按照类型来自动装配的,即byType。

默认按照类型装配

关键点findAutowireCandidates这个方法。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

protected Map<String, Object> findAutowireCandidates(

 String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

  

 //获取给定类型的所有bean名称,里面实际循环所有的beanName,获取它的实例

 //再通过isTypeMatch方法来确定

 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(

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

   

 Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);

  

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

 for (String candidateName : candidateNames) {

 if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {

  result.put(candidateName, getBean(candidateName));

 }

 }

 return result;

}

按照名称装配

可以看到它返回的是一个列表,那么就表明,按照类型匹配可能会查询到多个实例。到底应该装配哪个实例呢?我看有的文章里说,可以加注解以此规避。比如@qulifier、@Primary等,实际还有个简单的办法。

比如,按照UserService接口类型来装配它的实现类。UserService接口有多个实现类,分为UserServiceImpl、UserServiceImpl2。那么我们在注入的时候,就可以把属性名称定义为Bean实现类的名称。

?

1

2

@Autowired

UserService UserServiceImpl2;

这样的话,Spring会按照byName来进行装配。首先,如果查到类型的多个实例,Spring已经做了判断。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,

  Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {

   

 //按照类型查找Bean实例

 Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

 //如果Bean集合为空,且isRequired成立就抛出异常

 if (matchingBeans.isEmpty()) {

 if (descriptor.isRequired()) {

  raiseNoSuchBeanDefinitionException(type, "", descriptor);

 }

 return null;

 }

 //如果查找的Bean实例大于1个

 if (matchingBeans.size() > 1) {

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

 String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor);

 if (primaryBeanName == null) {

  throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());

 }

 if (autowiredBeanNames != null) {

  autowiredBeanNames.add(primaryBeanName);

 }

 return matchingBeans.get(primaryBeanName);

 }

}

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

protected String determineAutowireCandidate(Map<String, Object> candidateBeans,

  DependencyDescriptor descriptor) {

 //循环拿到的Bean集合

 for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {

 String candidateBeanName = entry.getKey();

 Object beanInstance = entry.getValue();

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

 if (matchesBeanName(candidateBeanName, descriptor.getDependencyName())) {

  return candidateBeanName;

 }

 }

 return null;

}

最后我们回到问题上,得到的答案就是:@Autowired默认使用byType来装配属性,如果匹配到类型的多个实例,再通过byName来确定Bean。

3、主和优先级

上面我们已经看到了,通过byType可能会找到多个实例的Bean。然后再通过byName来确定一个合适的Bean,如果通过名称也确定不了呢?

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

protected String determineAutowireCandidate(Map<String, Object> candidateBeans,

  DependencyDescriptor descriptor) {

 Class<?> requiredType = descriptor.getDependencyType();

 //通过@Primary注解来标识Bean

 String primaryCandidate = determinePrimaryCandidate(candidateBeans, requiredType);

 if (primaryCandidate != null) {

 return primaryCandidate;

 }

 //通过@Priority(value = 0)注解来标识Bean value为优先级大小

 String priorityCandidate = determineHighestPriorityCandidate(candidateBeans, requiredType);

 if (priorityCandidate != null) {

 return priorityCandidate;

 }

 return null;

}

Primary

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

protected String determinePrimaryCandidate(Map<String, Object> candidateBeans, Class<?> requiredType) {

 String primaryBeanName = null;

 for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {

 String candidateBeanName = entry.getKey();

 Object beanInstance = entry.getValue();

 if (isPrimary(candidateBeanName, beanInstance)) {

  if (primaryBeanName != null) {

  boolean candidateLocal = containsBeanDefinition(candidateBeanName);

  boolean primaryLocal = containsBeanDefinition(primaryBeanName);

  if (candidateLocal && primaryLocal) {

   throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(),

    "more than one 'primary' bean found among candidates: " + candidateBeans.keySet());

  }

  else if (candidateLocal) {

   primaryBeanName = candidateBeanName;

  }

  }

  else {

  primaryBeanName = candidateBeanName;

  }

 }

 }

 return primaryBeanName;

}

Priority

你也可以在Bean上配置@Priority注解,它有个int类型的属性value,可以配置优先级大小。数字越小的,就被优先匹配。同样的,你也不能把多个Bean的优先级配置成相同大小的数值,否则NoUniqueBeanDefinitionException异常照样出来找你。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

protected String determineHighestPriorityCandidate(Map<String, Object> candidateBeans,

     Class<?> requiredType) {

 String highestPriorityBeanName = null;

 Integer highestPriority = null;

 for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {

 String candidateBeanName = entry.getKey();

 Object beanInstance = entry.getValue();

 Integer candidatePriority = getPriority(beanInstance);

 if (candidatePriority != null) {

  if (highestPriorityBeanName != null) {

  //如果优先级大小相同

  if (candidatePriority.equals(highestPriority)) {

   throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(),

   "Multiple beans found with the same priority ('" + highestPriority + "') " +

    "among candidates: " + candidateBeans.keySet());

  }

  else if (candidatePriority < highestPriority) {

   highestPriorityBeanName = candidateBeanName;

   highestPriority = candidatePriority;

  }

  }

  else {

  highestPriorityBeanName = candidateBeanName;

  highestPriority = candidatePriority;

  }

 }

 }

 return highestPriorityBeanName;

}

最后,有一点需要注意。Priority的包在javax.annotation.Priority;,如果想使用它还要引入一个坐标。

?

1

2

3

4

5

<dependency>

 <groupId>javax.annotation</groupId>

 <artifactId>javax.annotation-api</artifactId>

 <version>1.2</version>

</dependency>

三、总结

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

在Spring3.0之后,有效的自动装配策略分为byType、byName、constructor三种方式。注解Autowired默认使用byType来自动装配,如果存在类型的多个实例就尝试使用byName匹配,如果通过byName也确定不了,可以通过Primary和Priority注解来确定。

发布了14 篇原创文章 · 获赞 4 · 访问量 547

Guess you like

Origin blog.csdn.net/sc_942344134/article/details/104093619