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 註解元素類型
註解元素可以有以下類型:
- 所有基本類型(int、float、boolean等)
- String
- Class
- enum
- Annotation
- 以上類型的數組
如果使用了其他類型,那麼編譯器就會報錯。因爲有自動裝箱機制,所以也可以使用基本類型的包裝類型。
6 默認值
註解元素的默認值只能是以下兩種情況:
- 有默認值;
- 就在使用註解時提供元素的值。
注意: 不能使用 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)