【spring】依赖注入之@Autowired寻找注入点

@Autowired寻找注入点

  • 本文源码基于spring-framework-5.3.10。
  • 在属性注入的时候,spring需要找到那些属性需要注入!
  • 源码位于:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(RootBeanDefinition, Class<?>, String)
  • 他在Bean的生命周期的合并BeanDefinition中调用。

@Autowired注解可以写的位置

  • 属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个
  • 构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
  • set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个

@Autowired寻找注入点原理

  • 遍历当前类的所有的属性字段Field
  • 查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段是一个注入点
    如果字段是static的,则不进行注入
  • 获取@Autowired中的required属性的值
  • 将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到currElements集合中。
    遍历当前类的所有方法Method
  • 判断当前Method是否是桥接方法,如果是找到原方法
  • 查看方法上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该方法是一个注入点
  • 如果方法是static的,则不进行注入
  • 获取@Autowired中的required属性的值
  • 将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到currElements集合中。
  • 遍历完当前类的字段和方法后,将遍历父类的,直到没有父类。
  • 最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对于的注入点集合对象,并缓存。

@Autowired寻找注入点:postProcessMergedBeanDefinition源码分析

/**
 * 在合并BeanDefinition的时候调用
 */
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
	// 寻找注入点
	InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
	// 检查配置
	metadata.checkConfigMembers(beanDefinition);
}

/**
 * 寻找注入点的具体逻辑
 */
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
	// Fall back to class name as cache key, for backwards compatibility with custom callers.
	// 得到当前Bean的名字
	String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
	// Quick check on the concurrent map first, with minimal locking.
	// metadata:可以简单理解为一个注入点的集合
	InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);

	// 读取到了类的元数据之后,进行对字段、普通方法、构造方法的处理
	if (InjectionMetadata.needsRefresh(metadata, clazz)) {
		synchronized (this.injectionMetadataCache) {
			// metadata:可以简单理解为一个注入点的集合
			metadata = this.injectionMetadataCache.get(cacheKey);
			// 双重检测在不在缓存,没有变化
			if (InjectionMetadata.needsRefresh(metadata, clazz)) {
				// 清理一些要跳过的自己设置的值?已经注入的值
				if (metadata != null) {
					metadata.clear(pvs);
				}
				// 解析注入点
				metadata = buildAutowiringMetadata(clazz);
				// 将当前Bean的注入信息缓存
				this.injectionMetadataCache.put(cacheKey, metadata);
			}
		}
	}
	return metadata;
}

/**
 * 寻找解析注入点
 */
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
	// 如果一个Bean的是java.开头的类,那么则根本不需要进行依赖注入
	if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
		return InjectionMetadata.EMPTY;
	}
	
	// 定义一个存放注入信息的集合:包含每一个注入点
	List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
	// 得到当前Bean的class对象
	Class<?> targetClass = clazz;

	// 这里的循环主要作用是子类找完找父类,直到没有父类!
	do {
		// 定义一个存放注入信息的集合:包含当前一个字段活方法的信息
		final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

		// 遍历targetClass中的所有Field,执行第二个参数的内容
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
			// 得到field上是否存在@Autowired、@Value、@Inject中的其中一个
			MergedAnnotation<?> ann = findAutowiredAnnotation(field);
			if (ann != null) {
				// static filed不是注入点,不会进行自动注入。static属于类,原型Bean在创建的时候会有属性值覆盖问题
				if (Modifier.isStatic(field.getModifiers())) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation is not supported on static fields: " + field);
					}
					return;
				}

				// 判断是否是必须的,简单理解就是@autowired(required = true)
				boolean required = determineRequiredStatus(ann);
				// 注入点加入到上面的集合中
				currElements.add(new AutowiredFieldElement(field, required));
			}
		});

		// 遍历targetClass中的所有Method
		ReflectionUtils.doWithLocalMethods(targetClass, method -> {
			// 桥接方法,找到真正的方法
			Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
			if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
				return;
			}
			// method上是否存在@Autowired、@Value、@Inject中的其中一个
			MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
			if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
				// static method不是注入点,不会进行自动注入。static属于类,原型Bean在创建的时候会有属性值覆盖问题
				if (Modifier.isStatic(method.getModifiers())) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation is not supported on static methods: " + method);
					}
					return;
				}
				// set方法最好有入参,无参数会打印日志。这里也就是说,提供一个s方法,标记位@Autowired,他会在依赖注入的时候(初始化前)执行!
				if (method.getParameterCount() == 0) {
					if (logger.isInfoEnabled()) {
						logger.info("Autowired annotation should only be used on methods with parameters: " +
								method);
					}
				}
				// 判断是否是必须的,简单理解就是@autowired(required = true)
				boolean required = determineRequiredStatus(ann);
				// 属性描述器中存在当前的属性(set方法或者get方法的名称)
				PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
				// 注入点加入到上面的集合中
				currElements.add(new AutowiredMethodElement(method, required, pd));
			}
		});

		// 头插法的方式将当前类的所有注入点放入集合中
		elements.addAll(0, currElements);
		// 得到他的父类,继续循环,直到没有父类!
		targetClass = targetClass.getSuperclass();
	}
	while (targetClass != null && targetClass != Object.class);

	// 生成InjectionMetadata对象
	return InjectionMetadata.forElements(elements, clazz);
}

一个字段能不能成为注入点

private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
	// 得到当前字段上所有的注解
	MergedAnnotations annotations = MergedAnnotations.from(ao);
	// 包含符合注入点的条件直接返回(@Autowired、@Value、@Inject),否则返回null
	for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
		MergedAnnotation<?> annotation = annotations.get(type);
		if (annotation.isPresent()) {
			return annotation;
		}
	}
	return null;
}

有什么样的标记会成为注入点?

public AutowiredAnnotationBeanPostProcessor() {
	// @Autowired会成为注入点
	this.autowiredAnnotationTypes.add(Autowired.class);
	// @Value会成为注入点
	this.autowiredAnnotationTypes.add(Value.class);
	try {
		// @Inject也会成为注入点
		this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
				ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
		logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
	}
	catch (ClassNotFoundException ex) {
		// JSR-330 API not available - simply skip.
	}
}

结束语

  • 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
  • 关注公众号,可以让你对MySQL、并发编程、spring源码有深入的了解!
  • 关注公众号,后续持续高效的学习JVM!
  • 这个公众号,无广告!!!每日更新!!!
    作者公众号.jpg
posted @ 2022-02-22 23:46  程序java圈  阅读(132)  评论(0编辑  收藏  举报