1. 程式人生 > 實用技巧 >3-14 Python處理XML檔案

3-14 Python處理XML檔案

自定義註解

原文: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: 類、介面、註解、enum
  • ElementType.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
年齡值不符合條件
校驗未通過
校驗通過