探索 Java 註解

Java 註解讓我們可以在代碼中添加信息元數據,這樣這些元數據就可以實現與源代碼放在一處管理與維護,而且還可以表述程序所需的信息。

1 標準註解

Java 定義了 5 種標準註解,具體說明如下:

註解 JDK 版本 說明
@Override JDK5 表示當前定義的方法將覆蓋基類定義的方法。
@Deprecated JDK5 表示當前定義的元素已過時,如果有其它元素調用該元素,那麼編譯器會記錄警告信息。
@SuppressWarnings JDK5 表示關閉當前定義元素的編譯器警告信息。
@SafeVarargs JDK7 表示關閉對調用具有泛型varargs參數的方法或構造函數的編譯器警告信息。
@FunctionalInterface JDK8 表示類型聲明爲函數式接口。

2 元註解

Java 定義了 5 種元註解,具體說明如下:

註解 說明 可用值
@Target 表示註解可以用於哪些地方。 CONSTRUCTOR:構造器;FIELD:字段(包括 enum 實例);LOCAL_VARIABLE:局部變量;METHOD:方法;PACKAGE:包;PARAMETER:參數;TYPE:類、接口(包括註解類型)或者 enum
@Retention 表示註解信息存活範圍 SOURCE:註解只在源代碼中,編譯器會將其丟棄;CLASS:註解在 class 文件中可用,VM 會將其丟棄;RUNTIME:註解在 VM 運行期也可用,因此,我們可以通過反射機制讀取註解信息。
@Documented 該註解可保存在 Javadoc 中
@Inherited 子類可繼承父類的註解
@Repeatable 允許該註解被多次使用

3 自定義註解

利用註解語法,我們就可以自行定義註解。假設我們定義一個可以在其中編寫 SQL Select 語句的註解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Select 語句註解
 * <p/>
 *
 * @author Deniro Lee
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
    String sql();
    String desc() default "無描述";
}

自定義註解很像接口定義,它使用了 @interface 作爲描述符,最終會被編譯成 class 文件。

註解可以包含元素,比如上述示例中的 Select 註解,就定義了 sql() 與 desc() 兩個元素。當然也可以不包含元素,不包含任何元素的註解稱爲標記註解(marker annotation)。

desc 元素定義了一個 default 值,即默認值。如果在使用該註解時,沒有實際給出 desc 的值,則註解處理器會使用這裏定義的默認值。

註解定義好後,可以這樣使用:

/**
 * 賬號 DAO
 * <p/>
 *
 * @author Deniro Lee
 */
public interface UserDao {

    @Select(sql = "select * from t_account where name='deniro'", desc = "依據用戶名獲取賬號")
    public void findUserByName();

    @Select(sql = "select * from t_account where id='1'")
    public void findUserById();
}

以 “名-值”對的形式使用自定義註解元素,,這些元素需要放置在 @Select 聲明之後的括號內。

4 處理自定義註解

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Method;

/**
 * @Select 註解處理器
 * <p/>
 *
 * @author Deniro Lee
 */
public class UserDaoTracker {

    private static final Logger logger = LogManager.getFormatterLogger();


    public static void main(String[] args) {
        for(Method m: UserDao.class.getDeclaredMethods()){
            Select select=m.getAnnotation(Select.class);
            if(select!=null){
                logger.info("查詢語句 -> %s ;描述 -> %s", select.sql(),select.desc());
            }
        }
    }
}

運行結果:

13:43.153 [main-23] INFO  - 查詢語句 -> select * from t_account where id='1' ;描述 -> 無描述
13:43.201 [main-23] INFO  - 查詢語句 -> select * from t_account where name='deniro' ;描述 -> 依據用戶名獲取賬號

這裏我們利用 Java 反射機制來查找 Select 註解。getAnnotation() 反射方法會返回指定類型的註解對象,如果沒找到,則會返回 null。然後通過調用註解對象所定義的方法來提取元素值。

5 註解元素類型

註解元素可以有以下類型:

  1. 所有基本類型(int、float、boolean等)
  2. String
  3. Class
  4. enum
  5. Annotation
  6. 以上類型的數組

如果使用了其他類型,那麼編譯器就會報錯。因爲有自動裝箱機制,所以也可以使用基本類型的包裝類型。

6 默認值

註解元素的默認值只能是以下兩種情況:

  1. 有默認值;
  2. 就在使用註解時提供元素的值。

注意: 不能使用 null 作爲默認值。但我們可以自定義一些特殊的值,比如空字符串或者負數,來表示某個元素不存在。形如:

String sql() default "";

7 嵌套註解

註解也可以作爲元素的類型,即註解嵌套。

(1)定義

假設,我們定義兩個註解,用於把查詢出的數據轉換爲結果集。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 結果集字段
 * <p/>
 *
 * @author Deniro Lee
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Result {
    /**
     * Java 字段
     *
     * @return
     */
    String property() default "";

    /**
     * 表字段
     *
     * @return
     */
    String column() default "";

    /**
     * 是否爲主鍵
     *
     * @return
     */
    boolean id() default false;
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 結果集
 * <p/>
 *
 * @author Deniro Lee
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Results {
    Result[] value();
}

Results 註解定義了一個唯一元素,就是另一個註解 Result 的數組。

(2)使用

結果集註解定義好以後,這樣使用:

    @Select(sql = "select * from t_account where id='1'")
    @Results({
            @Result(property = "id", column = "id", id = true),
            @Result(property = "roleName", column = "role_name")
    })
    public void findUserById();

@Results 註解中嵌套了多個 @Result 註解。

(3)處理

註解處理器,通過 getAnnotation 獲取類中定義的註解:

Method[] methods = UserDao.class.getDeclaredMethods();
        for (Method method : methods) {
            Annotation annotation = method.getAnnotation(Results.class);
            if (annotation instanceof Results) {
                log.info("annotation -> %s", annotation);

                Results results=(Results)annotation;
                Result[] resultArray=results.value();
                for (Result result : resultArray) {
                    log.info("result -> "+result);
                }
            }
        }

運行結果:

59:13.959 [main-37] INFO  - result -> @net.deniro.jdk.annotations.study.Result(property=id, column=id, id=true)
59:13.960 [main-37] INFO  - result -> @net.deniro.jdk.annotations.study.Result(property=roleName, column=role_name, id=false)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章