Содержание этой статьи основано на JDK8. Аннотации появились в JDK5, а в последующих версиях JDK часть содержимого была расширена Аннотации, в которых явно не указана версия в этой статье, уже поддерживаются JDK5.
:notebook: Эта статья была заархивирована в: "blog"
:keyboard: Пример кода в этой статье был заархивирован в: "javacore"
Введение
форма аннотации
В Java аннотации@
Модификатор начала персонажа. следующее:
@Override
void mySuperMethod() { ... }
Аннотации могут содержать именованные или неименованные свойства, и эти свойства имеют значения.
@Author(
name = "Benjamin Franklin",
date = "3/27/2003"
)
class MyClass() { ... }
Если имеется только одно свойство с именем value, имя можно не указывать, например:
@SuppressWarnings("unchecked")
void myMethod() { ... }
Если аннотация не является атрибутом,标记注解
. как:@Override
.
что такое аннотации
По сути,Аннотация — это своего рода тег, который по сути можно рассматривать как особый вид аннотации, он ничем не сильнее обычной аннотации без кода для ее разбора.
Разбор аннотации часто принимает две формы:
- Прямое сканирование- Сканирующий компилятор компилятор относится к классу или модифицированному способу для обработки кода Java Commited Bytecode обнаружит некоторые заметки, то он выполнит некоторую обработку по этим аннотациям. Это относится только к встроенным нотам класса JDK.
- отражение во время выполнения- Если вы хотите настроить аннотацию, компилятор Java не может распознать и обработать эту аннотацию, он может только выбрать, следует ли компилировать ее в файл байт-кода в соответствии с областью действия аннотации. Если вы хотите обрабатывать аннотации, вы должны использовать технологию отражения, чтобы идентифицировать аннотацию и информацию, которую она несет, а затем обрабатывать ее соответствующим образом.
Роль аннотаций
Аннотации имеют множество применений:
- Информация о компиляторе. Аннотации могут использоваться компилятором для обнаружения ошибок или подавления предупреждений.
- Обработка во время компиляции и во время развертывания. Программы могут обрабатывать информацию аннотаций для создания кода, XML-файлов и т. д.
- Обработка времени выполнения - определенные аннотации могут быть проверены и обработаны во время выполнения.
В качестве программиста Java я испытал страх доминирования различных файлов конфигурации (XML, свойства). Слишком много файлов конфигурации могут затруднить удержание проекта. На мой взгляд, использование аннотаций для уменьшения файлов конфигурации или кода является наибольшим использованием аннотаций.
Стоимость аннотации
Во всем есть плюсы и минусы, и то же самое можно сказать и о технологии аннотаций. Существует также цена использования аннотаций:
- Очевидно, что это навязчивое программирование, поэтому, естественно, возникает проблема увеличения связанности программ.
- Обработка пользовательских аннотаций требует получения атрибутов с помощью технологии отражения во время выполнения. Если элемент, измененный аннотацией, является закрытым членом класса, его также можно получить с помощью отражения. Это нарушает объектно-ориентированную инкапсуляцию.
- Проблемы, вызванные аннотациями, относительно трудно отлаживать или локализовать.
Однако, как говорится, цена, уплаченная аннотациями, приемлема по сравнению с функциями, которые она обеспечивает.
Примечание
Аннотации можно применять к объявлениям классов, полей, методов и других элементов программы.
Начиная с JDK8 область применения аннотаций была дополнительно расширена.Ниже приведены новые области применения:
Выражение инициализации экземпляра класса:
new @Interned MyObject();
Преобразование типа:
myString = (@NonNull String) str;
Объявление реализации интерфейса:
class UnmodifiableList<T> implements
@Readonly List<@Readonly T> {}
Бросьте оператор исключения:
void monitorTemperature()
throws @Critical TemperatureException {}
Встроенные аннотации
В JDK встроены следующие аннотации:
@Override
@Deprecated
@SuppressWarnnings
-
@SafeVarargs
(Введено в JDK7) -
@FunctionalInterface
(Введено в JDK8)
@Override
@Override
Используется для указания того, что измененный метод переопределяет метод родительского класса.
Если вы попытаетесь использовать@Override
Когда тег на самом деле не перезаписан метод родительского класса, Compiler Java будет тревожить.
@Override
Пример:
public class OverrideAnnotationDemo {
static class Person {
public String getName() {
return "getName";
}
}
static class Man extends Person {
@Override
public String getName() {
return "override getName";
}
/**
* 放开下面的注释,编译时会告警
*/
/*
@Override
public String getName2() {
return "override getName2";
}
*/
}
public static void main(String[] args) {
Person per = new Man();
System.out.println(per.getName());
}
}
@Deprecated
@Deprecated
Он используется, чтобы указать, что модифицированный класс или член класса, метод класса устарел, и его не рекомендуется использовать.
@Deprecated
иметь определенныйпреемственность: Если мы используем устаревший класс или член класса в нашем коде путем наследования или переопределения, даже если подкласс или подметод не помечен как@Deprecated
, но компилятор все равно предупреждает.
Уведомление:
@Deprecated
Эти типы аннотаций и javadoc@deprecated
Этот тег отличается: первый распознается компилятором java, второй распознается инструментом javadoc для создания документации (включая описание того, почему член программы устарел, как его следует запретить или заменить).
@Deprecated
Пример:
public class DeprecatedAnnotationDemo {
static class DeprecatedField {
@Deprecated
public static final String DEPRECATED_FIELD = "DeprecatedField";
}
static class DeprecatedMethod {
@Deprecated
public String print() {
return "DeprecatedMethod";
}
}
@Deprecated
static class DeprecatedClass {
public String print() {
return "DeprecatedClass";
}
}
public static void main(String[] args) {
System.out.println(DeprecatedField.DEPRECATED_FIELD);
DeprecatedMethod dm = new DeprecatedMethod();
System.out.println(dm.print());
DeprecatedClass dc = new DeprecatedClass();
System.out.println(dc.print());
}
}
//Output:
//DeprecatedField
//DeprecatedMethod
//DeprecatedClass
@SuppressWarnnings
@SuppressWarnings
Используется для отключения определенных предупреждений, генерируемых во время компиляции для классов, методов и членов.
@SuppressWarning
Не разметка аннотации. Он имеет типString[]
Член массива , в котором хранится тип будильника, который нужно закрыть. Для компилятора javac да-Xlint
Эффективные варианты предупреждения также для имени@SuppressWarings
Действителен, и компилятор игнорирует нераспознанные имена предупреждений.
@SuppressWarning
Пример:
@SuppressWarnings({"rawtypes", "unchecked"})
public class SuppressWarningsAnnotationDemo {
static class SuppressDemo<T> {
private T value;
public T getValue() {
return this.value;
}
public void setValue(T var) {
this.value = var;
}
}
@SuppressWarnings({"deprecation"})
public static void main(String[] args) {
SuppressDemo d = new SuppressDemo();
d.setValue("南京");
System.out.println("地名:" + d.getValue());
}
}
@SuppressWarnings
Краткое описание общих значений параметров для аннотаций:
-
deprecation
- предупреждения при использовании устаревших классов или методов; -
unchecked
- предупреждения при выполнении непроверенных преобразований, например, при использовании коллекций без использования Generics для указания типа коллекции; -
fallthrough
- предупреждение, когда блок Switch ведет прямо к следующему кейсу без Break; -
path
- предупреждения при наличии несуществующих путей в пути к классам, путях к исходным файлам и т.д.; -
serial
- Предупреждение об отсутствии определения SerialVersionuid в сериализованном классе; -
finally
- предупреждение, когда любое предложение finally не может быть завершено нормально; -
all
- Все предупреждения.
@SuppressWarnings({"uncheck", "deprecation"})
public class InternalAnnotationDemo {
/**
* @SuppressWarnings 标记消除当前类的告警信息
*/
@SuppressWarnings({"deprecation"})
static class A {
public void method1() {
System.out.println("call method1");
}
/**
* @Deprecated 标记当前方法为废弃方法,不建议使用
*/
@Deprecated
public void method2() {
System.out.println("call method2");
}
}
/**
* @Deprecated 标记当前类为废弃类,不建议使用
*/
@Deprecated
static class B extends A {
/**
* @Override 标记显示指明当前方法覆写了父类或接口的方法
*/
@Override
public void method1() { }
}
public static void main(String[] args) {
A obj = new B();
obj.method1();
obj.method2();
}
}
@SafeVarargs
@SafeVarargs
Представлен в JDK7.
@SafeVarargs
Роль: сообщает компилятору, что дженерики в параметрах переменной длины являются типобезопасными. Параметры переменной длины хранятся с использованием массивов, а массивы и дженерики плохо сочетаются.
Проще говоря, типы данных элементов массива определяются во время компиляции и во время выполнения, в то время как типы данных дженериков могут быть определены только во время выполнения. Поэтому при хранении универсального типа в массиве компилятор не может подтвердить соответствие типа данных на этапе компиляции, поэтому будет выдано предупреждающее сообщение, то есть если реальный тип данных универсального типа не может соответствовать типу массива параметров, это вызоветClassCastException
аномальный.
@SafeVarargs
Примечания используют:
-
@SafeVarargs
Аннотации можно использовать в конструкторах. -
@SafeVarargs
Аннотации можно использовать дляstatic
илиfinal
метод.
@SafeVarargs
Пример:
public class SafeVarargsAnnotationDemo {
/**
* 此方法实际上并不安全,不使用此注解,编译时会告警
*/
@SafeVarargs
static void wrongMethod(List<String>... stringLists) {
Object[] array = stringLists;
List<Integer> tmpList = Arrays.asList(42);
array[0] = tmpList; // 语法错误,但是编译不告警
String s = stringLists[0].get(0); // 运行时报 ClassCastException
}
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
List<String> list2 = new ArrayList<>();
list.add("1");
list.add("2");
wrongMethod(list, list2);
}
}
Приведенный выше код, если не используется@SafeVarargs
, он предупредит при компиляции
[WARNING] /D:/Codes/ZP/Java/javacore/codes/basics/src/main/java/io/github/dunwu/javacore/annotation/SafeVarargsAnnotationDemo.java: 某些输入文件使用了未经检查或不安全的操作。
[WARNING] /D:/Codes/ZP/Java/javacore/codes/basics/src/main/java/io/github/dunwu/javacore/annotation/SafeVarargsAnnotationDemo.java: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。
@FunctionalInterface
@FunctionalInterface
Введен в JDK8.
@FunctionalInterface
Используется для указания того, что декорируемый интерфейс является функциональным интерфейсом.
Следует отметить, что если интерфейс соответствует определению «функциональный интерфейс»,@FunctionalInterface
Это нормально, но если вы не пишете функциональный интерфейс, используйте@FunctionInterface
, то компилятор сообщит об ошибке.
Что такое функциональный интерфейс?
Функциональный интерфейс (Functional Interface) — это один и только один абстрактный метод, но может быть множество неабстрактных методов интерфейса. Функциональный интерфейс можно неявно преобразовать в лямбда-выражение.
Особенности функционального интерфейса:
- Интерфейс имеет и может иметь только один абстрактный метод (абстрактный метод имеет только определение метода, без тела метода).
- Вы не можете переопределить публичный метод в классе Object в интерфейсе (компилятор сообщит об ошибке, если вы его напишете).
- Разрешены реализации по умолчанию.
Пример:
public class FunctionalInterfaceAnnotationDemo {
@FunctionalInterface
public interface Func1<T> {
void printMessage(T message);
}
/**
* @FunctionalInterface 修饰的接口中定义两个抽象方法,编译时会报错
* @param <T>
*/
/*@FunctionalInterface
public interface Func2<T> {
void printMessage(T message);
void printMessage2(T message);
}*/
public static void main(String[] args) {
Func1 func1 = message -> System.out.println(message);
func1.printMessage("Hello");
func1.printMessage(100);
}
}
метааннотация
Хотя в JDK есть несколько встроенных аннотаций, они далеки от удовлетворения постоянно меняющихся потребностей, возникающих в процессе разработки. Итак, нам нужны пользовательские аннотации, а для этого требуется использование метааннотаций.
Роль мета-аннотаций заключается в определении других аннотаций..
В Java доступны следующие типы метааннотаций:
@Retention
@Target
@Documented
-
@Inherited
(Введено в JDK8) -
@Repeatable
(Введено в JDK8)
Эти типы и классы они поддерживаютjava.lang.annotation
можно найти в упаковке. Давайте посмотрим на роль каждой мета-аннотации и инструкции по использованию соответствующих подпараметрических параметров.
@Retention
@Retention
Указывает уровень хранения аннотации.
@Retention
Исходный код:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
RetentionPolicy
тип перечисления, определяющий@Retention
Уровни хранения, поддерживаемые оформленными аннотациями:
-
RetentionPolicy.SOURCE
- Отмеченные аннотации допустимы только в исходных файлах и игнорируются компилятором. -
RetentionPolicy.CLASS
- Отмеченные аннотации допустимы в файлах классов и будут игнорироваться JVM. -
RetentionPolicy.RUNTIME
- Отмеченные аннотации действительны во время выполнения.
@Retention
Пример:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
@Documented
@Documented
Указывает, что Javadoc следует использовать всякий раз, когда используется указанная аннотация (по умолчанию аннотации не включаются в Javadoc). Для получения дополнительной информации см.:Javadoc tools page.
@Documented
Пример:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
@Target
@Target
Указывает типы элементов, которые может украшать аннотация.
@Target
Исходный код:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
ElementType
тип перечисления, определяющий@Target
Измененные аннотации могут быть применены к:
-
ElementType.ANNOTATION_TYPE
- Отмеченные аннотации можно применять к типам аннотаций. -
ElementType.CONSTRUCTOR
- Отмеченные аннотации можно применять к конструкторам. -
ElementType.FIELD
- Отмеченные аннотации можно применять к полям или свойствам. -
ElementType.LOCAL_VARIABLE
- Отмеченные аннотации можно применять к локальным переменным. -
ElementType.METHOD
- К методам можно применять отмеченные аннотации. -
ElementType.PACKAGE
- отмеченные аннотации могут быть применены к декларации пакетов. -
ElementType.PARAMETER
- отмеченные аннотации могут быть применены к параметрам метода. -
ElementType.TYPE
- Отмеченные аннотации можно применять к любому элементу класса.
@Target
Пример:
@Target(ElementType.TYPE)
public @interface Table {
/**
* 数据表名称注解,默认值为类名称
* @return
*/
public String tableName() default "className";
}
@Target(ElementType.FIELD)
public @interface NoDBColumn {}
@Inherited
@Inherited
Указывает, что тип аннотации может быть унаследован (не по умолчанию).
Указывает на автоматическое наследование типов аннотаций. Если объявление типа аннотации существует@Inherited
Задержанные аннотации все подклассы модифицированного класса наследуют эту аннотацию.
Уведомление:
@Inherited
Типы аннотаций наследуются подклассами аннотированного класса. Класс не наследует аннотации от интерфейсов, которые он реализует, а методы не наследуют аннотации от методов, которые он переопределяет.Кроме того, когда
@Inherited
Аннотировано аннотациями типа@Retention
даRetentionPolicy.RUNTIME
, API отражения расширяет возможности этого наследования. если мы используемjava.lang.reflect
расспросить об одном@Inherited
Когда типы примечаний, проверки начнут код отражения работы: проверка класса и его родитель, пока обнаружение указанного типа аннотации не обнаружено, или на верхний этаж класса наследования структуры.
@Inherited
public @interface Greeting {
public enum FontColor{ BULE,RED,GREEN};
String name();
FontColor fontColor() default FontColor.GREEN;
}
@Repeatable
@Repeatable
Указывает, что аннотацию можно использовать повторно.
с весной@Scheduled
Например:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Schedules {
Scheduled[] value();
}
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
// ...
}
Пример приложения:
public class TaskRunner {
@Scheduled("0 0/15 * * * ?")
@Scheduled("0 0 12 * ?")
public void task1() {}
}
пользовательская аннотация
использовать@interface
Когда пользовательские аннотации автоматически наследуютсяjava.lang.annotation.Annotation
Интерфейс, остальные детали выполняются автоматически компилятором. При определении аннотации другие аннотации или интерфейсы не могут наследоваться.@interface
Используется для объявления аннотации, где каждый метод фактически объявляет параметр конфигурации. Имя метода — это имя параметра, а тип возвращаемого значения — тип параметра (тип возвращаемого значения может быть только базовыми типами, Class, String, enum). в состоянии пройтиdefault
объявить значения по умолчанию для параметров.
Здесь я реализуюRegexValid
Обычный инструмент проверки аннотаций, показывающий полные шаги пользовательской аннотации.
1. Определение аннотаций
Синтаксис аннотаций следующий:
public @interface 注解名 {定义体}
Давайте определим аннотацию:
@Documented
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface RegexValid {}
инструкция:
Через предыдущий раздел для мета-аннотаций
@Target
,@Retention
,@Documented
Здесь легко понять.
- Приведенный выше код определяет
@RegexValid
аннотации.@Documented
выражать@RegexValid
следует использовать javadoc.@Target({ElementType.FIELD, ElementType.PARAMETER})
выражать@RegexValid
Может быть оформлен на членах класса или параметрах метода.- @Retention(RetentionPolicy.RUNTIME) означает
@RegexValid
Действует во время выполнения.
На данный момент мы определили аннотацию без каких-либо атрибутов, и пока это просто аннотация разметки. Как регуляризатор, вы ничего не можете сделать без атрибутов. Далее мы добавим к нему атрибуты аннотации.
2. Свойства аннотации
Синтаксис атрибута аннотации следующий:
[访问级别修饰符] [数据类型] 名称() default 默认值;
Например, мы хотим определить строковое свойство с именем value в аннотации, его значение по умолчанию — пустая строка, а уровень доступа — уровень по умолчанию, тогда его следует определить следующим образом:
String value() default "";
Уведомление:В аннотации, когда мы определяем атрибут, необходимо добавить имя атрибута.
()
.
Определение свойств аннотации имеет следующие точки:
-
Атрибуты аннотации можно использовать только
public
или уровень доступа по умолчанию (т.е. без указания модификатора уровня доступа) оформленный. -
Существуют ограничения на тип данных атрибута аннотации.. Поддерживаются следующие типы данных:
- Все основные типы данных (byte, char, short, int, long, float, double, boolean)
- Тип строки
- класс класс
- тип перечисления
- Тип аннотации
- Массивы всех вышеперечисленных типов
-
Атрибут аннотации должен иметь определенное значение, рекомендуется указать значение по умолчанию. Свойства аннотации можно указать, только указав значение по умолчанию или при использовании аннотации, что является более надежным способом указать значение по умолчанию. Если свойство аннотации является ссылочным типом, оно не может иметь значение null. Это ограничение затрудняет для процессора аннотаций определение того, является ли атрибут аннотации значением по умолчанию или значением атрибута, указанным при использовании аннотации. По этой причине, когда мы устанавливаем значения по умолчанию, мы обычно определяем некоторые специальные значения, такие как пустые строки или отрицательные числа.
-
Если только одна аннотация значения атрибута будет названа лучшим значением. Поскольку указанное свойство называется значением, при использовании аннотаций значение указанного значения не может быть указано именем свойства.
// 这两种方式效果相同
@RegexValid("^((\\+)?86\\s*)?((13[0-9])|(15([0-3]|[5-9]))|(18[0,2,5-9]))\\d{8}$")
@RegexValid(value = "^((\\+)?86\\s*)?((13[0-9])|(15([0-3]|[5-9]))|(18[0,2,5-9]))\\d{8}$")
Пример:
Знание основных моментов определения аннотационных атрибутов, давайте@RegexValid
Аннотация определяет несколько свойств.
@Documented
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface RegexValid {
enum Policy {
// @formatter:off
EMPTY(null),
DATE("^(?:(?!0000)[0-9]{4}([-/.]?)(?:(?:0?[1-9]|1[0-2])\\1(?:0?[1-9]|1[0-9]|2[0-8])|(?:0?[13-9]|1[0-2])\\1"
+ "(?:29|30)|(?:0?[13578]|1[02])\\1(?:31))|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|"
+ "(?:0[48]|[2468][048]|[13579][26])00)([-/.]?)0?2\\2(?:29))$"),
MAIL("^[A-Za-z0-9](([_\\.\\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\\.\\-]?[a-zA-Z0-9]+)*)\\.([A-Za-z]{2,})$");
// @formatter:on
private String policy;
Policy(String policy) {
this.policy = policy;
}
public String getPolicy() {
return policy;
}
}
String value() default "";
Policy policy() default Policy.EMPTY;
}
инструкция:
В приведенном выше примере кода мы определили два свойства аннотации:
String
атрибут value типа иPolicy
Атрибут политики типа перечисления.Policy
В перечислении определены несколько регулярных выражений по умолчанию, которые предназначены для непосредственного использования этих общих выражений для регулярной проверки. Учитывая, что нам может потребоваться передать некоторые пользовательские регулярные выражения для проверки других сценариев, атрибут value определен, чтобы позволить пользователям передавать регулярные выражения.
Уже,@RegexValid
заявление закончилось. Однако программа по-прежнему не знает, что делать.@RegexValid
эта аннотация. Нам также необходимо определить процессоры аннотаций.
3. Обработчик аннотаций
Аннотации не более полезны, чем аннотации, если нет возможности и труда их прочитать. Важной частью использования аннотаций является создание процессора аннотаций. JDK5 расширяет API механизма отражения, чтобы помочь программистам быстро создавать собственные процессоры аннотаций.
java.lang.annotation.Annotation
Это интерфейс.Программа может получить объект аннотации указанного элемента программы посредством отражения, а затем получить метаданные в аннотации через объект аннотации..
Annotation
Исходный код интерфейса выглядит следующим образом:
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
Кроме того, Java поддерживаетИнтерфейс процессора аннотацийjava.lang.reflect.AnnotatedElement
, интерфейс представляет элементы программы в программе, которые могут принимать аннотации.Интерфейс в основном имеет следующие классы реализации:
-
Class
- определение класса -
Constructor
- Определение конструктора -
Field
- Усталые определения переменных участников -
Method
- определение метода класса -
Package
- определение пакета класса
java.lang.reflect
Пакет в основном содержит некоторые классы инструментов, которые реализуют функцию отражения. По факту,java.lang.reflect
API отражения, предоставляемый пакетом, расширяет возможности чтения аннотаций во время выполнения. Когда тип аннотации определен как аннотация времени выполнения, аннотация может быть видна во время выполнения, а аннотация, сохраненная в файле класса при загрузке файла класса, будет прочитана виртуальной машиной.AnnotatedElement
Интерфейс является родительским интерфейсом всех элементов программы (класса, метода и конструктора), поэтому программа получаетAnnotatedElement
После объекта программа может вызвать следующие четыре метода объекта для доступа к аннотационной информации:
-
getAnnotation
- Возвращает аннотацию указанного типа, которая существует в этом программном элементе, или null, если аннотация типа не существует. -
getAnnotations
- Возвращает все аннотации, которые существуют на этом элементе программы. -
isAnnotationPresent
- Определяет, содержит ли программный элемент аннотацию указанного типа, возвращает true, если она существует, иначе возвращает false. -
getDeclaredAnnotations
- Возвращает все аннотации, существующие непосредственно в этом элементе. В отличие от других методов в этом интерфейсе, этот метод игнорирует унаследованные аннотации. (Если непосредственно для этого элемента не существует аннотации, возвращается массив нулевой длины.) Вызывающие этот метод могут свободно изменять возвращаемый массив по своему желанию; это не влияет на массивы, возвращаемые другими вызывающими объектами.
Зная вышеизложенное, реализуем@RegexValid
Процессор аннотаций для:
import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexValidUtil {
public static boolean check(Object obj) throws Exception {
boolean result = true;
StringBuilder sb = new StringBuilder();
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
// 判断成员是否被 @RegexValid 注解所修饰
if (field.isAnnotationPresent(RegexValid.class)) {
RegexValid valid = field.getAnnotation(RegexValid.class);
// 如果 value 为空字符串,说明没有注入自定义正则表达式,改用 policy 属性
String value = valid.value();
if ("".equals(value)) {
RegexValid.Policy policy = valid.policy();
value = policy.getPolicy();
}
// 通过设置 setAccessible(true) 来访问私有成员
field.setAccessible(true);
Object fieldObj = null;
try {
fieldObj = field.get(obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (fieldObj == null) {
sb.append("\n")
.append(String.format("%s 类中的 %s 字段不能为空!", obj.getClass().getName(), field.getName()));
result = false;
} else {
if (fieldObj instanceof String) {
String text = (String) fieldObj;
Pattern p = Pattern.compile(value);
Matcher m = p.matcher(text);
result = m.matches();
if (!result) {
sb.append("\n").append(String.format("%s 不是合法的 %s !", text, field.getName()));
}
} else {
sb.append("\n").append(
String.format("%s 类中的 %s 字段不是字符串类型,不能使用此注解校验!", obj.getClass().getName(), field.getName()));
result = false;
}
}
}
}
if (sb.length() > 0) {
throw new Exception(sb.toString());
}
return result;
}
}
инструкция:
Для процессора аннотации в приведенном выше примере этапы выполнения следующие:
- Получите все элементы входящего объекта с помощью метода отражения getDeclaredFields.
- Пройдитесь по элементам и используйте isAnnotationPresent, чтобы определить, изменен ли элемент указанной аннотацией, если нет, пропустите ее напрямую.
- Если элемент изменен аннотацией, передайте
RegexValid valid = field.getAnnotation(RegexValid.class);
Эта форма получена, аннотирована для создания экземпляра объекта, а затем вы можете использоватьvalid.value()
илиvalid.policy()
Эта форма получает значение атрибута, установленное в аннотации.- Логическая обработка выполняется в соответствии со значением атрибута.
4. Используйте аннотации
После завершения вышеуказанной работы мы можем использовать пользовательские аннотации, примеры следующие:
public class RegexValidDemo {
static class User {
private String name;
@RegexValid(policy = RegexValid.Policy.DATE)
private String date;
@RegexValid(policy = RegexValid.Policy.MAIL)
private String mail;
@RegexValid("^((\\+)?86\\s*)?((13[0-9])|(15([0-3]|[5-9]))|(18[0,2,5-9]))\\d{8}$")
private String phone;
public User(String name, String date, String mail, String phone) {
this.name = name;
this.date = date;
this.mail = mail;
this.phone = phone;
}
@Override
public String toString() {
return "User{" + "name='" + name + '\'' + ", date='" + date + '\'' + ", mail='" + mail + '\'' + ", phone='"
+ phone + '\'' + '}';
}
}
static void printDate(@RegexValid(policy = RegexValid.Policy.DATE) String date){
System.out.println(date);
}
public static void main(String[] args) throws Exception {
User user = new User("Tom", "1990-01-31", "xxx@163.com", "18612341234");
User user2 = new User("Jack", "2019-02-29", "sadhgs", "183xxxxxxxx");
if (RegexValidUtil.check(user)) {
System.out.println(user + "正则校验通过");
}
if (RegexValidUtil.check(user2)) {
System.out.println(user2 + "正则校验通过");
}
}
}