1. 程式人生 > >java註解細節

java註解細節

ann 反射機制 cor father resource 類的方法 內部 spring uri

  現在很多框架都使用註解了,典型的像Spring裏面就可以看到大量的註解,比如@Service,@Controller這一類的都是註解。註解Annotation是java一項很重要的功能。下面就來整理一下關於註解的一些細節。

  1.首先,什麽是註解呢?

  較為官方的解釋是,註解是元數據,就是解釋數據的數據。說得通俗一點,它是一種能夠修飾類、變量、方法、參數等數據的元數據。以一個簡單的例子,我們經常看到的一個註解是@Override。比如如下代碼。

package com.xdx.learn;

public class Father {
    public void func(){
        System.out.println(
"hello"); } } class son extends Father{ @Override public void func() { System.out.println("hi"); } }

可以看到在son類中,重寫了(覆蓋)父類的func()方法,我們用了一個@Override的註解去修飾這個方法。它起到的作用是告訴編譯器,這個方法時覆蓋父類的方法,假如我不小心把func寫成了Func,編譯器由於已經得知了我是想覆蓋父類的func方法,就會提示我,父類並沒有一個叫做Func的方法,我就會去檢查,哦,原來是我寫錯了啊。

  所以註解起到了一種修飾元數據的作用,被修飾過的元數據具有某些特性,這些特性將在程序編譯、運行等時段被利用。具體是如何利用而起作用的,我們就不用去管了。提供該註解的提供者會搞定。

  比如Spring中用@Resource註解的成員變量,將在該類實例化的時候,被註入到類的實例當中。而具體如何利用@Resource註解實現這一過程的,Spring幫我們做了。

  2.註解的定義

  我們來看看上面講的@Resource

的註解的內部是如何定義的。  

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
    String name() default "";
    String lookup() default "";
    Class<?> type() default
java.lang.Object.class; enum AuthenticationType { CONTAINER, APPLICATION } AuthenticationType authenticationType() default AuthenticationType.CONTAINER; boolean shareable() default true; String mappedName() default ""; String description() default ""; }

  上述便是一個註解的定義,可以看到,這個註解都被兩個元註解所修飾,分別是@Target和@Retention

  先來看看@Target。 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

  它也被三個元註解來修飾,除了@Target和@Retention,還多出了一個@Documented

  事實上,總共有4種元註解,@Target、@Retention、@Documented@Inherited

   @Target——指明該類型的註解可以註解的程序元素的範圍(作用目標)。該元註解的取值可以為TYPE,METHOD,CONSTRUCTOR,FIELD等。如果Target元註解沒有出現,那麽定義的註解可以應用於程序的任何元素。由上述的@interface Target這個註解的定義,我們可以看到它的取值是一個枚舉類型的數組。事實上,它們具體的值如下。

  

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

  @Documented --類和方法的Annotation在缺省情況下是不出現在javadoc中的。如果使用@Documented修飾該Annotation,則表示它可以出現在javadoc中。
定義Annotation時,@Documented可有可無;若沒有定義,則Annotation不會出現在javadoc中。

  @Retention---指定註解的生命周期,也就是它能存活到什麽時候,我們來看看@Retention的具體定義就知道。 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

  然後我們看看RetentionPolicy 這個枚舉類有哪些對象。  

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

  很清楚了,註解可以存活的生命周期有3種,編譯階段,存儲在.class文件裏面,還要就是到運行階段。這裏需要註意的是,只有當註解的RetentionRetentionPolicy.RUNTIME的時候,我們才可以通過反射獲取方法或者類的註解信息。因為反射機制是在類加載完成以後的運行階段發揮作用的。

  @Inherited——如果一個類的註解(比如@A)被@Inherited元註解所標註,則這個類的子類可以繼承該類的這個註解@A。註意的是,這種繼承是針對類的註解,如果是方法的註解,則不需要用@Inherited。子類在繼承父類的方法的同時也繼承了這個方法的註解,除非你去重寫方法。

  回到@Resource註解,我們看到,定義一個註解需要用public @interface來修飾,並且被@Target、@Retention、@Documented(可選)、@Inherited(可選)這四個元註解所修飾。我們可以把註解看成一種特殊的類。看類內部,定義了很多方法,沒有訪問修飾符,沒有方法體,沒有參數,只有返回值類型。返回值可以帶有默認值,如

   String name() default "";

String lookup() default "";

  Class<?> type() default java.lang.Object.class;

  enum AuthenticationType { CONTAINER, APPLICATION }

  AuthenticationType authenticationType() default AuthenticationType.CONTAINER;

  boolean shareable() default true;

  String mappedName() default ""; String description() default "";

  需要記住的是這種方法定義的格式,無參,無方法體,事實上,我們可以理解成定義了一些成員變量,只不過這些成員變量名後面都加了一個(),並且可用default關鍵字來賦初值。

  定義了成員變量以後,我們可以在用這個註解的時候給成員變量賦值。比如@Resource(name="baseDao")。

  ps:如果註解中只有一個成員,且叫做value,我們在傳入value的值的時候,就不用指定這個成員的名稱的了。比如@Target(ElementType.ANNOTATION_TYPE)就屬於這種情況。

  3.就從一個註解類本身提供的信息而言,我們只能知道它的作用目標(從@Target元註解獲悉),生命周期(@Retention元註解獲悉),然後還能知道的是它的成員變量。除了從其他渠道(比如看源碼註釋)獲悉這個註解的具體作用並加以應用(比如使用@Service標註一個Service類),僅從註解的定義文件你是看不出它有什麽作用的。而我們對註解信息的編程也很有限,可以通過反射機制獲取類,成員,或者方法的註解信息,註解的成員值等。比如下面代碼。

  

package com.xdx.learn;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//定義一個註解類,可作用於類和方法
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Anno {
    String value() default "";
}

package com.xdx.learn;

import java.lang.reflect.Method;

@Anno("anno")
public class AnnoTest {
    @Anno("anno")
    public void func(){
        System.out.println("hello");
    }
    public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, SecurityException{
        Class clz=Class.forName("com.xdx.learn.AnnoTest");
        //反射獲取類的註解信息
        if(clz.getAnnotation(Anno.class)!=null){
            Anno anno=(Anno) clz.getAnnotation(Anno.class);
            System.out.println(anno.value());
            System.out.println(anno.annotationType());
        }
        //反射獲取方法的註解信息
        Method method=clz.getDeclaredMethod("func", null);
        if(method.isAnnotationPresent(Anno.class)){
            Anno anno=method.getAnnotation(Anno.class);
            System.out.println(anno.value());
            System.out.println(anno.annotationType());
        }
    }

}

  上述代碼運行的結果為:

  anno
  interface com.xdx.learn.Anno
  anno
  interface com.xdx.learn.Anno

  

  

  

java註解細節