Spring component-scan源码分析(三) -- @Autowired等注解的处理

相关笔记:
Spring component-scan源码分析(一) – XML解析
Spring component-scan源码分析(二) – @Configuration注解处理

本篇文章分析注入注解(@Autowired、@Value等)的处理,其逻辑在AutowiredAnnotationBeanPostProcessor类中。
Spring component-scan源码分析(三) -- @Autowired等注解的处理_第1张图片

可以看到AutowiredAnnotationBeanPostProcessor类实现了一些增强处理的接口,其中关键方法是postProcessMergedBeanDefinition和postProcessProperties方法。

  • postProcessMergedBeanDefinition是MergedBeanDefinitionPostProcessor接口的方法,Spring是在创建了对象实例后还没填充注入属性前调用的;
  • postProcessProperties是InstantiationAwareBeanPostProcessor接口的方法,Spring是在创建了对象实例后,整理收集完实例的注入属性描述(PropertyValues类)后调用的;

总之调用顺序postProcessMergedBeanDefinition -> postProcessProperties

1 postProcessMergedBeanDefinition方法

	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		//1.1 对bean把带有注入注解的字段、方法封装起来,最后封装成一个InjectionMetadata对象
		InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
		//1.2 把需要注入的Member(java反射包下的类,描述Field、Method、Constructor的信息)记录在beanDefinition
		metadata.checkConfigMembers(beanDefinition);
	}

1.1 对bean把带有注入注解的字段、方法封装起来,最后封装成一个InjectionMetadata对象

	private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
		//以类名或bean名作为缓存的key
		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
		//获取缓存
		InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
		//用了double check
		if (InjectionMetadata.needsRefresh(metadata, clazz)) {
			synchronized (this.injectionMetadataCache) {
				metadata = this.injectionMetadataCache.get(cacheKey);
				if (InjectionMetadata.needsRefresh(metadata, clazz)) {
					if (metadata != null) {
						//clazz改变了才进入这里
						//从pvs中清理重复的属性
						metadata.clear(pvs);
					}
					//【标记1】对当前类进行解析所有要注入的的属性
					metadata = buildAutowiringMetadata(clazz);
					//缓存
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}

【标记1】对当前类进行解析所有要注入的的属性

	private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
		Class<?> targetClass = clazz;
		do {
			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
			//遍历类的成员
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
				//拿到@Autowired、@Value、@Inject(JSR-330的)注解
				AnnotationAttributes ann = findAutowiredAnnotation(field);
				if (ann != null) {
					//不支持注入的静态的变量
					if (Modifier.isStatic(field.getModifiers())) {
						...省略log
						return;
					}
					//拿到注解的required属性
					boolean required = determineRequiredStatus(ann);
					//封装成AutowiredFieldElement
					currElements.add(new AutowiredFieldElement(field, required));
				}
			});
			//遍历类的方法
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				//拿到实际的泛型方法
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
				//判断是不是实际的泛型方法
				if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
					return;
				}
				//拿到@Autowired注解
				AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					//不支持静态方法注入
					if (Modifier.isStatic(method.getModifiers())) {
						...省略log
						return;
					}
					//没参数的话没什么意义啊
					if (method.getParameterCount() == 0) {
						...省略log
					}
					//拿到注解的required属性
					boolean required = determineRequiredStatus(ann);
					//这里规定了要符合javaBean规范(PropertyDescriptor是java.beans包下的类,可以拿到setter、getter方法)
					PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
					//封装成AutowiredMethodElement
					currElements.add(new AutowiredMethodElement(method, required, pd));
				}
			});

			elements.addAll(0, currElements);
			//继续解析父类
			targetClass = targetClass.getSuperclass();
		}
		while (targetClass != null && targetClass != Object.class);
		//封装成InjectionMetadata
		return new InjectionMetadata(clazz, elements);
	}

可以看到该方法是对类中需要注入的字段、方法分别封装成AutowiredFieldElement、AutowiredMethodElement,然后统一存入集合,最后封装成一个InjectionMetadata对象。

1.2 把需要注入的Member(java反射包下的类,描述Field、Method、Constructor的信息)记录在beanDefinition

	public void checkConfigMembers(RootBeanDefinition beanDefinition) {
		Set<InjectedElement> checkedElements = new LinkedHashSet<>(this.injectedElements.size());
		for (InjectedElement element : this.injectedElements) {
			Member member = element.getMember();
			////这里是为了防止重复?
			if (!beanDefinition.isExternallyManagedConfigMember(member)) {
				beanDefinition.registerExternallyManagedConfigMember(member);
				checkedElements.add(element);
				if (logger.isTraceEnabled()) {
					logger.trace("Registered injected element on class [" + this.targetClass.getName() + "]: " + element);
				}
			}
		}
		//记录以检查过的
		this.checkedElements = checkedElements;
	}

该方法是属于InjectionMetadata类的,上面已经提过,类中需要注入的字段、方法会被封装后传进来,injectedElements就是装载它们的集合。而这个方法做的是,利用beanDefinition中的externallyManagedConfigMembers(一个Set),来判断是否已存在相同的Member,没有就加入checkedElements。

2 postProcessProperties方法

	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		//在postProcessMergedBeanDefinition已经解析过并缓存了,直接可以拿到
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		try {
			//注入
			metadata.inject(bean, beanName, pvs);
		}
		catch (BeanCreationException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
		}
		return pvs;
	}

可以看到上面又调用了findAutowiringMetadata方法,然后调用InjectionMetadata类中的inject方法直接进行注入处理。

	public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Collection<InjectedElement> checkedElements = this.checkedElements;
		//checkedElements为空就遍历injectedElements
		Collection<InjectedElement> elementsToIterate =
				(checkedElements != null ? checkedElements : this.injectedElements);
		if (!elementsToIterate.isEmpty()) {
			//有Field和Method两种注入方式
			for (InjectedElement element : elementsToIterate) {
				...省略log
				element.inject(target, beanName, pvs);
			}
		}
	}

该方法会去checkedElements或injectedElements进行遍历处理待注入字段或方法。上面提到,字段的注入会被封装成AutowiredFieldElement,方法的注入会被封装成AutowiredMethodElement,它们都是InjectedElement类型。

先看看AutowiredFieldElement#inject

	protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
			Field field = (Field) this.member;
			Object value;
			if (this.cached) {
				//从缓存中取
				value = resolvedCachedArgument(beanName, this.cachedFieldValue);
			}
			else {
				DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
				desc.setContainingClass(bean.getClass());
				Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
				Assert.state(beanFactory != null, "No BeanFactory available");
				TypeConverter typeConverter = beanFactory.getTypeConverter();
				try {
					//拿到要注入的值
					value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
				}
				catch (BeansException ex) {
					throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
				}
				synchronized (this) {
					if (!this.cached) {
						if (value != null || this.required) {
							this.cachedFieldValue = desc;
							//想容器注册依赖的bean
							registerDependentBeans(beanName, autowiredBeanNames);
							if (autowiredBeanNames.size() == 1) {
								String autowiredBeanName = autowiredBeanNames.iterator().next();
								if (beanFactory.containsBean(autowiredBeanName) &&
										beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
									//封装成ShortcutDependencyDescriptor进行缓存
									this.cachedFieldValue = new ShortcutDependencyDescriptor(
											desc, autowiredBeanName, field.getType());
								}
							}
						}
						else {
							this.cachedFieldValue = null;
						}
						this.cached = true;
					}
				}
			}
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				//反射赋值
				field.set(bean, value);
			}
		}
	}

该方法主要逻辑是从容器beanFactory获得相应的bean,然后反射赋值

AutowiredMethodElement#inject的逻辑和AutowiredFieldElement的差不多,只不过变成了一个或多个bean的处理。主要逻辑还是从容器beanFactory中获取相应的bean,最终反射调用方法。

总结

注入注解的处理时机是在Spring创建bean后还没进行属性填充之前,通过从beanFactory容器中获取相应的bean,最后反射赋值。

你可能感兴趣的:(Spring源码学习)