Spring源码之自动装配
我们使用Spring开发过程中经常会用到Autowired注解注入依赖的bean,这部分也是面试的热点问题之一。今天咱们一起来深入研究下自动注入的背后实现原理。首先上一个例子,如下所示:
@RestController public class TestController { @Autowired List<ICheckRuleService> checkRuleService; @RequestMapping("/test") public void test(){ checkRuleService.forEach(x->x.valid()); } }
populateBean
Autowired是怎么实现自动注入的呢,今天我们来通过源码分析一下。当Spring创建 TestController Bean时,会调用AbstractBeanFactory#doGetBean(如果对Spring创建Bean流程不熟的读者,可以给我留言,后面考虑是否写个IOC系列),doGetBean里面会调用doCreateBean()方法去创建Bean,创建Bean之后,会对Bean进行填充
try { this.populateBean(beanName, mbd, instanceWrapper); exposedObject = this.initializeBean(beanName, exposedObject, mbd); }
populateBean 里有这样一段代码,看起来是处理Autowired的,分别是autowireByName 和 autowireByType
PropertyValues pvs = mbd.hasPropertyValues() ? mbd.getPropertyValues() : null; if (mbd.getResolvedAutowireMode() == 1 || mbd.getResolvedAutowireMode() == 2) { MutablePropertyValues newPvs = new MutablePropertyValues((PropertyValues)pvs); if (mbd.getResolvedAutowireMode() == 1) { this.autowireByName(beanName, mbd, bw, newPvs); } if (mbd.getResolvedAutowireMode() == 2) { this.autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; }
我们来验证一下,通过断点调试我们发现并不会进入if里,所以自动注入并不是这里实现的。那这里有什么用呢,先放一放,后面再说。
AutowiredAnnotationBeanPostProcessor
那么到底是哪里注入进去的呢?我们继续往下看,在这段代码下方有个BeanPostProcessor的逻辑,通过断点我们发现有个AutowiredAnnotationBeanPostProcessor 的后置处理器,当这个BeanPostProcessor执行完 postProcessPropertyValues方法后,testController的checkRuleService 属性就有了值了,说明属性值注入肯定和 AutowiredAnnotationBeanPostProcessor 有关,我们跟进去看一下
进入AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues 方法里,里面主要有两部分逻辑
-
首先看到一段 findAutowiringMetadata 的逻辑,根据方法名称知道是获取当前bean的注入元信息
-
调用 metadata.inject 注入属性
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException { InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); return pvs; } catch (BeanCreationException var7) { throw var7; } catch (Throwable var8) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var8); } }
我们先来看第一部分:findAutowiringMetadata
我们进入findAutowiringMetadata,看下它的逻辑,先从 injectionMetadataCache 缓存里取,如果取不到值,则调用buildAutowiringMetadata 构建 InjectionMetadata ,构建成功后设置到缓存里。
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) { String cacheKey = StringUtils.hasLength(beanName) ? beanName : clazz.getName(); InjectionMetadata metadata = (InjectionMetadata)this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized(this.injectionMetadataCache) { metadata = (InjectionMetadata)this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { metadata.clear(pvs); } metadata = this.buildAutowiringMetadata(clazz); this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; }
我们来看下 buildAutowiringMetadata,继续跟进去,源码如下:
里面是通过当前Bean的Class反射获取 Field 和 Method ,然后对 Field 和 Method 分别调 findAutowiredAnnotation 方法获取自动注入的注解,然后根据注解类型是否required构建不同类型的InjectedElement。
-
AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement:
boolean required = this.determineRequiredStatus(ann); currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
-
AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement:
boolean required = this.determineRequiredStatus(ann); PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement(method, required, pd));
补充:通过AutowiredAnnotationBeanPostProcessor 构造函数我们知道,自动注入处理的是被 @Autowired 和 @Value 这两个注解标注的属性(Field)或方法(Method):
public AutowiredAnnotationBeanPostProcessor() { this.autowiredAnnotationTypes.add(Autowired.class); this.autowiredAnnotationTypes.add(Value.class); //......
到这里,需要注入的元数据信息就已经构建完成了,接下来就要到注入部分了。来看下 postProcessPropertyValues 的第二部分。
再看第二部分:metadata.inject
前面获取到了需要注入的元数据信息,接下来是元数据 inject 的实现,继续跟进去,里面是一个for循环,循环调用了element的inject方法
if (!((Collection)elementsToIterate).isEmpty()) { for(Iterator var6 = ((Collection)elementsToIterate).iterator(); var6.hasNext(); element.inject(target, beanName, pvs)) { element = (InjectionMetadata.InjectedElement)var6.next(); if (logger.isDebugEnabled()) { logger.debug("Processing injected element of bean '" + beanName + "': " + element); } } }
我们断点调试进去,发现element的真实类型是AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement,而当前element 真实类型是 TestController.checkRuleService 的集合。
我们进入AutowiredFieldElement#inject方法,首先尝试从缓存里拿当前Field的值,肯定拿不到,所以走的是else分支,else分支里从beanFactory里解析当前Field属性值
value = AutowiredAnnotationBeanPostProcessor.this.beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
继续跟进去,发现其实调用的 doResolveDependency 方法
越来越接近真相了,不要着急,继续跟进去
发现一个类型为Object的 multipleBeans ,结果返回的也是这个Object,我们大胆猜测这个Object就是我们需要注入的List属性,继续跟进去验证一下:
我们看一下 Collection 分支的源码
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { elementType = descriptor.getResolvableType().asCollection().resolveGeneric(new int[0]); if (elementType == null) { return null; } else { Map<String, Object> matchingBeans = this.findAutowireCandidates(beanName, elementType, new DefaultListableBeanFactory.MultiElementDescriptor(descriptor)); if (matchingBeans.isEmpty()) { return null; } else { if (autowiredBeanNames != null) { autowiredBeanNames.addAll(matchingBeans.keySet()); } TypeConverter converter = typeConverter != null ? typeConverter : this.getTypeConverter(); Object result = converter.convertIfNecessary(matchingBeans.values(), type); if (this.getDependencyComparator() != null && result instanceof List) { ((List)result).sort(this.adaptDependencyComparator(matchingBeans)); } return result; } } }
里面是调用了 findAutowireCandidates 来获取Bean,findAutowireCandidates 内部会获取到依赖的BeanNames,然后根据beanName 循环调用beanFactory#getBean 获取需要注入的bean
this.findAutowireCandidates(beanName,elementType,new DefaultListableBeanFactory.MultiElementDescriptor(descriptor))
beanFactory#getBean方法,最终会调用 AbstractBeanFactory#doGetBean,获取到需要装配进去的属性bean。
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) throws BeansException { return beanFactory.getBean(beanName); }
当所有的循环执行完毕,就获取到了 multipleBeans ,验证了前面的猜测。真是太不容易,赶紧设置缓存