<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          @Autowired 注解是如何實現(xiàn)的?

          共 8993字,需瀏覽 18分鐘

           ·

          2021-08-03 22:11

          前言

          使用spring開發(fā)時,進行配置主要有兩種方式,一是xml的方式,二是java config的方式。

          spring技術(shù)自身也在不斷的發(fā)展和改變,從當前springboot的火熱程度來看,java config的應(yīng)用是越來越廣泛了,在使用java config的過程當中,我們不可避免的會有各種各樣的注解打交道,其中,我們使用最多的注解應(yīng)該就是@Autowired注解了。這個注解的功能就是為我們注入一個定義好的bean。

          那么,這個注解除了我們常用的屬性注入方式之外還有哪些使用方式呢?它在代碼層面又是怎么實現(xiàn)的呢?這是本篇文章著重想討論的問題。

          @Autowired注解用法

          在分析這個注解的實現(xiàn)原理之前,我們不妨先來回顧一下@Autowired注解的用法。

          將@Autowired注解應(yīng)用于構(gòu)造函數(shù),如以下示例所示

          public class MovieRecommender {
           
              private final CustomerPreferenceDao customerPreferenceDao;
           
              @Autowired
              public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
                  this.customerPreferenceDao = customerPreferenceDao;
              }
           
              // ...
          }

          將@Autowired注釋應(yīng)用于setter方法

          public class SimpleMovieLister {
           
              private MovieFinder movieFinder;
           
              @Autowired
              public void setMovieFinder(MovieFinder movieFinder) {
                  this.movieFinder = movieFinder;
              }
           
              // ...
          }

          將@Autowired注釋應(yīng)用于具有任意名稱和多個參數(shù)的方法

          public class MovieRecommender {
           
              private MovieCatalog movieCatalog;
           
              private CustomerPreferenceDao customerPreferenceDao;
           
              @Autowired
              public void prepare(MovieCatalog movieCatalog,
                      CustomerPreferenceDao customerPreferenceDao) {
                  this.movieCatalog = movieCatalog;
                  this.customerPreferenceDao = customerPreferenceDao;
              }
           
              // ...
          }

          您也可以將@Autowired應(yīng)用于字段,或者將其與構(gòu)造函數(shù)混合,如以下示例所示

          public class MovieRecommender {
           
              private final CustomerPreferenceDao customerPreferenceDao;
           
              @Autowired
              private MovieCatalog movieCatalog;
           
              @Autowired
              public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
                  this.customerPreferenceDao = customerPreferenceDao;
              }
           
              // ...
          }

          直接應(yīng)用于字段是我們使用的最多的一種方式,但是使用構(gòu)造方法注入從代碼層面卻是更加好的。除此之外,還有以下不太常見的幾種方式

          將@Autowired注釋添加到需要該類型數(shù)組的字段或方法,則spring會從ApplicationContext中搜尋符合指定類型的所有bean,如以下示例所示:

          public class MovieRecommender {
           
              @Autowired
              private MovieCatalog[] movieCatalogs;
           
              // ...
          }

          數(shù)組可以,我們可以馬上舉一反三,那容器也可以嗎,答案是肯定的,下面是set以及map的例子:

          public class MovieRecommender {
           
              private Set<MovieCatalog> movieCatalogs;
           
              @Autowired
              public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
                  this.movieCatalogs = movieCatalogs;
              }
           
              // ...
          }
          public class MovieRecommender {
           
              private Map<String, MovieCatalog> movieCatalogs;
           
              @Autowired
              public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
                  this.movieCatalogs = movieCatalogs;
              }
           
              // ...
          }

          以上就是@Autowired注解的主要使用方式,經(jīng)常使用spring的話應(yīng)該對其中常用的幾種不會感到陌生。

          @Autowired注解的作用到底是什么

          @Autowired這個注解我們經(jīng)常在使用,現(xiàn)在,我想問的是,它的作用到底是什么呢?

          首先,我們從所屬范圍來看,事實上這個注解是屬于spring的容器配置的一個注解,與它同屬容器配置的注解還有:@Required,@Primary, @Qualifier等等。因此@Autowired注解是一個用于容器(container)配置的注解。

          其次,我們可以直接從字面意思來看,@autowired注解來源于英文單詞autowire,這個單詞的意思是自動裝配的意思。自動裝配又是什么意思?這個詞語本來的意思是指的一些工業(yè)上的用機器代替人口,自動將一些需要完成的組裝任務(wù),或者別的一些任務(wù)完成。而在spring的世界當中,自動裝配指的就是使用將Spring容器中的bean自動的和我們需要這個bean的類組裝在一起。

          因此,筆者個人對這個注解的作用下的定義就是:將Spring容器中的bean自動的和我們需要這個bean的類組裝在一起協(xié)同使用。

          接下來,我們就來看一下這個注解背后到底做了些什么工作。

          @Autowired注解是如何實現(xiàn)的

          事實上,要回答這個問題必須先弄明白的是java是如何支持注解這樣一個功能的。

          java的注解實現(xiàn)的核心技術(shù)是反射,讓我們通過一些例子以及自己實現(xiàn)一個注解來理解它工作的原理。Java 核心技術(shù)教程和示例源碼:https://github.com/javastacks/javastack

          例如注解@Override

          @Override注解的定義如下:

          @Target(ElementType.METHOD)
          @Retention(RetentionPolicy.SOURCE)
          public @interface Override {
          }

          @Override注解使用java官方提供的注解,它的定義里面并沒有任何的實現(xiàn)邏輯。注意,所有的注解幾乎都是這樣的,注解只能是被看作元數(shù)據(jù),它不包含任何業(yè)務(wù)邏輯。 注解更像是一個標簽,一個聲明,表面被注釋的這個地方,將具有某種特定的邏輯。

          那么,問題接踵而至,注解本身不包含任何邏輯,那么注解的功能是如何實現(xiàn)的呢?答案必然是別的某個地方對這個注解做了實現(xiàn)。以@Override注解為例,他的功能是重寫一個方法,而他的實現(xiàn)者就是JVM,java虛擬機,java虛擬機在字節(jié)碼層面實現(xiàn)了這個功能。46張PPT弄懂 JVM 性能調(diào)優(yōu)!這個分享給你,

          但是對于開發(fā)人員,虛擬機的實現(xiàn)是無法控制的東西,也不能用于自定義注解。所以,如果是我們自己想定義一個獨一無二的注解的話,則我們需要自己為注解寫一個實現(xiàn)邏輯,換言之,我們需要實現(xiàn)自己注解特定邏輯的功能。

          在自己寫注解之前我們有一些基礎(chǔ)知識需要掌握,那就是我們寫注解這個功能首先是需要java支持的,java在jdk5當中支持了這一功能,并且在java.lang.annotation包中提供了四個注解,僅用于編寫注解時使用,他們是:

          下面我們開始自己實現(xiàn)一個注解,注解僅支持 primitives, stringenumerations這三種類型。注解的所有屬性都定義為方法,也可以提供默認值。我們先實現(xiàn)一個最簡單的注解。

          import java.lang.annotation.ElementType;
          import java.lang.annotation.Retention;
          import java.lang.annotation.RetentionPolicy;
          import java.lang.annotation.Target;
           
          @Target(ElementType.METHOD)
          @Retention(RetentionPolicy.RUNTIME)
          public @interface SimpleAnnotation {
              String value();
          }

          上面這個注釋里面只定義了一個字符傳,它的目標注釋對象是方法,保留策略是在運行期間。下面我們定義一個方法來使用這個注解:

          public class UseAnnotation {
           
              @SimpleAnnotation("testStringValue")
              public void testMethod(){
                  //do something here
              }
           
          }

          我們在這里使用了這個注解,并把字符串賦值為:testStringValue,到這里,定義一個注解并使用它,我們就已經(jīng)全部完成。

          簡單的不敢相信。但是,細心一想的話,我們雖然寫了一個注解也用了它,可是它并沒有產(chǎn)生任何作用啊。也沒有對我們這里方法產(chǎn)生任何效果啊。是的現(xiàn)在確實是這樣的,原因在于我們前面提到的一點,我們還沒有為這個注解實現(xiàn)它的邏輯,現(xiàn)在我們就來為這個注解實現(xiàn)邏輯。

          應(yīng)該怎么做呢?我們不妨自己來想一想。首先,我想給標注了這個注解的方法或字段實現(xiàn)功能,我們必須得知道,到底有哪些方法,哪些字段使用了這個注解吧,因此,這里我們很容易想到,這里應(yīng)該會用到反射。

          其次,利用反射,我們利用反射拿到這樣目標之后,得為他實現(xiàn)一個邏輯,這個邏輯是這些方法本身邏輯之外的邏輯,這又讓我們想起了代理,aop等知識,我們相當于就是在為這些方法做一個增強。事實上的實現(xiàn)主借的邏輯也大概就是這個思路。梳理一下大致步驟如下:

          現(xiàn)在我們來實現(xiàn)一下這個邏輯,代碼如下:

          private static void annotationLogic() {

               Class useAnnotationClass = UseAnnotation.class;
               for(Method method : useAnnotationClass.getMethods()) {
                   SimpleAnnotation simpleAnnotation = (SimpleAnnotation)method.getAnnotation(SimpleAnnotation.class);
                   if(simpleAnnotation != null) {
                       System.out.println(" Method Name : " + method.getName());
                       System.out.println(" value : " + simpleAnnotation.value());
                       System.out.println(" --------------------------- ");
                   }
               }
           }

          在這里我們實現(xiàn)的邏輯就是打印幾句話。

          從上面的實現(xiàn)邏輯我們不能發(fā)現(xiàn),借助于java的反射我們可以直接拿到一個類里所有的方法,然后再拿到方法上的注解,當然,我們也可以拿到字段上的注解。借助于反射我們可以拿到幾乎任何屬于一個類的東西。

          一個簡單的注解我們就實現(xiàn)完了。現(xiàn)在我們再回過頭來,看一下@Autowired注解是如何實現(xiàn)的。另外,Spring 系列面試題和答案全部整理好了,微信搜索Java技術(shù)棧,在后臺發(fā)送:面試,可以在線閱讀。

          @Autowired注解實現(xiàn)邏輯分析

          知道了上面的知識,我們不難想到,上面的注解雖然簡單,但是@Autowired和他最大的區(qū)別應(yīng)該僅僅在于注解的實現(xiàn)邏輯,其他利用反射獲取注解等等步驟應(yīng)該都是一致的。

          先來看一下@Autowired這個注解在spring的源代碼里的定義是怎樣的,如下所示:

          package org.springframework.beans.factory.annotation;
           
          import java.lang.annotation.Documented;
          import java.lang.annotation.ElementType;
          import java.lang.annotation.Retention;
          import java.lang.annotation.RetentionPolicy;
          import java.lang.annotation.Target;
           
          @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public @interface Autowired {
              boolean required() default true;
          }

          閱讀代碼我們可以看到,Autowired注解可以應(yīng)用在構(gòu)造方法,普通方法,參數(shù),字段,以及注解這五種類型的地方,它的保留策略是在運行時。下面,我們不多說直接來看spring對這個注解進行的邏輯實現(xiàn)。


          在Spring源代碼當中,Autowired注解位于包org.springframework.beans.factory.annotation之中,該包的內(nèi)容如下:

          經(jīng)過分析,不難發(fā)現(xiàn)Spring對autowire注解的實現(xiàn)邏輯位于類:AutowiredAnnotationBeanPostProcessor之中,已在上圖標紅。

          其中的核心處理代碼如下:

          private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
            LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
            Class<?> targetClass = clazz;//需要處理的目標類
                 
            do {
             final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();
           
                      /*通過反射獲取該類所有的字段,并遍歷每一個字段,并通過方法findAutowiredAnnotation遍歷每一個字段的所用注解,并如果用autowired修飾了,則返回auotowired相關(guān)屬性*/  
           
             ReflectionUtils.doWithLocalFields(targetClass, field -> {
              AnnotationAttributes ann = findAutowiredAnnotation(field);
              if (ann != null) {//校驗autowired注解是否用在了static方法上
               if (Modifier.isStatic(field.getModifiers())) {
                if (logger.isWarnEnabled()) {
                 logger.warn("Autowired annotation is not supported on static fields: " + field);
                }
                return;
               }//判斷是否指定了required
               boolean required = determineRequiredStatus(ann);
               currElements.add(new AutowiredFieldElement(field, required));
              }
             });
                      //和上面一樣的邏輯,但是是通過反射處理類的method
             ReflectionUtils.doWithLocalMethods(targetClass, method -> {
              Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
              if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
               return;
              }
              AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
              if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
               if (Modifier.isStatic(method.getModifiers())) {
                if (logger.isWarnEnabled()) {
                 logger.warn("Autowired annotation is not supported on static methods: " + method);
                }
                return;
               }
               if (method.getParameterCount() == 0) {
                if (logger.isWarnEnabled()) {
                 logger.warn("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));
              }
             });
              //用@Autowired修飾的注解可能不止一個,因此都加在currElements這個容器里面,一起處理  
             elements.addAll(0, currElements);
             targetClass = targetClass.getSuperclass();
            }
            while (targetClass != null && targetClass != Object.class);
           
            return new InjectionMetadata(clazz, elements);
           }

          博主在源代碼里加了注釋,結(jié)合注釋就能看懂它做的事情了,最后這個方法返回的就是包含所有帶有autowire注解修飾的一個InjectionMetadata集合。這個類由兩部分組成:

          public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
            this.targetClass = targetClass;
            this.injectedElements = elements;
           }

          一是我們處理的目標類,二就是上述方法獲取到的所以elements集合。

          有了目標類,與所有需要注入的元素集合之后,我們就可以實現(xiàn)autowired的依賴注入邏輯了,實現(xiàn)的方法如下:

          @Override
          public PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

           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;
          }

          它調(diào)用的方法是InjectionMetadata中定義的inject方法,如下

          public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
            Collection<InjectedElement> checkedElements = this.checkedElements;
            Collection<InjectedElement> elementsToIterate =
              (checkedElements != null ? checkedElements : this.injectedElements);
            if (!elementsToIterate.isEmpty()) {
             for (InjectedElement element : elementsToIterate) {
              if (logger.isTraceEnabled()) {
               logger.trace("Processing injected element of bean '" + beanName + "': " + element);
              }
              element.inject(target, beanName, pvs);
             }
            }
           }

          其邏輯就是遍歷,然后調(diào)用inject方法,inject方法其實現(xiàn)邏輯如下:

          /**
           * Either this or {@link #getResourceToInject} needs to be overridden.
           */
          protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
            throws Throwable {

           if (this.isField) {
            Field field = (Field) this.member;
            ReflectionUtils.makeAccessible(field);
            field.set(target, getResourceToInject(target, requestingBeanName));
           }
           else {
            if (checkPropertySkipping(pvs)) {
             return;
            }
            try {
             Method method = (Method) this.member;
             ReflectionUtils.makeAccessible(method);
             method.invoke(target, getResourceToInject(target, requestingBeanName));
            }
            catch (InvocationTargetException ex) {
             throw ex.getTargetException();
            }
           }
          }

          在這里的代碼當中我們也可以看到,是inject也使用了反射技術(shù)并且依然是分成字段和方法去處理的。在代碼里面也調(diào)用了makeAccessible這樣的可以稱之為暴力破解的方法,但是反射技術(shù)本就是為框架等用途設(shè)計的,這也無可厚非。

          對于字段的話,本質(zhì)上就是去set這個字段的值,即對對象進行實例化和賦值,例如下面代碼:

          @Autowired
          ObjectTest objectTest;

          那么在這里實現(xiàn)的就相當于給這個objecTest引用賦值了。

          對于方法的話,本質(zhì)就是去調(diào)用這個方法,因此這里調(diào)用的是method.invoke.

          getResourceToInject方法的參數(shù)就是要注入的bean的名字,這個方法的功能就是根據(jù)這個bean的名字去拿到它。

          以上,就是@Autowire注解實現(xiàn)邏輯的全部分析。結(jié)合源代碼再看一遍的話,會更加清楚一點。下面是spring容器如何實現(xiàn)@AutoWired自動注入的過程的圖:

          總結(jié)起來一句話:使用@Autowired注入的bean對于目標類來說,從代碼結(jié)構(gòu)上來講也就是一個普通的成員變量,@Autowired和spring一起工作,通過反射為這個成員變量賦值,也就是將其賦為期望的類實例。

          問題

          注解的有效周期是什么?

          各種注釋之間的第一個主要區(qū)別是,它們是在編譯時使用,然后被丟棄(如@Override),還是被放在編譯的類文件中,并在運行時可用(如Spring的@Component)。這是由注釋的“@Retention”策略決定的。如果您正在編寫自己的注釋,則需要決定該注釋在運行時(可能用于自動配置)還是僅在編譯時(用于檢查或代碼生成)有用。

          當用注釋編譯代碼時,編譯器看到注釋就像看到源元素上的其他修飾符一樣,比如訪問修飾符(public/private)或.。當遇到注釋時,它運行一個注釋處理器,就像一個插件類,表示對特定的注釋感興趣。注釋處理器通常使用反射API來檢查正在編譯的元素,并且可以簡單地對它們執(zhí)行檢查、修改它們或生成要編譯的新代碼。

          @Override是一個示例;它使用反射API來確保能夠在其中一個超類中找到方法簽名的匹配,如果不能,則使用@Override會導致編譯錯誤。

          注入的bean和用它的bean的關(guān)系是如何維護的?

          無論以何種方式注入,注入的bean就相當于類中的一個普通對象應(yīng)用,這是它的實例化是spring去容器中找符合的bean進行實例化,并注入到類當中的。他們之間的關(guān)系就是普通的一個對象持有另一個對象引用的關(guān)系。只是這些對象都是spring當中的bean而已。

          為什么注入的bean不能被定義為static的?

          從設(shè)計的角度來說 ,使用靜態(tài)字段會鼓勵使用靜態(tài)方法。靜態(tài)方法是evil的。依賴注入的主要目的是讓容器為您創(chuàng)建對象并進行連接。而且,它使測試更加容易。

          一旦開始使用靜態(tài)方法,您就不再需要創(chuàng)建對象的實例,并且測試變得更加困難。同樣,您不能創(chuàng)建給定類的多個實例,每個實例都注入不同的依賴項(因為該字段是隱式共享的,并且會創(chuàng)建全局狀態(tài))。

          靜態(tài)變量不是Object的屬性,而是Class的屬性。spring的autowire是在對象上完成的,這樣使得設(shè)計很干凈。在spring當中我們也可以將bean對象定義為單例,這樣就能從功能上實現(xiàn)與靜態(tài)定義相同的目的。

          但是從純粹技術(shù)的層面,我們可以這樣做:

          將@Autowired可以與setter方法一起使用,然后可以讓setter修改靜態(tài)字段的值。但是這種做法非常不推薦。另外,關(guān)注公眾號Java技術(shù)棧,在后臺回復:面試,可以獲取我整理的 Spring 系列面試題和答案,非常齊全。

          原文鏈接:https://blog.csdn.net/topdeveloperr/article/details/87971446

          推薦閱讀:

          世界的真實格局分析,地球人類社會底層運行原理

          不是你需要中臺,而是一名合格的架構(gòu)師(附各大廠中臺建設(shè)PPT)

          企業(yè)IT技術(shù)架構(gòu)規(guī)劃方案

          論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?

          華為干部與人才發(fā)展手冊(附PPT)

          企業(yè)10大管理流程圖,數(shù)字化轉(zhuǎn)型從業(yè)者必備!

          【中臺實踐】華為大數(shù)據(jù)中臺架構(gòu)分享.pdf

          華為的數(shù)字化轉(zhuǎn)型方法論

          華為如何實施數(shù)字化轉(zhuǎn)型(附PPT)

          超詳細280頁Docker實戰(zhàn)文檔!開放下載

          華為大數(shù)據(jù)解決方案(PPT)

          瀏覽 34
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色丁香操逼片 | 五月天激情成人网 | 亚洲人成色777777无码 | 青草青草久久 | 乱伦小说亚洲 |