spring中自動注入field的繼承問題

git: github.com/fw103699437…html

先說問題

隔離在家,閒的蛋疼,正好有空梳理一下項目裏的代碼,就是所謂的重構羅,可是呢,我很顯然沒有相關的經驗和知識,就是想把一些java裏的繼承之類的、設計模式之類的給用上。
可是因爲spring的存在,有些東西沒法很方便的弄到spring中來,不少時候須要驗證。
例如一個接口的多個實現,有一些重複代碼,確定是要提出來,我將他提到一個抽象類裏。
那麼問題來了,抽象類沒法實例化,那麼也就沒法放入到spring容器中,可是抽象類中又要注入一個Bean。
按照個人理解java

  • 放入Spring容器中的bean的屬性(field)能夠@Autowired,這個是沒有爭議的。
  • 沒有放入Spring容器中的類,天然沒法@Autowired,由於spring對這個類視而不見了。
  • 那麼,沒有被顯式放入spring(即加上@Component等)的父類的field,能不能被@Autowired呢?

面向百度編程

百度獲得結果以下:git

  • www.cnblogs.com/zhjh256/p/9…
    文章結論:只要父類要注入的屬性是protected保護級別便可 (文中父類屬性原先爲default)
  • www.cnblogs.com/walson/p/38…
    文章結論:super.set方法 / 父類屬性由private爲protected,利用子類的自動注入方法(加上自動注入註解)來設置子類中的屬性
  • www.iteye.com/blog/arthur…
    文章結論:在抽象類中把屬性聲明爲protected並使用註解方式注入屬性

說實話,有點get不到點,protected還有這麼神奇的功效?github

本身實踐

咱們寫一個簡單的例子好了,如圖所示:
spring

git: github.com/fw103699437…

@RestController
public class Controller {
    @Qualifier("impl1")
// @Qualifier("impl2")
    @Autowired
    Service service;

    public void save(){
        service.save();
    }
}
複製代碼

通過實驗能夠發現,用@Qualifier指定不一樣實現時,子類的bean中會注入Dao,不會有問題。編程

斷點進源碼

getBean(beanName=controller)
而後注入屬性 populateBean(beanName=controller)
爲Controller注入beanName=imp1的bean設計模式

getBean(beanName=imp1) 利用AutowiredAnnotationBeanPostProcessor爲imp1尋找須要被注入的屬性post

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
			return InjectionMetadata.EMPTY;
		}

		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
		Class<?> targetClass = clazz;

		do {
			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

            //這裏找到須要注入的field
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
				MergedAnnotation<?> ann = findAutowiredAnnotation(field);
				if (ann != null) {
					if (Modifier.isStatic(field.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static fields: " + field);
						}
						return;
					}
					boolean required = determineRequiredStatus(ann);
					currElements.add(new AutowiredFieldElement(field, required));
				}
			});

			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
				if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
					return;
				}
				MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					if (Modifier.isStatic(method.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static methods: " + method);
						}
						return;
					}
					if (method.getParameterCount() == 0) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation should only be used on methods with parameters: " +
									method);
						}
					}
					boolean required = determineRequiredStatus(ann);
					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);

		return InjectionMetadata.forElements(elements, clazz);
	}
複製代碼

其中while (targetClass != null && targetClass != Object.class)這一句,先是在 ServiceImp1中須要要注入的field,沒有找到,而後在父類AbstractService中尋找,利用getDeclaredFields(clazz)找到了field(也就是dao)ui

而後根據findAutowiredAnnotation,找到了field上的註解@Autowired,肯定要注入父類中的Daothis

@Nullable
	private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
		MergedAnnotations annotations = MergedAnnotations.from(ao);
		for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
			MergedAnnotation<?> annotation = annotations.get(type);
			if (annotation.isPresent()) {
				return annotation;
			}
		}
		return null;
	}
複製代碼

具體注入過程就不分析了。

結論

子類交給spring容器,父類不交給spring容器,父類中的field也能夠完成自動注入

總結

1:百度的結果中所說的protected,實際上是由private(或者default)改過來的,目的只是讓子類可以訪問到而已。(第一篇文章中從default改成protected,其實子類也在同一個包下時default就夠用。所以其實仍是有那麼一絲誤導,沒有說的太透徹。)


百度獲得的結果,他們爲何都改爲了protected?**其實只是爲了嚴謹,protected是保證子類能獲取到父類field的最小權限而已。**結果讓我這種菜鳥花了眼,哈哈哈哈。 2:好好的複習了一下private:
當一個子類被實例化的時候,默認會先調用父類的構造方法對父類進行初始化,即在內存中建立一個父類對象,而後再父類對象的外部放上子類獨有的屬性,二者合起來成爲一個子類的對象。 因此:子類繼承了父類的全部屬性和方法或子類擁有父類的全部屬性和方法是對的,只不過父類的私有屬性和方法,子類是沒法直接訪問到的。即只是擁有,可是沒法使用。 3:IDEA debug的Drop Frame真香

一點副產物

其中MergedBeanDefinitionPostProcessor的接口postProcessMergedBeanDefinition的實現,負責找到須要注入的屬性
InstantiationAwareBeanPostProcessor的接口postProcessProperties負責真正注入。

BeanPostProcessor真的是一應俱全

相關文章
相關標籤/搜索