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




先說問題

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

  • 放入Spring容器中的bean的屬性(field)可以@Autowired,這個是沒有爭議的。
  • 沒有放入Spring容器中的類,自然無法@Autowired,因為spring對這個類視而不見了。
  • 那麼,沒有被顯式放入spring(即加上@Component等)的父類的field,能不能被@Autowired呢?
  • 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還有這麼神奇的功效?

自己實踐

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

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

<code> git: github.com/fw103699437…/<code>
<code>@RestControllerpublic class Controller {    @Qualifier("impl1")//    @Qualifier("impl2")    @Autowired    Service service;    public void save(){        service.save();    }}/<code>

經過實驗可以發現,用@Qualifier指定不同實現時,子類的bean中會注入Dao,不會有問題。

斷點進源碼

getBean(beanName=controller) 然後注入屬性 populateBean(beanName=controller) 為Controller注入beanName=imp1的bean

getBean(beanName=imp1) 利用AutowiredAnnotationBeanPostProcessor為imp1尋找需要被注入的屬性

<code>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<>();            //這裡找到需要注入的fieldReflectionUtils.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);}/<injectionmetadata.injectedelement>/<injectionmetadata.injectedelement>/<code>

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

然後根據findAutowiredAnnotation,找到了field上的註解@Autowired,確定要注入父類中的Dao

<code>@Nullableprivate 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;}/<code>

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

結論

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

總結

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

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


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


一點副產物


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

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

BeanPostProcessor真的是包羅萬象


作者:Wayne_Kdl
鏈接:https://juejin.im/post/5e3adbe0e51d4526d43f229c


分享到:


相關文章: