java註解(Annotation)解析

 

註解(Annotation)在java中應用很是普遍。它既能幫助咱們在編碼中減小錯誤,(好比最多見的Override註解),還能夠幫助咱們減小各類xml文件的配置,好比定義AOP切面用@AspectJ模式代替Schema模式,特別是最近接觸了一點Spring MVC,每次編寫Controller的時候用@RequestMapping(),@RequestParam,@ResponseBody等等。java

咱們所用的java中自帶的註解都是定義好了的,直接拿來用,它就能發揮做用。固然了,本身也能夠定義註解。數組

 

註解定義:app

public @interface 註解名 {
    
    //
} 

能夠給註解中能夠定義「屬性」,暫且理解爲屬性吧,定義以下:框架

public @interface MyAnnotation {
    
    String hello(); // 定義了一個屬性名hello

    String world() default "world";// 定義了一個屬性名world,默認值爲「world」,注意類型匹配

    int number();// 定義了一個屬性名爲number

    String[] value(); // 定義了一個屬性爲value的數組

}

後面必須有括號,修飾符只能爲public或abstract,不過通常都是默認不寫的。ide

當咱們定義一個註解時,它暗自的繼承了Annotation這個藉口。若是手動定義了一個Interface 並繼承了java.lang.annotation.Annotation,並不意味着定義了一個註解。註解只能經過@Interface方式定義。Annotation是一個接口而不是一個註解。編碼

使用方式:spa

1.當咱們對一個註解定義了多個屬性後,在使用該註解時,除了定義了默認值的屬性,其餘屬性都要賦值。固然,也能夠顯式的對含有默認值的屬性賦值。code

例如:xml

public class MyAnnotationTest{
    
    private String name;
    @MyAnnotation(hello = "", number = 0, value = { "aa" },world="world1")
    public static void main(String[] args) {
        MyAnnotationTest test = new MyAnnotationTest();
        test.method();
    }
    @MyAnnotation(hello = "hello", number = 0, value = {"a"},world="aa")
    public void method(){
        System.out.println("method");
    }
}

2.當一個註解只含有一個屬性且屬性名爲value時,咱們能夠簡化使用方式。對象

好比定義一個註解:
public @interface SingleAnnotation {
    String[] value();
}

在使用在註解時能夠簡化:

    @SingleAnnotation("value")
    public void singleAnnotationMethod(){
        
    }    

 註解基本的定義和使用差很少就這些了。接下來看看定義的註解爲何會生效。

@Retention註解

 爲了更好的使用註解,java內置的這個註解也是須要了解的。

java.lang.annotation.Retention能夠告訴編譯器怎樣去對待和處理咱們自定義的註解。默認狀況下會將Annotation的信息留在class文件中,但不會被JVM讀取。

在使用Retention註解時,須要提供java.lang.annotation.RetentionPolicy的枚舉值。編譯器會根據設置不一樣的值來決定如何處理定義的註解。

RetentionPolicy定義以下:

public enum RetentionPolicy {
   
    SOURCE, //編譯程序丟棄該註解,表示他不會存在於class文件中

    CLASS, //編譯程序將Annotation存在class文件中(缺省)

    RUNTIME //編譯程序將Annotation存在class文件中,可由JVM讀取
}

 好比@SuppressWarnings註解,它所對應的@Retention(RetentionPolicy.SOURCE)是這樣設置的,由於它的做用是告知編譯程序來壓制警告,不必將這個信息存在於class文件中。

RetentionPolicy.RUNTIME:搭配反射機制,可讓註解被JVM讀出。

一個註解信息如何被JVM讀出

  1.將該註解加上 @Retention(value = RetentionPolicy.RUNTIME)。

  1.得到一個類的class對象

  2.得到這個類中被該註解修飾的方法或屬性。 AccessibleObject的isAnnotationPresent(Class<? extends Annotation> annotationClass)方法能夠判斷一個註解是否出現,因爲Field和Method類繼承了AccessibleObject,因此能夠判斷出一個類的方法或屬性是否有該註解。又由於Method實現了AnnotatedElement ,因此method能夠獲得它對應的註解的Annotation對象,進而能夠獲取註解的相關信息。

 

下面給出例子:

首先給MyAnnotation註解 加上 @Retention(value = RetentionPolicy.RUNTIME)。

 

public class MyAnnotationTest{
    
    
    public static void main(String[] args)throws Exception{
        MyAnnotationTest test = new MyAnnotationTest();
        
        Class<?> clazz = test.getClass();
        Method[] methods = clazz.getMethods();
        
        for(Method m : methods){
            //該方法是否出現MyAnnotation註解,出現爲true,不然爲false
            if(m.isAnnotationPresent(MyAnnotation.class)){
                
                MyAnnotation annotation = m.getAnnotation(MyAnnotation.class);
                String hello = annotation.hello();
                int number = annotation.number();
                String[] values = annotation.value();
                String world = annotation.world();
                
                m.invoke(test,new Object[]{});
                
                System.out.println("annotation.hello():" + hello);
                System.out.println("annotation.number():" + number);
                System.out.println("annotation.value():" + values[0]);
                System.out.println("annotation.world():" + world);
                
            }
        }
    }
    
    @MyAnnotation(hello = "hello", number = 100, value = {"a"},world="aa")
    public void method(){
        System.out.println("method");
    }
    public void method2(){
        System.out.println("method2");
    }
    
}

console 結果:

結果一目瞭然,可讓該方法執行,能夠拿到該註解的全部賦值狀況。

若是 MyAnnotation的Retention註解爲 @Retention(value = RetentionPolicy.CLASS) 或@Retention(value = RetentionPolicy.SOURCE),那麼method方法也不會執行,更不會獲取到MyAnnotation的先關信息了。

@Target註解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

它也是用來註解咱們定義的註解。value爲ElementType類型的枚舉值。

ElementType定義:

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

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

    /** Method declaration */
    METHOD,

    /** Parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE
}

好吧,不用解釋了,看註釋都知道每個值是什麼意思了。

若是一個註解被@Target直接,那麼該註解所修飾的類型將受到限制,若是修飾的類型不匹配編譯將會報錯。

表示MyAnnotation註解只能修飾方法,可根據ElementType 的值以此類推。

@Documented

它表示被修飾的註解將會生成到javadoc幫助文檔中。

@Inherited:容許子類繼承父類的註解。缺省狀況下子類並不會繼承父類中的Annotation.

就算java的原生註解,通常也是被這幾種註解中的一種或多種修飾,進而達到其功能。

總結:個其實java註解的原理仍是反射。在運行時,由於有了註解,利用反射機制在運行時纔會找到註解對應的方法,找到註解對應的值。值拿到了,方法也找到了,想幹什麼就幹什麼了,該幹什麼這通常應該是底層或框架作的事情。但我想應該就是這麼回事吧!

相關文章
相關標籤/搜索