在CRUD的过程中,我们经常使用到一个注解@Autowired
当然@Resource也会经常使用到
三者的区别:
@Resource注解方式进行装配,默认按名称装配,当找不到与名称匹配的bean才会按类型装配
@Autowired自动装配具有兼容类型的单个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。