Java的註解機制——Spring自動裝配的實現原理

來源:互聯網
上載者:User

  JDK1.5加入了對註解機制的支援,實際上我學習Java的時候就已經使用JDK1.6了,而且除了@Override和@SuppressWarnings(後者還是IDE給產生的……)之外沒接觸過其他的。

  進入公司前的面試,技術人員就問了我關於註解的問題,我就說可以產生chm手冊……現在想起來真囧,注釋和註解被我搞得完全一樣了。

  

  使用註解主要是在需要使用Spring架構的時候,特別是使用SpringMVC。因為這時我們會發現它的強大之處:預先處理。

  註解實際上相當於一種標記,它允許你在運行時(源碼、文檔、類檔案我們就不討論了)動態地對擁有該標記的成員進行操作。

  實現註解需要三個條件(我們討論的是類似於Spring自動裝配的進階應用程式):註解聲明、使用註解的元素、操作使用註解元素的代碼。

  

  首先是註解聲明,註解也是一種類型,我們要定義的話也需要編寫代碼,如下:

 1 package annotation; 2  3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.RetentionPolicy; 6 import java.lang.annotation.Target; 7  8 /** 9  * 自訂註解,用來配置方法10  * 11  * @author Johness12  *13  */14 @Retention(RetentionPolicy.RUNTIME) // 表示註解在運行時依然存在15 @Target(ElementType.METHOD) // 表示註解可以被使用於方法上16 public @interface SayHiAnnotation {17     String paramValue() default "johness"; // 表示我的註解需要一個參數 名為"paramValue" 預設值為"johness"18 }

 

  然後是使用我們註解的元素:

 1 package element; 2  3 import annotation.SayHiAnnotation; 4  5 /** 6  * 要使用SayHiAnnotation的元素所在類 7  * 由於我們定義了只有方法才能使用我們的註解,我們就使用多個方法來進行測試 8  *  9  * @author Johness10  *11  */12 public class SayHiEmlement {13 14     // 普通的方法15     public void SayHiDefault(String name){16         System.out.println("Hi, " + name);17     }18     19     // 使用註解並傳入參數的方法20     @SayHiAnnotation(paramValue="Jack")21     public void SayHiAnnotation(String name){22         System.out.println("Hi, " + name);23     }24     25     // 使用註解並使用預設參數的方法26     @SayHiAnnotation27     public void SayHiAnnotationDefault(String name){28         System.out.println("Hi, " + name);29     }30 }

  最後,是我們的操作方法(值得一提的是雖然有一定的規範,但您大可不必去浪費精力,您只需要保證您的作業碼在您希望的時候執行即可):

 1 package Main; 2  3 import java.lang.reflect.InvocationTargetException; 4 import java.lang.reflect.Method; 5  6 import element.SayHiEmlement; 7 import annotation.SayHiAnnotation; 8  9 public class AnnotionOperator {10     public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException {11         SayHiEmlement element = new SayHiEmlement(); // 初始化一個執行個體,用於方法調用12         Method[] methods = SayHiEmlement.class.getDeclaredMethods(); // 獲得所有方法13         14         for (Method method : methods) {15             SayHiAnnotation annotationTmp = null;16             if((annotationTmp = method.getAnnotation(SayHiAnnotation.class))!=null) // 檢測是否使用了我們的註解17                 method.invoke(element,annotationTmp.paramValue()); // 如果使用了我們的註解,我們就把註解裡的"paramValue"參數值作為方法參數來調用方法18             else19                 method.invoke(element, "Rose"); // 如果沒有使用我們的註解,我們就需要使用普通的方式來調用方法了20         }21     }22 }

  結果為:Hi, Jack
      Hi, johness
      Hi, Rose

  可以看到,註解是進行預先處理的很好方式(這裡的預先處理和編譯原理有區別)!

 

  接下來我們看看Spring是如何使用註解機制完成自動裝配的:

  

    首先是為了讓Spring為我們自動裝配要進行的操作,無外乎兩種:繼承org.springframework.web.context.support.SpringBeanAutowiringSupport類或者添加@Component/@Controller等註解並(只是使用註解方式需要)在Spring設定檔裡聲明context:component-scan元素。

    我說說繼承方式是如何?自動裝配的,我們開啟Spring原始碼查看SpringBeanAutowiringSupport類。我們會發現以下語句:

1 public SpringBeanAutowiringSupport() {2         processInjectionBasedOnCurrentContext(this);3     }

 

    眾所周知,Java執行個體構造時會調用預設父類無參構造方法,Spring正是利用了這一點,讓"操作元素的代碼"得以執行!(我看到第一眼就震驚了!真是奇思妙想啊。果然,高手都要善於用Java來用Java)

    後面的我就不就不多說了,不過還是要糾正一些人的觀點:說使用註解的自動裝配來完成注入也需要setter。這明顯是錯誤的嘛!我們看Spring註解裝配(繼承方式)的方法調用順序: org.springframework.web.context.support.SpringBeanAutowiringSupport#SpringBeanAutowiringSupport=>

        org.springframework.web.context.support.SpringBeanAutowiringSupport#processInjectionBasedOnCurrentContext=>

      org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#processInjection=>

               org.springframework.beans.factory.annotation.InjectionMetadata#Injection(繼承,方法重寫)。最後看看Injection方法的方法體:

 1 /** 2          * Either this or {@link #getResourceToInject} needs to be overridden. 3          */ 4         protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable { 5             if (this.isField) { 6                 Field field = (Field) this.member; 7                 ReflectionUtils.makeAccessible(field); 8                 field.set(target, getResourceToInject(target, requestingBeanName)); 9             }10             else {11                 if (checkPropertySkipping(pvs)) {12                     return;13                 }14                 try {15                     Method method = (Method) this.member;16                     ReflectionUtils.makeAccessible(method);17                     method.invoke(target, getResourceToInject(target, requestingBeanName));18                 }19                 catch (InvocationTargetException ex) {20                     throw ex.getTargetException();21                 }22             }23         }

      雖然不完全,但可以基本判定此種自動裝配是使用了java放射機制。

 

 歡迎您移步我們的交流群,無聊的時候大家一起打發時間:

 或者通過QQ與我聯絡:

 (最後編輯時間2013-04-19 09:52:27)

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.