Java註解詳解

方誌朋發表於2019-02-26

轉載請標明出處:
blog.csdn.net/forezp/arti…
本文出自方誌朋的部落格

在使用SpringBoot作為Web敏捷開發的框架之後,SpringBoot除了自動裝配配置的便捷之外,在很多時候需要基於註解來開發。註解不僅增加了程式碼的可讀性,還增加了開發的速度。這篇文章主要講述Java 註解。

元註解

元註解用於註解其他註解的。Java 5.0定義了4個標準的元註解,如下:

  • @Target
  • @Retention
  • @Documented
  • Inherited

現在來說說這四個元註解有什麼作用。

@Target

 @Target註解用於宣告註解的作用範圍,例如作用範圍為類、介面、方法等。它的取值以及值所對應的範圍如下:

  • CONSTRUCTOR:用於描述構造器
  • FIELD:用於描述域
  • LOCAL_VARIABLE:用於描述區域性變數
  • METHOD:用於描述方法
  • PACKAGE:用於描述包
  • PARAMETER:用於描述引數
  • TYPE:用於描述類、介面(包括註解型別) 或enum宣告

@Retention

該註解宣告瞭註解的生命週期,即註解在什麼範圍內有效。

  • SOURCE:在原始檔中有效
  • CLASS:在class檔案中有效
  • RUNTIME:在執行時有效(即執行時保留)

大多數註解都為RUNTIME

@Documented

是一個標記註解,有該註解的註解會在生成 java 文件中保留。

@Inherited

該註解表明子類是有繼承了父類的註解。比如一個註解被該元註解修飾,並且該註解的作用在父類上,那麼子類有持有該註解。如果註解沒有被該元註解修飾,則子類不持有父類的註解。

自定義註解

在Java開發者,JDK自帶了一些註解,在第三方框架Spring 帶了大量的註解,這些註解稱為第三方註解。在很多實際開發過程中,我們需要定義自己的註解。那麼現在以案例的方式來講解自定義註解。

在註解中,需要使用四種元註解來宣告註解的作用範圍、生命週期、繼承,是否生成文件等。另外在註解中也可以有自己的成員變數,如果一個註解沒有成員變數則稱為標記註解。註解的成員變數,只支援原始型別、Class、Enumeration、Annoation。

現在定義一個@Writer註解,該註解被Retention、Documented、Inherited、Target修飾,表明該註解的作用範圍為類、介面和方法,生命週期為執行時、該註解生成文件,並且子類可繼承該註解。該註解有2個成員變數,一個為name一個為 age,程式碼如下:

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Writer {

    String name();

    int age();

}複製程式碼

那麼有了該註解,怎麼用呢?

該註解的作用範圍為類、方法,寫一個WriterTest,程式碼如下:

@Writer(name = "forezp", age = 12)
public class WriterTest {

    @Writer(name = "miya", age = 10)
    public void writeBlog() {
        System.out.println("writing blog");
    }

  }複製程式碼

該類有了這個註解有何用?

一般來說,用該類修飾的類,需要通過反射來做一下邏輯的開發的工作,可廣泛用於AOP、程式的配置等。現在寫一個方法通過反射來解析該註解:

  public static void main(String[] args) throws ClassNotFoundException {
        Class c = Class.forName("com.forezp.annotation.WriterTest");
        if (c.isAnnotationPresent(Writer.class)) {
            Writer w = (Writer) c.getAnnotation(Writer.class);
            System.out.println("name:" + w.name() + "   age:" + w.age());
        }
        Method[] methods = c.getMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(Writer.class)) {
                Writer w = method.getAnnotation(Writer.class);
                System.out.println("name:" + w.name() + "   age:" + w.age());
            }
        }
    }複製程式碼

這些程式碼基本為反射的內容,因為反射在另一篇文章已經詳細講述過,不再重複,執行該Main方法,控制檯列印出如下內容:

name:forezp age:12

name:miya age:10

案例實戰

有了上述的講解,你可能對註解有所瞭解,但是對註解的具體應用並不是很深刻。現在以一個案例來詳細講述。

大家都對ORM框架Mybitis都非常的熟悉,在這個框架中用了大量的註解。現在模仿這個框架,通過自定義註解,來解析sql 的查詢語句。實現過程大概如下:

  • 定義@Table @Colum註解
  • 定義一個實體User,定義一些基本的欄位,並用註解修飾
  • 用User類new物件,給物件的某些欄位賦值
  • 通過反射和註解來生成sql 的查詢語句

首先定義個一個Table註解,它的作用範圍為類,程式碼如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
public @interface Table {
    String value() default "";
}複製程式碼

定義一個Column註解,作用範圍為欄位,程式碼如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Column {
    String value();
}複製程式碼

定義一個User類,在該類的加上@Table註解,在具體的欄位上 @Column註解,程式碼如下:

@Table("user")
public class User {
    @Column("id")
    private int id;
    @Column("name")
    private String name;
    @Column("age")
    private int age;
    @Column("address")
    private String address;
    ..//省略getter setter
   }複製程式碼

寫一個生成sql語句的類,它是通過反射來獲取表名、欄位名,加上判斷實體物件的欄位值來生成 查詢的 sql 語句的。程式碼如下:

public class GenUserSql {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        User u1 = new User();
        User u2 = new User();
        u1.setId(1);
        u2.setName("forezp");
        genSql(u2);
        genSql(u1);
    }

    private static void genSql(User user) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class c = user.getClass();
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("select * from ");
        if (c.isAnnotationPresent(Table.class)) {
            Table table = (Table) c.getAnnotation(Table.class);
            String tableName = table.value();
            stringBuilder.append(tableName).append(" where 1=1 and ");
        }
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            String columnName;
            if (field.isAnnotationPresent(Column.class)) {
                Column column = field.getAnnotation(Column.class);
                columnName = column.value();
            } else {
                continue;
            }
            String fieldName = field.getName();
            String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                       Method method = c.getMethod(getMethodName);
            Object fieldValue = method.invoke(user);
            if (fieldValue == null || ((fieldValue instanceof Integer) && (Integer) fieldValue == 0)) {
                continue;
            }
            if (fieldValue instanceof Integer) {
                stringBuilder.append(columnName + "=" + fieldValue);
            }
            if (fieldValue instanceof String) {
                stringBuilder.append(columnName + "=" + "`" + fieldValue + "`");
            }

        }
        System.out.println(stringBuilder.toString());

    }
}複製程式碼

執行程式,控制檯列印如下:

>

select * from user where 1=1 and name=`forezp`

select * from user where 1=1 and id=1

參考資料

www.cnblogs.com/peida/archi…

慕課網視訊

原始碼下載

github.com/forezp/java…

關注我的公眾號

精彩內容不能錯過!

forezp.jpg
forezp.jpg

相關文章