註解就這麼簡單

前言

今天要講的是註解,對於本章節,最好是有Servlet基礎的人查閱~由於單純是Java基礎的話,可能用不上註解這個東西。但若是開發過Servlet,就對@WebServlet不會陌生。java

如今的開發都推崇使用註解來進行開發,這樣就能夠免去寫XML配置了,十分方便的一項技術~web

學習註解能夠更好地理解註解是怎麼工做的,看見註解了就能夠想到它的運行原理了~。數組

若是有錯的地方請你們多多包涵並歡迎在評論區指正~安全

1、什麼是註解?

註解:Annotation....微信

註解其實就是代碼中的特殊標記,這些標記能夠在編譯、類加載、運行時被讀取,並執行相對應的處理函數

2、爲何咱們須要用到註解?

傳統的方式,咱們是經過配置文件(xml文件)來告訴類是如何運行的工具

有了註解技術之後,咱們就能夠經過註解告訴類如何運行學習

例如:咱們之前編寫Servlet的時候,須要在web.xml文件配置具體的信息this

這裏寫圖片描述

咱們使用了註解之後,能夠直接在Servlet源代碼上,增長註解...Servlet就被配置到Tomcat上了。也就是說,註解能夠給類、方法上注入信息。spa

這裏寫圖片描述

明顯地能夠看出,這樣是很是直觀的,而且Servlet規範是推崇這種配置方式的

3、基本Annotation

在java.lang包下存在着5個基本的Annotation,其中有3個Annotation咱們是很是常見的了。

3.1@Overried

重寫註解

若是咱們使用IDE重寫父類的方法,咱們就能夠看見它了。那它有什麼用呢??

@Overried是告訴編譯器要檢查該方法是實現父類的...能夠幫咱們避免一些低級的錯誤...

好比,咱們在實現equals()方法的時候,把euqals()打錯了,那麼編譯器就會發現該方法並非實現父類的,與註解@Overried衝突,因而就會給予錯誤

這裏寫圖片描述


3.2@Deprecated

過期註解

該註解也很是常見,Java在設計的時候,可能以爲某些方法設計得很差,爲了兼容之前的程序,是不能直接把它拋棄的,因而就設置它爲過期

Date對象中的toLocalString()就被設置成過期了

@Deprecated
    public String toLocaleString() {
        DateFormat formatter = DateFormat.getDateTimeInstance();
        return formatter.format(this);
    }
複製代碼

當咱們在程序中調用它的時候,在IDE上會出現一條橫槓,說明該方法是過期的。

這裏寫圖片描述


3.3@SuppressWarnings

抑制編譯器警告註解

該註解在咱們寫程序的時候並非很常見,咱們能夠用它來讓編譯器不給予咱們警告

當咱們在使用集合的時候,若是沒有指定泛型,那麼會提示安全檢查的警告

這裏寫圖片描述

若是咱們在類上添加了@SuppressWarnings這個註解,那麼編譯器就不會給予咱們警告了

這裏寫圖片描述

3.4@SafeVarargs

Java 7「堆污染」警告

什麼是堆污染呢??當把一個不是泛型的集合賦值給一個帶泛型的集合的時候,這種狀況就很容易發生堆污染....

這個註解也是用來抑制編譯器警告的註解...用的地方並很少,我也不詳細說明了......有用到的時候再回來填坑吧。

3.5@FunctionalInterface

@FunctionalInterface用來指定該接口是函數式接口

用該註解顯示指定該接口是一個函數式接口。


4、自定義註解基礎

上面講解的是java.lang包下的5個註解,咱們是能夠本身來寫註解,給方法或類注入信息

##4.1標記Annotation ##

沒有任何成員變量的註解稱做爲標記註解,@Overried就是一個標記註解

//有點像定義一個接口同樣,只不過它多了一個@
public @interface MyAnnotation {
    
}

複製代碼

4.2元數據Annotation

咱們自定義的註解是能夠帶成員變量的,定義帶成員變量的註解叫作元數據Annotation

在註解中定義成員變量,語法相似於聲明方法同樣....

public @interface MyAnnotation {

    //定義了兩個成員變量
    String username();
    int age();
}

複製代碼

注意:在註解上定義的成員變量只能是String、數組、Class、枚舉類、註解

有的人可能會奇怪,爲何註解上還要定義註解成員變量??聽起來就很複雜了....

上邊已經說了,註解的做用就是給類、方法注入信息。那麼咱們常用XML文件,告訴程序怎麼運行。XML常常會有嵌套的狀況

<>
	<做者>zhongfucheng</做者>
	<價錢>22222</價錢>

</>

複製代碼

那麼,當咱們在使用註解的時候,也可能須要有嵌套的時候,因此就容許了註解上能夠定義成員變量爲註解。

4.3使用自定義註解

上面咱們已經定義了一個註解了,下面咱們來使用它吧

4.3.1常規使用

下面我有一個add的方法,須要username和age參數,咱們經過註解來讓該方法擁有這兩個變量

//註解擁有什麼屬性,在修飾的時候就要給出相對應的值
    @MyAnnotation(username = "zhongfucheng", age = 20)
    public void add(String username, int age) {

    }

複製代碼

4.3.2默認值

固然啦,咱們能夠在註解聲明屬性的時候,給出默認值。那麼在修飾的時候,就能夠不用具體指定了。

public @interface MyAnnotation {

    //定義了兩個成員變量
    String username() default "zicheng";
    int age() default 23;
}

複製代碼
  • 在修飾的時候就不須要給出具體的值了
@MyAnnotation()
    public void add(String username, int age) {

    }

複製代碼

4.3.3註解屬性爲value

還有一種特殊的狀況,若是註解上只有一個屬性,而且屬性的名稱爲value,那麼在使用的時候,咱們能夠不寫value,直接賦值給它就行

public @interface MyAnnotation2 {

    String value();
}

複製代碼
  • 使用註解,能夠不指定value,直接賦值
@MyAnnotation2("zhongfucheng")
    public void find(String id) {
        
    }
複製代碼

4.4把自定義註解的基本信息注入到方法上

上面咱們已經使用到了註解,可是目前爲止註解上的信息和方法上的信息是沒有任何關聯的

咱們使用Servlet註解的時候,僅僅調用註解,那麼註解的就生效了。這是Web容器把內部實現了。咱們本身寫的自定義註解是須要咱們本身來處理的

那如今問題來了,咱們怎麼把註解上的信息注入到方法上呢???咱們利用的是反射技術

步驟可分爲三部:

  • 反射出該類的方法
  • 經過方法獲得註解上具體的信息
  • 將註解上的信息注入到方法上
//反射出該類的方法
        Class aClass = Demo2.class;
        Method method = aClass.getMethod("add", String.class, int.class);

        //經過該方法獲得註解上的具體信息
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        String username = annotation.username();
        int age = annotation.age();

        //將註解上的信息注入到方法上
        Object o = aClass.newInstance();
        method.invoke(o, username, age);
複製代碼

當咱們執行的時候,咱們發現會出現異常...

這裏寫圖片描述

此時,咱們須要在自定義註解上加入這樣一句代碼**(下面就會講到,爲何要加入這句代碼)**

@Retention(RetentionPolicy.RUNTIME)
複製代碼

再次執行的時候,咱們就會發現,能夠經過註解來把信息注入到方法中了。


5、JDK的元Annotation

前面咱們已經介紹了java.lang包下的幾個基本Annotation了。在JDK中除了java.lang包下有Annotation,在java.lang.annotation下也有幾個經常使用的元Annotation。

在annotation包下的好幾個元Annotation都是用於修飾其餘的Annotation定義


5.1@Retention

上面在將註解信息注入到方法中的時候,咱們最後加上了@Retention的註解....否則就會報錯了..那它是幹什麼用的呢?

@Retention只能用於修飾其餘的Annotation,用於指定被修飾的Annotation被保留多長時間。

@Retention 包含了一個RetentionPolicy類型的value變量,因此在使用它的時候,必需要爲value成員變量賦值

value變量的值只有三個:

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
}

複製代碼

java文件有三個時期:編譯,class,運行。@Retention默認是class

前面咱們是使用反射來獲得註解上的信息的,由於@Retention默認是class,而反射是在運行時期來獲取信息的。所以就獲取不到Annotation的信息了。因而,就得在自定義註解上修改它的RetentionPolicy值


5.2@Target

@Target也是只能用於修飾另外的Annotation它用於指定被修飾的Annotation用於修飾哪些程序單元

@Target是隻有一個value成員變量的,該成員變量的值是如下的:

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指定的是ElementType.ANNOTATION_TYPE,那麼該被修飾的Annotation只能修飾Annotaion


5.3@Documented

@Documented用於指定被該Annotation修飾的Annotation類將被javadoc工具提取成文檔

該元Annotation用得挺少的....


5.4@Inherited

@Inherited也是用來修飾其餘的Annotation的,被修飾過的Annotation將具備繼承性。。。

例子:

  1. @xxx是我自定義的註解,我如今使用@xxx註解在Base類上使用....
  2. 使用@Inherited修飾@xxx註解
  3. 當有類繼承了Base類的時候,該實現類自動擁有@xxx註解

6、注入對象到方法或成員變量上

6.1把對象注入到方法上

前面咱們已經可使用註解將基本的信息注入到方法上了,如今咱們要使用的是將對象注入到方法上.....

上邊已經說過了,註解上只能定義String、枚舉類、Double之類的成員變量,那怎麼把對象注入到方法上呢?

6.1.2模擬場景:

  • Person類,定義username和age屬性,擁有uername和age的getter和setter方法
public class Person {
    
    private String username;
    private int age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

複製代碼
  • PersonDao類,PersonDao類定義了Person對象,擁有person的setter和getter方法
public class PersonDao {
    
    private Person person;

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }
}

複製代碼
  • 如今我要作的就是:使用註解將Person對象注入到setPerson()方法中,從而設置了PersonDao類的person屬性
public class PersonDao {

    private Person person;

    public Person getPerson() {
        return person;
    }


	//將username爲zhongfucheng,age爲20的Person對象注入到setPerson方法中
    @InjectPerson(username = "zhongfucheng",age = 20)
    public void setPerson(Person person) {
        
        this.person = person;
    }
}

複製代碼

步驟:

①: 自定義一個註解,屬性是和JavaBean類一致的

//注入工具是經過反射來獲得註解的信息的,因而保留域必須使用RunTime
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectPerson {

    String username();
    int age();
}

複製代碼

②:編寫注入工具

//1.使用內省【後邊須要獲得屬性的寫方法】,獲得想要注入的屬性
        PropertyDescriptor descriptor = new PropertyDescriptor("person", PersonDao.class);

        //2.獲得要想注入屬性的具體對象
        Person person = (Person) descriptor.getPropertyType().newInstance();

        //3.獲得該屬性的寫方法【setPerson()】
        Method method = descriptor.getWriteMethod();

        //4.獲得寫方法的註解
        Annotation annotation = method.getAnnotation(InjectPerson.class);

        //5.獲得註解上的信息【註解的成員變量就是用方法來定義的】
        Method[] methods = annotation.getClass().getMethods();

        //6.將註解上的信息填充到person對象上

        for (Method m : methods) {

            //獲得註解上屬性的名字【age或name】
            String name = m.getName();

            //看看Person對象有沒有與之對應的方法【setAge(),setName()】
            try {

                //6.1這裏假設:有與之對應的寫方法,獲得寫方法
                PropertyDescriptor descriptor1 = new PropertyDescriptor(name, Person.class);
                Method method1 = descriptor1.getWriteMethod();//setAge(), setName()

                //獲得註解中的值
                Object o = m.invoke(annotation, null);

                //調用Person對象的setter方法,將註解上的值設置進去
                method1.invoke(person, o);

            } catch (Exception e) {

                //6.2 Person對象沒有與之對應的方法,會跳到catch來。咱們要讓它繼續遍歷註解就行了
                continue;
            }
        }
		
		//當程序遍歷完以後,person對象已經填充完數據了

        //7.將person對象賦給PersonDao【經過寫方法】
        PersonDao personDao = new PersonDao();
        method.invoke(personDao, person);

        System.out.println(personDao.getPerson().getUsername());
        System.out.println(personDao.getPerson().getAge());


複製代碼

③:總結一下步驟

其實咱們是這樣把對象注入到方法中的:

  • 獲得想要類中注入的屬性
  • 獲得該屬性的對象
  • 獲得屬性對應的寫方法
  • 經過寫方法獲得註解
  • 獲取註解詳細的信息
  • 將註解的信息注入到對象上
  • 調用屬性寫方法,將已填充數據的對象注入到方法中

6.2把對象注入到成員變量

上面已經說了如何將對象注入到方法上了,那麼注入到成員變量上也是很是簡單的。

步驟:

①:在成員變量上使用註解

public class PersonDao {

    @InjectPerson(username = "zhongfucheng",age = 20) private Person person;

    public Person getPerson() {
        return person;
    }
    
    public void setPerson(Person person) {

        this.person = person;
    }
}

複製代碼

②:編寫注入工具

//1.獲得想要注入的屬性
        Field field = PersonDao.class.getDeclaredField("person");

        //2.獲得屬性的具體對象
        Person person = (Person) field.getType().newInstance();

        //3.獲得屬性上的註解
        Annotation annotation = field.getAnnotation(InjectPerson.class);

        //4.獲得註解的屬性【註解上的屬性使用方法來表示的】
        Method[] methods = annotation.getClass().getMethods();

        //5.將注入的屬性填充到person對象上
        for (Method method : methods) {

            //5.1獲得註解屬性的名字
            String name = method.getName();

            //查看一下Person對象上有沒有與之對應的寫方法
            try {

                //若是有
                PropertyDescriptor descriptor = new PropertyDescriptor(name, Person.class);

                //獲得Person對象上的寫方法
                Method method1 = descriptor.getWriteMethod();

                //獲得註解上的值
                Object o = method.invoke(annotation, null);

                //填充person對象
                method1.invoke(person, o);
            } catch (IntrospectionException e) {

                //若是沒有想對應的屬性,繼續循環
                continue;
            }
        }

        //循環完以後,person就已經填充好數據了


        //6.把person對象設置到PersonDao中
        PersonDao personDao = new PersonDao();
        field.setAccessible(true);
        field.set(personDao, person);

        System.out.println(personDao.getPerson().getUsername());
複製代碼

7、總結

①:注入對象的步驟:獲得想要注入的對象屬性,經過屬性獲得註解的信息,經過屬性的寫方法將註解的信息注入到對象上,最後將對象賦給類

②:註解其實就是兩個做用:

  • 讓編譯器檢查代碼
  • 將數據注入到方法、成員變量、類上

③:在JDK中註解分爲了

  • 基本Annotation
    • 在lang包下,用於經常使用於標記該方法,抑制編譯器警告等
  • 元Annotaion
    • 在annotaion包下,經常使用於修飾其餘的Annotation定義

若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y

相關文章
相關標籤/搜索