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)