3-14 Python處理XML檔案
阿新 • • 發佈:2020-12-14
自定義註解
原文:https://zhuanlan.zhihu.com/p/60730622
之前也看過一些自定義註解的內容,但是因為沒有實際使用過,時隔3個月,現在來重新學習一下。
由於本人是個初學者,幾乎全文摘抄,加深一下映像。
註解簡介
註解(Annontation),Java5引入的新特性,位於java.lang.annotation包中。提供了一種安全的類似註釋的機制,用來將任何的資訊或元資料(metadata)與程式元素(類、方法、成員變數等)進行關聯。是一種說明、配置、描述性的資訊,與具體業務無關,也不會影響正常的業務邏輯。但我們可以用反射機制來進行校驗、賦值等操作。
常見的註解:@Override,@author,@param,@Deprecated,@SuppressWarnings。
註解的常見用途
- 生成文件的註解,如
@author
,@param
。 - 跟蹤程式碼依賴性,實現替代配置檔案功能,如spring mvc的註解。
- 編譯時進行格式檢查,如
@override
。 - 編譯時進行程式碼生成補全,如lombok外掛的
@Data
。
註解的定義
- 註解的定義通過
@interface
表示,所有的註解會自動繼承java.lang.Annotation
介面,且不能再繼承別的類或是介面。 - 註解的成員引數只能用
public
或預設(default
) 訪問權修飾來進行修飾。 - 成員引數只能使用八種基本型別(byte、short、char、int、long、float、double、boolean)和String、Enum、Class、annotations等資料型別,及其陣列。
- 獲取類方法和欄位的註解資訊,只能通過Java的反射技術來獲取
Annotation
物件。 - 註解可以沒有定義成員,只做標識。
元註解
元註解是專門用來註解其他註解的註解,聽起來有些繞口,實際上就是專門為自定義註解提供的註解。java.lang.annotation提供了四種元註解:
@Documented
– 註解是否將包含在JavaDoc中@Retention
– 什麼時候使用該註解@Target
– 註解用於什麼地方@Inherited
– 是否允許子類繼承該註解,在使用在類上面時,子類會自動繼承此註解@Repeatable
- 是否可重複註解,jdk1.8引入
註解的生命週期
通過@Retention
定義註解的生命週期,格式如下:
@Retention(RetentionPolicy.SOURCE)
其中RetentionPolicy的不同策略對應的生命週期如下:
RetentionPolicy.SOURCE
: 僅存在於原始碼中,編譯階段會被丟棄,不會包含於class位元組碼檔案中。@Override, @SuppressWarnings都屬於這類註解。RetentionPolicy.CLASS
: 預設策略,在class位元組碼檔案中存在,在類載入的時被丟棄,執行時無法獲取到。RetentionPolicy.RUNTIME
: 始終不會丟棄,可以使用反射獲得該註解的資訊。自定義的註解最常用的使用方式。
註解的作用目標
通過@Target定義註解作用的目標,比如作用於類、屬性、或方法等,預設可用於任何地方。格式如下:
@Target(ElementType.TYPE)
對應ElementType引數值適用範圍如下:
ElementType.TYPE
: 類、介面、註解、enumElementType.CONSTRUCTOR
: 建構函式ElementType.FIELD
: 成員變數、物件、屬性、列舉的常量ElementType.LOCAL_VARIABLE
: 區域性變數ElementType.METHOD
: 方法ElementType.PACKAGE
: 包ElementType.PARAMETER
: 引數ElementType.ANNOTATION_TYPE
: 註解ElementType.TYPE_PARAMETER
:型別引數,表示這個註解可以用在 Type的宣告式前,jdk1.8引入。ElementType.TYPE_USE
:型別的註解,表示這個註解可以用在所有使用Type的地方(如:泛型,型別轉換等),jdk1.8引入。
例項
下面通過一個例項來演示註解的使用:通過註解進行賦值和通過註解進行校驗。
自定義註解
這裡定義兩個註解,一個用來賦值,一個用來校驗。
/**
* 性別賦值
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface InitSex {
/**
* sex enum
*/
enum SEX_TYPE {MAN, WOMAN}
SEX_TYPE sex() default SEX_TYPE.MAN;
}
/**
* 年齡校驗
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface ValidateAge {
/**
* 最小值
*/
int min() default 18;
/**
* 最大值
*/
int max() default 99;
/**
* 預設值
*/
int value() default 20;
}
定義資料模型
這裡用User類來表示具體待處理的資料物件。
/**
* user
*/
public class User {
private String username;
@ValidateAge(min = 20, max = 35, value = 22)
private int age;
@InitSex(sex = InitSex.SEX_TYPE.MAN)
private String sex;
// 省略getter/setter方法
}
測試呼叫
具體測試呼叫的過程,參考程式碼中的註解,其中initUser方法來演示通過反射給屬性賦值,checkUser方法通過反射拿到當前屬性的值進行對比校驗。
import java.lang.reflect.Field;
public class TestInitParam {
public static void main(String[] args) throws IllegalAccessException {
User user = new User();
initUser(user);
// 年齡為0,校驗為通過情況
boolean checkResult = checkUser(user);
printResult(checkResult);
// 重新設定年齡,校驗通過情況
user.setAge(22);
checkResult = checkUser(user);
printResult(checkResult);
}
static void initUser(User user) throws IllegalAccessException {
// 獲取User類中所有的屬性(getFields無法獲得private屬性)
Field[] fields = User.class.getDeclaredFields();
// 遍歷所有屬性
for (Field field : fields) {
// 如果屬性上有此註解,則進行賦值操作
if (field.isAnnotationPresent(InitSex.class)) {
InitSex init = field.getAnnotation(InitSex.class);
field.setAccessible(true);
// 設定屬性的性別值
field.set(user, init.sex().toString());
System.out.println("完成屬性值的修改,修改值為:" + init.sex().toString());
}
}
}
static boolean checkUser(User user) throws IllegalAccessException {
// 獲取User類中所有的屬性(getFields無法獲得private屬性)
Field[] fields = User.class.getDeclaredFields();
boolean result = true;
// 遍歷所有屬性
for (Field field : fields) {
// 如果屬性上有此註解,則進行賦值操作
if (field.isAnnotationPresent(ValidateAge.class)) {
ValidateAge validateAge = field.getAnnotation(ValidateAge.class);
field.setAccessible(true);
int age = (int) field.get(user);
if (age < validateAge.min() || age > validateAge.max()) {
result = false;
System.out.println("年齡值不符合條件");
}
}
}
return result;
}
static void printResult(boolean checkResult) {
if (checkResult) {
System.out.println("校驗通過");
} else {
System.out.println("校驗未通過");
}
}
}
列印日誌
完成屬性值的修改,修改值為:MAN
年齡值不符合條件
校驗未通過
校驗通過