Spring自动装配的方式

1. 什么是自动装配?

​ 自动装配就是会通过Spring 的上下文为你找出相应依赖项的类,通俗的说就是Spring 会在上下文中自动查找,并自动给Bean装配与其相关的属性!

也就是 Spring 会在容器中自动的查找,并自动的给 bean 装配及其关联的属性

​ 涉及到自动装配 bean 的依赖关系时,Spring 有多种处理方式。Spring 提供了 4 种自动装配策略:

public interface AutowireCapableBeanFactory extends BeanFactory {// 无需自动装配int AUTOWIRE_NO = 0;// 按名称自动装配 bean 属性int AUTOWIRE_BY_NAME = 1;// 按类型自动装配 bean 属性int AUTOWIRE_BY_TYPE = 2;// 按构造器自动装配int AUTOWIRE_CONSTRUCTOR = 3;// 过时方法,Spring3.0 之后不再支持@Deprecatedint AUTOWIRE_AUTODETECT = 4;
}

2.什么是依赖注入?

2.1. 依赖注入和控制反转

​ 依赖注入:当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。然而采用依赖注入的方式,创建被调用者的工作不再由调用者来完成,创建被调用者的实例的工作由 IOC 容器来完成。然后注入调用者,称为依赖注入。

​ 控制反转:当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。然而采用控制反转的方式,创建被调用者的工作不再由调用者来完成,创建被调用者的实例的工作由 IOC 容器来完成。也就是把对象的创建,初始化,销毁的工作交给 spring ioc 容器来做。由 spring ioc 容器来管理对象的生命周期。

​ 依赖注入(Dependency Injection,DI)和控制反转含义相同,它们是从两个角度描述的同一个概念。详细介绍请参考下文:https://blog.csdn.net/Mr_zhang66/article/details/115439221

2.1. 依赖注入与自动装配的关系

​ 依赖注入的本质就是装配,装配是依赖注入的具体行为

​ 在传统的使用 xml 文件装配 bean 是一件很繁琐的事情,而且还需要找到对应类型的 bean 才能装配,一旦 bean 很多,就不好维护了。为了解决这种问题,spring 使用注解来进行自动装配。自动装配就是开发人员不必知道具体要装配哪个 bean 的引用,这个识别的工作会由 spring 来完成。与自动装配配合的还有“自动检测”,这个动作会自动识别哪些类需要被配置成 bean,进而来进行装配

​ 因此也可以这样理解:自动装配是为了将依赖注入“自动化”的一个简化配置的操作

3. Spring 中自动装配的策略

3.1. byName

​ 它的意思是:把与 bean 的属性具有相同名字的其他 bean 自动装配到 bean 的对应属性中

例:在 Userbean 中有个属性 Role myRole,再创建一个 Rolebean,它的名字如果叫 myRole,那么在 User 中就可以使用 byName 来自动装配。

public class User{private Role myRole;
}public class Role {private String id;  private String name;
}

上面是 bean 的定义,再看配置文件

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

​ 如上所述,只要属性名称和 bean 的名称可以对应,那么在 userbean 中就可以使用 byName 来自动装配。

​ 除了以上xml配置的方式(springMVC的方式),当使用springboot的时候,可以直接在Role中或者其他的bean中使用@Resoure(“XXX”)/@Service(“XXX”)指定属性名XXX,XXX相当于 xml配置中得bean id = service,也可以不指定XXX, 不指定相当于 bean id = com. service.service 就是这个类的全限定名,表示给当前类命名一个别名,方便注入到其他需要用到的类中;不加的话,默认别名就是当前类名,但是首字母小写 。

参考:spring注解@service(“service”)括号中的service有什么用。

那么,如果属性名称对应不上呢?就会根据type来装配

3.2. byType

​ 它的意思是:把与 bean 的属性具有相同类型的其他 bean 自动装配到 bean 的对应属性中

​ 使用byType的方式(autowire="byType"),自动寻找class属性与这个bean(people)属性的类型相同的已有的bean(cat和dog),将其装配到这个bean对象中。

<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 beanid 都可以省去。

3.3. constructor (构造器)

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

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>

3.4. autodetect(不推荐使用了)

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

3.5. no - 默认的自动装配策略

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

​ 如果你需要为所有的 bean 配置相同的 autowire 属性,有个办法可以简化这一操作,在根元素 beans 上增加属性。

<beans default-autowire="byType">

4. @Autowired 注解

​ 从 Spring 2.5 开始,开始支持使用注解来自动装配 bean 的属性。它允许更细粒度的自动装配,我们可以选择性的标注某一个属性来对其应用自动装配。Spring 支持几种不同的应用于自动装配的注解

Spring 自带的 @Autowired 注解
JSR-330 的 @Inject 注解
JSR-250 的 @Resource 注解
使用 @Autowired 它有几个点需要注意

4.1. 强制性

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

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {// 查找BeanMap<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);// 如果拿到的 Bean 集合为空,且 isRequired 为 true,就抛出异常if (matchingBeans.isEmpty()) {if (descriptor.isRequired()) {raiseNoSuchBeanDefinitionException(type, "", descriptor);}return null;}
}

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

@Autowired(required = false)
UserService userService;

4.2. 装配策略

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

4.2.1. 默认按照类型装配

关键点 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;
}

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

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

@Autowired
UserService UserServiceImpl2;

这样的话,spring 会按照 byName 来进行装配。首先,如果查到类型的多个实例,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);}
}

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

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

4.3. 主和优先级

上面我们已经看到了,通过 byType 可能会找到多个实例的 bean。然后再通过 byName 来确定一个合适的 bean,如果通过名称也确定不了呢?
还是 determineAutowireCandidate 这个方法,它还有两种方式来确定

protected String determineAutowireCandidate(Map<String, Object> candidateBeans, DependencyDescriptor descriptor) {Class<?> requiredType = descriptor.getDependencyType();// 通过 @Primary注解来标识 beanString 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;
}

4.3.1. @Primary 注解

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

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

4.3.2. @Priority 注解

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

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,如果想使用它还要引入一个坐标

<dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.2</version>
</dependency>

参考文章链接:https://www.jianshu.com/p/2f1c9fad1d2d

​ https://blog.csdn.net/weixin_38192427/article/details/108610810

​ https://blog.csdn.net/qq_57434877/article/details/123933529

​ https://blog.csdn.net/qq_53916344/article/details/124186676

学习篇(一)- Spring自动装配的方式相关推荐

  1. Spring自动装配的方式

    Spring自动装配的方式 Spring 装配包括手动装配和自动装配,手动装配是有基于xml 装配.构造方法.setter 方法等 自动装配有五种自动装配的方式,可以用来指导Spring 容器用自动装 ...

  2. Spring中自动装配的方式简介说明

    由于有了Spring自动装配的存在,才使得我们开发spring应用变的快捷,那么Spring自动装配有哪些方式呢? 下文笔者讲述Spring自动装配的方式分享,如下所示 Spring对外提供5种自动装 ...

  3. 【Spring学习笔记 四】Spring自动装配机制实践

    我们一般学习某个知识,一定会现有个较为复杂的配置让你理解其中的关系,这个配置清晰规整,但是可能会需要大量的配置,这个时候就会有约定大于配置的理论实现了,通过我们约定好的一致的名称,我可以少写很多对应关 ...

  4. Spring自动装配(autowire)篇

    什么是自动装配 自动装配帮我们省去了 property标签配置操作,Spring会自动根据 属性名称,类型,构造器来进行自动注入. 例如不进行自动装配配置如下: <bean id="u ...

  5. Spring注解驱动开发学习总结8:自动装配注解@Autowire、@Resource、@Inject

    Spring注解驱动开发学习总结8:自动装配注解@Autowire.@Resource.@Inject 1.自动装配@Autowire.@Resource.@Inject 1.1 构建bookDao ...

  6. 第六篇 Spring 自动装配

    <Spring>篇章整体栏目 ----------------------------- [第一章]spring 概念与体系结构 [第二章]spring IoC 的工作原理 [第三章]sp ...

  7. spring注解驱动开发-4 Spring 自动装配

    Spring 自动装配 前言 Spring 自动装配的几种方式 1.@Autowired @Qualifier("组件id") @Primary 2.@Resource方式 3.@ ...

  8. Java的注解机制——Spring自动装配的实现原理

    JDK1.5加入了对注解机制的支持,实际上我学习Java的时候就已经使用JDK1.6了,而且除了@Override和@SuppressWarnings(后者还是IDE给生成的--)之外没接触过其他的. ...

  9. (二)Spring自动装配

    Spring自动装配 为了减少XML的配置数量.Spring提供了几种技巧来解决这一问题: 自动装配(autowiring): 有助于减少<property>元素和<constroc ...

最新文章

  1. html input标签 alt和title 比较
  2. 系统设计经典题:手把手教你搭建一个IM(即时通讯) 系统
  3. 手机屏幕物理点击器是什么原理_手机屏幕为什么能触控 手机屏幕触控介绍【详解】...
  4. 2.第一个HTML页面
  5. 产品经理学习——卡诺模型
  6. TVP5150视频解码芯片 调试总结
  7. mysql 5.7.20免安装_Windows下MySQL 5.7.20 免安装版配置
  8. Win7更换锁屏和开机画面
  9. 如何停止线程?stop方法过时弊端
  10. 银行账户模拟java_使用Java模拟银行账户存、取款、转账功能
  11. 卷积神经网络结构相关
  12. css缓慢执行hover
  13. Keras系列之文本向量化
  14. 搞死SAP系统系列 让PO系统宕机
  15. MCE | 分子伴侣介导的自噬
  16. LPC1768 移植freeRTOS
  17. 第四课 C++中的运算符
  18. toArray()和toArray(Object a[ ]) 区别
  19. python教程(从入门到巅峰)1
  20. 174道 JavaScript 面试题,助你查漏补缺

热门文章

  1. AD原理图中如何修改系统库中的引脚
  2. 树莓派4+ docker+ homeassistant
  3. SpringCloud Alibaba之 Sentinel流量防卫兵
  4. 深度学习与自然语言处理第三次作业——LDA段落主题分布问题
  5. c语言里的%p的作用,c语言中 %p的含义
  6. python撤销功能思路,在python中进行撤消
  7. 微信迎来史诗级更新,以及两款微信清理工具
  8. win10屏幕亮度无法调节如何处理
  9. 企业人力资源立项报告
  10. 计算机集成制造系统服务合同,集成制造系统.ppt