天天看點

【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!
  • 這個公衆号,無廣告!!!每日更新!!!