CURD工程师——自动装配注解

在CRUD的过程中,我们经常使用到一个注解@Autowired
当然@Resource也会经常使用到
三者的区别:
@Resource注解方式进行装配,默认按名称装配,当找不到与名称匹配的bean才会按类型装配
@Autowired自动装配具有兼容类型的单个bean属性。可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。
@Autowied的使用来代替set方法。(property属性通过调用setter进行赋值)

@Autowired其实就是自动装配,而在Spring中有三种方法可以进行装配

1.在xml中显式配置

//这种方法比较古老了,现在一般很少用到
<bean id="chinese" class="lrf.spring.duty.ChineseImpl">
  <constructor-arg>
       <list>
            <value>李白</value>
            <value>杜甫</value>
            <value>苏轼</value>
       </list>
  </constructor-arg>

2.在java中显式配置

//创建配置类创建javaConfig类的关键在于为其添加 @Configuration注解,@Configuration注解表明这个类是一个配置类,该类应该包含Spring上下文中如何创建bean的细节。
//之前我们都是依赖组件扫描来发现Spring应该创建的bean。尽管可以同时使用组件扫描和显示配置,但是现在我们去掉了 @CompnentScan注解,现在的CDPlayerConfig类就没有任何作用了。
//如果现在运行之前的测试类,就会失败,并且出现BeanCreationException异常。
@Configuration
public class CDPlayerConfig {
 
}

//声明简单的bean
//@Bean注解会告诉Spring这个方法会返回一个对象,该对象要注册为Spring应用上下文中的bean。
@Bean
public CompactDisc sgtPeppers() {
 return new SgtPeppers();
}

xml更加万能,适用于任何场合,维护更加方便
注解 不是自己的类使用不了,维护相对复杂

3.隐式的bean发现机制和自动装配

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

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

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

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

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;
 }
}

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

默认按照类型装配

关键点findAutowireCandidates这个方法。

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;
}

如果查到类型的多个实例,Spring已经做了判断。

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);
 } 
}

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

 

 

posted @ 2020-06-23 18:32  smartcat994  阅读(222)  评论(0编辑  收藏  举报