1、自动装配与@Autowired
这里首先做一个区分,因为在之前的很长一段时间内,我都错误的以为 @Autowired 就是自动装配。这也就引发了我一直错误的任务Spring的自动 装配首先是 byType 然后是 byName 的。通过这段时间对于源码的阅读,我才意识到这个错误。
当涉及到自动装配Bean的依赖关系时,Spring提供了4种自动装配策略。
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; ...}
1.1 自动装配
在 xml 中定义 Bean的时候,可以通过如下的方式指定自动装配的类型。
如果使用了根据类型来自动装配,那么在IOC容器中只能有一个这样的类型,否则就会报错!
1.2 使用注解来实现自动装配
@Autowired 注解,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。Spring是通过 @Autowired 来实现自动装配的。 当然,Spring还支持其他的方式来实现自动装配,如:「JSR-330的@Inject注解」、「JSR-250的@Resource注解」。
通过注解的方式来自动装配 Bean 的属性,它允许更细粒度的自动装配,我们可以选择性的标注某一个属性来对其应用自动装配。
2、依赖注入
在这篇文章中,我将详细的分析,在一个对象中通过 @Autowired注入或 @Resource 注入属性的处理过程。这里我还是采取使用情形,然后画出简要 流程图,最后再是源码分析的方式来介绍本文所要涉及的知识点。
2.1 日常开发中注入对象的方式
「情形一」:通过 @Autowired 注解对象的方式
@Servicepublic class DemoServiceTwo { @Autowired DemoServiceThree demoServiceThree;}
「情形二」:通过 @Autowired 注解构造器的方式
@Servicepublic class DemoServiceTwo { DemoServiceOne demoServiceOne; @Autowired public DemoServiceTwo(DemoServiceOne demoServiceOne){ this.demoServiceOne = demoServiceOne; } }
「情形三」:通过 @Resource 注解对象的方式
@Servicepublic class DemoServiceTwo { @Resource DemoServiceOne demoServiceOne;}
「情形四」:通过 @Autowired 注解方法的方式
@Servicepublic class DemoServiceTwo { DemoServiceOne demoServiceOne; @Autowired public void prepare(DemoServiceOne demoServiceOne){ this.demoServiceOne = demoServiceOne; }}
上述的四种方式是我们在日常开发中经常用到的注入对象的方式。这四种方式,在 Spring 对应不同的处理逻辑。
2.2 对象之间依赖关系处理流程
对象之间依赖关系处理流程
- 上图中描述了前面 「2.1」 中所介绍的四种情形的处理,其中蓝色线条所表示的是 @Resource注解的处理过程。
- 红色线条表示 @Autowired注解的处理过程,与之对应的有拆分成三种子情况 AutowiredFieldElement 表示注解属性的情况AutowiredMethodElement 表示注解方法的情况绿颜色的线条表示注解在构造方法上的情况
通过上述流程图,我从中找到了以下几点。通过以下几点我们也可以区分 @Resource 和 @Autowired。
- 两种注解的处理方式都是通过后置处理器来完成处理的,getBeanPostProcessors() 在我们不做任何扩展的情况下,Spring 中只有五个。如有忘记请查看:「容器初始化先发五虎」;
- 对于 @Resource 的处理是通过 CommonAnnotationBeanPostProcessor 来完成的。
- 对于 @Autowired 的处理是通过 AutowiredAnnotationBeanPostProcessor来处理的。
- 对于 @Autowired 注解构造器的方式,获取到被注解元素为 null 则直接返回。完成 populateBean() 的过程。
- 对于剩下的情形,处理思路一致,都是先获取到被注入的对象,然后将维护对象属性之间的关系。
- 重点突出一下 getBean() 这里还是我们熟悉的 getBean()...
- field.set() 维护对象之间的依赖关系。
3、源码分析
源码分析首当其冲的就是方法入口,在对象的包装 BeanWrapper 创建完成之后,populateBean()来处理对象之间的依赖关系:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { if (bw == null) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { // Skip property population phase for null instance. // 没有任何属性需要填充 return; } } if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; /** * InstantiationAwareBeanPostProcessor 的 postProcessAfterInstantiation() 方法的应用 * 可以控制程序是否进行属性填充 */ if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } } /** * 取得BeanDefinition 中设置的 Property值, * 这些property来自对BeanDefinition的解析, * 具体的过程可以参看对载入个解析BeanDefinition的分析 * 这里是Spring 内部设置的属性值,一般不会设置 */ PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); /** * 处理自动装配,xml的方式可能会有配置自动装配类型的情况 * 或者通过 setAutowireMode() 的方式设置自动装配的模式 */ int resolvedAutowireMode = mbd.getResolvedAutowireMode(); // Spring 默认 既不是 byType 也不是 byName, 默认是null // 这里之所以做这个判断是因为某种特殊的场景下,会修改到自动注入的模型,所以需要做判断 if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); /** * byName 处理 * 通过反射从当前Bean中得到需要注入的属性名, * 然后使用这个属性名向容器申请与之同名的Bean, * 这样实际又触发了另一个Bean生成和依赖注入的过程 */ if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // byType 处理 if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } // 后置处理器已经初始化 boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); // 需要检查依赖 boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { if (pvs == null) { // 与构造方法的处理一样,对象有但对象里面的属性没有 pvs = mbd.getPropertyValues(); } for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // 通过后置处理器来完成处理 PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } } if (needsDepCheck) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } checkDependencies(beanName, mbd, filteredPds, pvs); } if (pvs != null) { // 对属性进行注入 applyPropertyValues(beanName, mbd, bw, pvs); }}
通过源码实现可以看出「自动装配」与「注解注入」的处理是有差别的。其中自动装配时通过属性判断来完成。注解注入是通过「后置处理器」来完成的。
不同的后置处理器的postProcessProperties()方法对应的是不同的处理逻辑。
4、@Autowired 注解属性
4.1 处理过程
首先,通过 findAutowiringMetadata() 方法获取被注入的元数据。以上述:「情形一」,为例:
然后,在inject()方法中做相应的处理:
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Collection checkedElements = this.checkedElements; // 获取被注入的元素 Collection elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { // 循环被注入的元素,调用 inject 方法 for (InjectedElement element : elementsToIterate) { if (logger.isTraceEnabled()) { logger.trace("Processing injected element of bean '" + beanName + "': " + element); } // 调用注入方法 element.inject(target, beanName, pvs); } } }
在这个方法中首先会检查注入的对象,这里需要指出,在 @Autowired 注解构造器的方式下,最终得到的 elementsToIterate 是空。
对于@Autowired注解的其他使用方式,最终都会调用 element.inject(target, beanName, pvs);
可以看出,这里区分了注解方法与注解属性这两种方式,在本文中,将以注解 属性的方式为例继续展开分析。
在 AutowiredFieldElement中类中的最终通过 beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter) 返回注入的对象,然后通过 field.set(bean, value);的方式来维护对象与属性之间的关系。
接下来将分析 resolveDependency()方法,通过该方法可以发现,最终调用的是 doResolveDependency()。看到了 doXXXX()的方法,就又到了 Spring 惯用的套路了,这里这方法就是真正做事的方式。下面就来看看在 doResolveDependency() 中做了什么事情...
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { Object shortcut = descriptor.resolveShortcut(this); if (shortcut != null) { return shortcut; } /** * 根据类型获取,@Autowired 默认根据type */ Class> type = descriptor.getDependencyType(); /** * 支持 @value 注解 */ Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { if (value instanceof String) { String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); try { /** 通过转换器将Bean的值转换为对应的type类型*/ return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor()); } catch (UnsupportedOperationException ex) { // A custom TypeConverter which does not support TypeDescriptor resolution... return (descriptor.getField() != null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); } } Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null) { return multipleBeans; } /** * 根据属性类型找到beanFactory中所有类型匹配的Bean * 返回值的结构为: * key:匹配的BeanName; * value:beanName 对应的实例化后的bean 通过 getBean(beanName)返回 */ Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { /** * 如果 autowire 的 require 属性为true * 找到的匹配项为空 则 抛出异常 */ if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; // 根据类型匹配到的数量大于 1个 if (matchingBeans.size() > 1) { // 确定自动注入的beanName autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans); } else { return null; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { // We have exactly one match. Map.Entry entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } if (autowiredBeanNames != null) { autowiredBeanNames.add(autowiredBeanName); } if (instanceCandidate instanceof Class) { //实例化对象 instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); } Object result = instanceCandidate; if (result instanceof NullBean) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } result = null; } if (!ClassUtils.isAssignableValue(type, result)) { throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass()); } return result; } finally { ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); }}
分析上述代码,可以发现其中主要做了如下的几件事:
①:获取注入对象的类型;
②:解析过程中对 @value 注解支持;
③:通过 findAutowireCandidates() 方法,根据属性类型找到 BeanFactory 中所有类型匹配的 Bean。存放在 Map中返回。在该方法中,会根据给定的类型获取所有 Bean 的名称作为 Map中的 key。
- 对类型匹配的 Bean 做相应的判断,如果大于 1 个,则通过 determineAutowireCandidate() 方法来确定注入 Bean的名称。 首先根据 @Primary 注解的对象来确定,如果有则返回;然后再通过 @Priority 注解的对象,如果有则返回。
- 如果等于 1 个,在返回 Map 中的 key 就是 beanName;
通过上述的描述发现 @Autowired 注解属性的方式先通过 byType 的方式获取对应类型的对象;当对应类型的对象大于 1 个时,通过 byName 的方式来确定。
④:最后 descriptor.resolveCandidate(autowiredBeanName, type, this); 通过 beanFactory.getBean(beanName); 获取注入的对象。
4.2 对象之间依赖关系的维护
在通过 beanFactory.resolveDependency() 方法获得依赖的对象之后,通过 registerDependentBeans() 方法来维护对象之间的依赖关系。
private void registerDependentBeans(@Nullable String beanName, Set autowiredBeanNames) { if (beanName != null) { for (String autowiredBeanName : autowiredBeanNames) { if (this.beanFactory != null && this.beanFactory.containsBean(autowiredBeanName)) { this.beanFactory.registerDependentBean(autowiredBeanName, beanName); } if (logger.isTraceEnabled()) { logger.trace("Autowiring by type from bean name '" + beanName + "' to bean named '" + autowiredBeanName + "'"); } } }}
上述代码中 for 循环所有 @Autowired 注入的属性的名称。判断容器中包含 BeanName 然后调用 this.beanFactory.registerDependentBean(autowiredBeanName, beanName)。Spring 通过下述的方法来维护了对象之间的依赖关系。
public void registerDependentBean(String beanName, String dependentBeanName) { String canonicalName = canonicalName(beanName); /** * dependentBeanMap中存储的是目前已经注册的依赖这个bean的所有bean, * 这里从这个集合中获取目前所有已经注册的依赖beanName的bean集合, * 然后看这个集合中是否包含dependentBeanName,即是否已经注册, * 如果包含则表示已经注册,则直接返回; * 否则,将bean依赖关系添加到两个map缓存即完成注册. */ synchronized (this.dependentBeanMap) { Set dependentBeans = this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8)); if (!dependentBeans.add(dependentBeanName)) { return; } } synchronized (this.dependenciesForBeanMap) { Set dependenciesForBean = this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8)); dependenciesForBean.add(canonicalName); } }
上述的代码中,有两个 Map。这里首先对这两个 Map稍加解释:
/** 指定的bean与目前已经注册的依赖这个指定的bean的所有依赖关系的缓存(我依赖的)*/private final Map> dependentBeanMap = new ConcurrentHashMap<>(64);/** 指定bean与目前已经注册的创建这个bean所需依赖的所有bean的依赖关系的缓存(依赖我的) */private final Map> dependenciesForBeanMap = new ConcurrentHashMap<>(64);复制代码
在上述的方法中,就是通过上述两个 Map 维护了对象间依赖与被依赖的关系,详细看下图
当前的 Bean 是 demoServiceTwo 注入的对象是 demoServiceThree。结合这个可以对上面的 Map 有一个更直观的理解。
最后提醒一点,这两个 Map 中保存容器中所有对象之间的关系,直到容器被销毁的时候删除掉。
5、@Autowired 注解构造器的处理方式
前面介绍过,通过 @Autowired 注解构造器的方式,在 populateBean() 方法中,通过后置处理器来处理时,获取到的被注入的元素为空,因此直接返回。也就是说这里并没有维护对象之间的依赖关系。但是对象和属性之间的依赖关系,在通过构造器实例化对象的时候已经依赖好了。我自己的理解就是 java 对象和对象属性之间的关系已经有了。
6、 总结
本文主要介绍了 Spring 中对象之间依赖关系的处理流程。通过流程图的方式,粗略的看了一下 @Resource 和 @Autowired 注解处理的过程。
本文详细介绍了 @Autowired 注解属性的处理过程、java 对象与属性关系的维护以及 Spring 对象之间的依赖关系的维护。
简单介绍了 @Autowired 注解构造器的处理构成。
关于 @Resource 注解与 @Autowired 注解方法的处理过程,后面有机会再详细分析。
作者:零星
链接:https://juejin.im/post/6854573218277048328
来源:掘金