1. Введение в рефлексию
1.1 Отражение
Отражение относится к способности программы получать доступ, обнаруживать и изменять свое собственное состояние или поведение.
1.2 механизм отражения Java
Механизм отражения в java означает, что в рабочем состоянии программы для любого класса можно получить атрибуты и методы этого класса, для любого объекта можно вызвать атрибуты и методы этого объекта. вызов метода объекта называется механизмом отражения java. Одним словом: механизм отражения позволяет получить свойства и методы любого класса и вызвать их во время работы программы.
1.3 Основные функции отражения Java
- Создает объект класса во время выполнения;
- Получить переменные-члены и методы класса во время выполнения;
- Вызов метода любого объекта во время выполнения;
- Сгенерировать динамический прокси; На самом деле, основная функция отражения, как мне кажется, заключается в том, чтобы использовать его с фреймворком.
1.4 типы классов Java
Чтобы понять отражение, вам сначала нужно знать класс Class, полное имя которого — класс java.lang.Class. Java - объектно-ориентированный язык. Он обращает внимание на то, что все является объектом. Даже если он мощный для класса, он все равно является объектом другого класса (класса класса). Другими словами, обычный класс является объектом класс Класс, то есть Класс - это класс всех классов.(Есть класс с именем Класс). Для обычных объектов мы обычно создаем их так:
Code code1 = new Code();
Как было сказано выше, все классы являются объектами Class, поэтому для их представления можно использовать следующие методы:
Class c = new Class();
Но когда мы смотрим на исходный код класса, он написан так:
private Class(ClassLoader loader) {
classLoader = loader;
}
Видно, что конструктор является закрытым.Только JVM может вызвать этот конструктор для создания объекта класса, поэтому невозможно создать новый объект класса, как обычный класс.Хотя мы не можем создать новый объект класса, мы можем передать существующий класс. Есть три способа получить объект класса, а именно:
Class c1 = Test.class; 这说明任何一个类都有一个隐含的静态成员变量class,这种方式是通过获取类的静态成员变量class得到的
Class c2 = test.getClass(); test是Test类的一个对象,这种方式是通过一个类的对象的getClass()方法获得的
Class c3 = Class.forName("com.catchu.me.reflect.Test"); 这种方法是Class类调用forName方法,通过一个类的全量限定名获得
Здесь c1, c2 и c3 — все объекты класса, они совершенно одинаковы и имеют научное название, называемое типом класса Test. Здесь странно, разве там не сказано, что Test — это объект класса, а c1, c2 и c3 — тоже объекты класса, так что разве Test не то же самое, что c1, c2 и c3? Почему тип класса называется Test? Не беспокойтесь о том, одинаковы ли они здесь, просто поймите, что делает тип класса.Как следует из названия, тип класса - это тип класса, то есть он описывает, что такое класс и что он имеет, поэтому мы можем узнать класс через тип класса, свойства и методы класса, и можем вызывать свойства и методы класса, что является основой отражения. Образец кода:
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Class<Test> class1 = Test.class;
System.out.println("类名1:"+class1.getName());
Test Test = new Test();
Class<? extends Test> class2 = Test.getClass();
System.out.println("类名2:"+class2.getName());
Class<?> class3 = Class.forName("com.catchu.me.reflect.Test");
System.out.println("类名3:"+class3.getName());
if(class1==class2){
System.out.println("class1==class2");
}
if(class1==class3){
System.out.println("class1==class3");
}
}
}
Выходной результат:
类名1:com.catchu.me.reflect.Test
类名2:com.catchu.me.reflect.Test
类名3:com.catchu.me.reflect.Test
class1==class2
class1==class3
2. Операция отражения
Операция отражения java в основном использует класс java.lang.Class и классы в пакете отражения java.lang.reflect. Как упоминалось выше, мы уже можем получить информацию о классе класса. Согласно этому классу, мы можем использовать некоторые методы для обработки (получения) следующей информации о классе:
2.1 Конструктор операций
Все является объектом. Конструктор класса является объектом класса java.lang.reflect.Constructor. Объект конструктора можно получить с помощью следующих методов класса:
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) // 获得该类所有的构造器,不包括其父类的构造器
public Constructor<T> getConstructor(Class<?>... parameterTypes) // 获得该类所有public构造器,包括父类
//具体
Constructor<?>[] allConstructors = class1.getDeclaredConstructors();//获取class对象的所有声明构造函数
Constructor<?>[] publicConstructors = class1.getConstructors();//获取class对象public构造函数
Constructor<?> constructor = class1.getDeclaredConstructor(String.class);//获取指定声明构造函数(局部变量是一个字符串类型的)
Constructor publicConstructor = class1.getConstructor(String.class);//获取指定声明的public构造函数
Код теста выглядит следующим образом:
public class TestConstructor {
public static void main(String[] args) throws Exception{
Class<?> personClass = Class.forName("com.catchu.me.reflect.Person");
//获取所有的构造函数,包括私有的,不包括父类的
Constructor<?>[] allConstructors = personClass.getDeclaredConstructors();
//获取所有公有的构造函数,包括父类的
Constructor<?>[] publicConstructors = personClass.getConstructors();
System.out.println("遍历之后的构造函数:");
for(Constructor c1 : allConstructors){
System.out.println(c1);
}
Constructor<?> c2 = personClass.getDeclaredConstructor(String.class);
c2.setAccessible(true); //设置是否可访问,因为该构造器是private的,所以要手动设置允许访问,如果构造器是public的就不用设置
Object person = c2.newInstance("刘俊重"); //使用反射创建Person类的对象,并传入参数
System.out.println(person.toString());
}
}
Класс Person выглядит следующим образом и содержит закрытый конструктор для проверки эффекта:
public class Person {
private int age;
private String name;
public Person() {
}
private Person(String name){
this.name = name;
}
public Person(int age,String name){
this.age = age;
this.name = name;
}
//省略set/get方法
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
Результаты теста следующие:
遍历之后的构造函数:
public com.catchu.me.reflect.Person(int,java.lang.String)
private com.catchu.me.reflect.Person(java.lang.String)
public com.catchu.me.reflect.Person()
Person{age=0, name='刘俊重'}
Из вышеизложенного видно, что после получения типа класса Class определенного класса, мы можем через метод в пакете отражения получить конструктор этого класса, а затем создать объект этого класса.
2.2 Управление переменными-членами
Все является объектом. Переменная-член класса является объектом класса java.lang.reflect.Field. Переменная-член класса может быть получена с помощью следующих методов класса Class. Стоит отметить, что переменная состоит из двух частей: типа переменной и имени переменной:
public Field getDeclaredField(String name) // 获得该类自身声明的所有变量,不包括其父类的变量
public Field getField(String name) // 获得该类自所有的public成员变量,包括其父类变量
//具体实现
Field[] allFields = class1.getDeclaredFields();//获取class对象的所有属性
Field[] publicFields = class1.getFields();//获取class对象的public属性
Field ageField = class1.getDeclaredField("age");//获取class指定属性
Field desField = class1.getField("des");//获取class指定的public属性
Пример кода выглядит следующим образом:
public class TestField {
public static void main(String[] args) throws Exception{
Class<Person> personClass = Person.class;
//获取所有的成员变量,包含私有的
Field[] allFields = personClass.getDeclaredFields();
//获取所有公有的成员变量,包含父类的
Field[] publicFields = personClass.getFields();
System.out.println("所有的成员变量:");
for(Field f : allFields){
System.out.println(f);
}
//获取某个变量的值
//创建对象的实例
Constructor<Person> c = personClass.getDeclaredConstructor(String.class);
c.setAccessible(true); //因为该构造函数时私有的,需要在这里设置成可访问的
Person person = c.newInstance("刘俊重");
//获取变量name对象
Field field = personClass.getDeclaredField("name");
field.setAccessible(true); //因为变量name是私有的,需要在这里设置成可访问的
//注意对比下面这两行,官方对field.get(Object obj)方法的解释是返回对象obj字段field的值
Object value = field.get(person);
//String name = person.getName();
System.out.println("获取的变量的值是:"+value);
}
}
Результат выглядит следующим образом:
所有的成员变量:
private int com.catchu.me.reflect.Person.age
private java.lang.String com.catchu.me.reflect.Person.name
获取的变量的值是:刘俊重
Здесь следует обратить внимание на метод field.get(person) Традиционный метод получения атрибутов по объекту через: String name = person.getName() В рефлексии можно передать: field.get(object) , что также является определенным способом получения объекта.Поле, чем-то похожее на метод вызова.
2.3 Методы члена операции
Все является объектом, метод-член класса является объектом java.lang.reflect.Method, метод-член класса можно получить с помощью следующих методов класса java.lang.Class и с помощью некоторых предоставленных методов методом класса Method, его можно получить, вызвав метод-член.
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到该类所有的方法,不包括父类的
public Method getMethod(String name, Class<?>... parameterTypes) // 得到该类所有的public方法,包括父类的
//具体使用
Method[] methods = class1.getDeclaredMethods();//获取class对象的所有声明方法
Method[] allMethods = class1.getMethods();//获取class对象的所有public方法 包括父类的方法
Method method = class1.getMethod("info", String.class);//返回此class1对应的public修饰的方法名是info的,包含一个String类型变量的方法
Method declaredMethod = class1.getDeclaredMethod("info", String.class);//返回此Class对象对应类的、带指定形参列表的方法
Код теста выглядит следующим образом:
public class TestMethod {
public static void main(String[] args) throws Exception {
Person person = new Person();
Class<? extends Person> personClass = person.getClass();
Method[] allMethods = personClass.getDeclaredMethods();
Method[] publicMethods = personClass.getMethods();
System.out.println("遍历所有的方法:");
for(Method m : allMethods){
System.out.println(m);
}
//下面是测试通过反射调用函数
//通过反射创建实例对象,默认调无参构造函数
Person person2 = personClass.newInstance();
//获取要调用的方法,要调用study方法,包含int和String参数,注意int和Integer在这有区别
Method method = personClass.getMethod("study", int.class, String.class);
Object o = method.invoke(person2, 18, "刘俊重");
}
}
Результаты теста:
遍历所有的方法:
public java.lang.String com.catchu.me.reflect.Person.toString()
public java.lang.String com.catchu.me.reflect.Person.getName()
public void com.catchu.me.reflect.Person.setName(java.lang.String)
public void com.catchu.me.reflect.Person.study(int,java.lang.String)
public int com.catchu.me.reflect.Person.getAge()
public void com.catchu.me.reflect.Person.setAge(int)
我叫刘俊重,我今年18,我在学习反射
Примечание: Object o = method.invoke(person2, 18, "Liu Junzhong"); предназначен для вызова метода метода объекта person2. Формат: имя метода.invoke(объект, параметр), аналогичный методу get, когда получение значения переменной-члена. Сила отражения видна из вышеизложенного: с помощью отражения мы можем получить тип класса, с помощью типа класса мы можем получить конструктор, а затем создать экземпляр нового объекта; с помощью отражения мы можем получить переменные-члены и методы-члены, с помощью экземпляр Исходящий объект может получить значения этих переменных-членов или вызвать методы-члены. Это только часть отражения. С помощью отражения мы также можем судить о том, содержат ли классы, переменные и методы определенные определенные аннотации. Мы также можем использовать отражение для динамического прокси для вызова других методов. Есть бесконечные хуки с аннотациями и динамические приостановки прокси. Пространство воображения, такое как пружинные рамки, нижний слой через эти принципы. Ниже приведены некоторые часто используемые API для отражения, и, наконец, будет представлена комбинация отражения с аннотациями и динамическими прокси.
2.4 Другие методы
- Обычно используемые методы в аннотациях:
Annotation[] annotations = (Annotation[]) class1.getAnnotations();//获取class对象的所有注解
Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);//获取class对象指定注解
Type genericSuperclass = class1.getGenericSuperclass();//获取class对象的直接超类的
Type Type[] interfaceTypes = class1.getGenericInterfaces();//获取class对象的所有接口的type集合
- Методы получения другой информации об объекте класса:
boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型
boolean isArray = class1.isArray();//判断是否是集合类
boolean isAnnotation = class1.isAnnotation();//判断是否是注解类
boolean isInterface = class1.isInterface();//判断是否是接口类
boolean isEnum = class1.isEnum();//判断是否是枚举类
boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判断是否被某个注解类修饰
String className = class1.getName();//获取class名字 包含包名路径
Package aPackage = class1.getPackage();//获取class的包信息
String simpleName = class1.getSimpleName();//获取class类名
int modifiers = class1.getModifiers();//获取class访问权限
Class<?>[] declaredClasses = class1.getDeclaredClasses();//内部类
Class<?> declaringClass = class1.getDeclaringClass();//外部类
ClassLoader ClassLoader = class1.getClassLoader() 返回类加载器
getSuperclass():获取某类所有的父类
getInterfaces():获取某类所有实现的接口
3. Динамический прокси
Работа прокси реализована в классе java.lang.reflect.Proxy.Прокси-объект может быть создан с помощью метода newProxyInstance() класса Proxy следующим образом:
public static Object newProxyInstance(ClassLoader loader,类<?>[] interfaces,InvocationHandler h)
Не пугайтесь, когда увидите в нем большую кучу непонятного дерьмового кода, в нем есть свои приемы, по сути, это все шаблоны, мы можем передать все, что нам нужно. Как видите, требуются три параметра: загрузчик классов, интерфейс и обработчик вызовов. Мы смогли получить указанный выше класс Class, использовать class.getClassLoader для получения загрузчика классов и использовать class.getgetInterfaces() для получения всех интерфейсов, так что теперь нам нужно написать новый объект InvocationHandler? По сути, здесь написан основной код нашего динамического прокси. Шаблон, о котором я упоминал выше, на самом деле состоит из следующих шагов:
- Напишите прокси-классы и прокси-методы и реализуйте proxy Proxy.newProxyInstance() в прокси-методе;
- В прокси требуются следующие параметры: загрузчик класса class.getClassLoader() проксируемого класса, все интерфейсы реализации проксируемого класса new Class[] { Interface.class }, метод обработки new InvocationHandler();
- Перепишите метод вызова в метод дескриптора.Входные данные метода вызова имеют три параметра: Object proxy (объект класса proxy), Method method (метод класса proxy), Object[] args (входящие параметры класса proxy). метод класса), в этом методе мы можем настроить наш бизнес;
- Получите прокси-класс и заставьте его быть прокси-интерфейсом;
- Наконец, мы можем вызвать любой метод интерфейса, как если бы мы не были проксированы.После вызова метода имя метода и список параметров будут переданы методу вызова класса прокси для выполнения логического потока нового бизнеса. . См. пример кода ниже: Интерфейс PersonInterface:
public interface PersonInterface {
void doSomething();
void saySomething();
}
Класс реализации интерфейса:
public class PersonImpl implements PersonInterface {
@Override
public void doSomething() {
System.out.println("人类在做事");
}
@Override
public void saySomething() {
System.out.println("人类在说话");
}
}
Класс прокси:
/**
* @author 刘俊重
*/
public class PersonProxy {
public static void main(String[] args) {
final PersonImpl person = new PersonImpl();
PersonInterface proxyPerson = (PersonInterface) Proxy.newProxyInstance(PersonImpl.class.getClassLoader(),
PersonImpl.class.getInterfaces(), new InvocationHandler() {
//在下面的invoke方法里面写我们的业务
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName()=="doSomething"){
person.doSomething();
System.out.println("通过常规方法调用了实现类");
}else{
method.invoke(person,args);
System.out.println("通过反射机制调用了实现类");
}
return null;
}
});
proxyPerson.doSomething();
proxyPerson.saySomething();
}
}
Результат выполнения следующий:
人类在做事
通过常规方法调用了实现类
人类在说话
通过反射机制调用了实现类
Когда мы вызываем через proxyPerson.doSomething(), мы не сразу входим в метод doSomething класса реализации, а вводим имя метода и параметры в наш вызов прокси-метода, где я выношу суждение, равен ли он "doSomething" использует обычные вызовы методов, в противном случае использует рефлексивные вызовы методов. Вроде бы обычный вызов, но каждое выполнение должно идти к нашему прокси-методу, в нем мы можем делать какие-то «руки-ноги» и присоединяться к нашему бизнес-процессингу. Другой пример вы можете увидеть ниже.Например, на Tmall предмет одежды обычно продается за 50. Теперь вы мой VIP-пользователь, и я могу сделать вам скидку 10 юаней.Другие услуги такие же.Только здесь на 10 дешевле , Очень хлопотно, использование прокси может решить эту проблему. Интерфейс ПродажиСервис:
public interface SaleService {
//根据尺码返回衣服的大小
int clothes(String size);
}
Класс реализации интерфейса SaleServiceImpl:
public class SaleServiceImpl implements SaleService {
@Override
public int clothes(String size) {
System.out.println("衣服大小"+size);
//模拟从数据库取衣服价格
return 50;
}
}
Обычный звонок без скидки:
/**
* @author 刘俊重
* @Description 普通用户
*/
public class OrdinaryCustom {
public static void main(String[] args) {
SaleService saleService = new SaleServiceImpl();
int money = saleService.clothes("XXl");
System.out.println("价格是:"+money);
}
}
Выходной результат:
衣服大小XXl
价格是:50
Прокси класса ProxySale:
/**
* @author 刘俊重
* @Description 代理类
*/
public class ProxySale {
//对接口方法进行代理
public static <T> T getProxy(final int discount, final Class<SaleServiceImpl> implementClasses, Class<SaleService> interfaceClasses){
return (T)Proxy.newProxyInstance(implementClasses.getClassLoader(),
implementClasses.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用原始对象的方法,获取未打折之前的价格
int price = (int) method.invoke(implementClasses.newInstance(), args);
return price-discount;
}
});
}
}
vip пользовательский тестовый класс VipCustom:
/**
* @author 刘俊重
* @Description Vip用户,有打折优惠
*/
public class VipCustom {
public static void main(String[] args) {
//vip用户,打10元折扣
int discount = 10;
SaleService saleService = ProxySale.getProxy(discount, SaleServiceImpl.class, SaleService.class);
int money = saleService.clothes("xxl");
System.out.println("价格是:"+money);
}
}
Результат:
衣服大小xxl
价格是:40
Это видно в случае с немодифицированным поставщиком услуг, мы подстроили класс прокси, что соответствует ожиданиям.
4. Аннотация
4.1 Концепция и роль
- концепция
- Аннотация — это метаданные, то есть метаданные исходного кода.
- Аннотации обеспечивают формальный способ добавления информации в код, упрощая использование этих данных в дальнейшем.
- Аннотация — это специальный модификатор, применяемый к объявлениям классов, методов, параметров, переменных, конструкторов и пакетов. Это инструмент, выбранный стандартом JSR-175 для описания метаданных.
- эффект
- Генерация документа
- Отслеживайте зависимости кода, реализуйте альтернативные функции файла конфигурации и сокращайте конфигурацию. Например, некоторые аннотации в Spring
- Проверка формата во время компиляции, например @Override и т. д.
- Всякий раз, когда вы создаете класс дескриптора или интерфейс, если он содержит повторяющуюся работу, рассмотрите возможность использования аннотаций для упрощения и автоматизации процесса.
4.2 Java-аннотация
- Что такое аннотации Java? В синтаксисе java используйте символ @ в качестве начала и сразу после имени аннотации следует @. Применительно к классам, интерфейсам, методам и полям пакет аннотаций в java — это java.lang.annotation, например:
@Override
void myMethod() {
......
}
Здесь @Override — это аннотация. Функция этой аннотации — сообщить компилятору, что метод myMethod() переопределяет метод myMethod() в родительском классе.
- Аннотации, встроенные в java В java есть три встроенных аннотации:
@Override:表示当前的方法定义将覆盖超类中的方法,如果出现错误,编译器就会报错。
@Deprecated:如果使用此注解,编译器会出现警告信息。
@SuppressWarnings:忽略编译器的警告信息。
- метааннотация Роль метааннотаций заключается в аннотировании других аннотаций (используемых при использовании пользовательских аннотаций). Java 5.0 определяет четыре стандартных типа метааннотаций, которые используются для предоставления описаний для других типов аннотаций. Четыре метааннотации, определенные Java 5.0:
@Target
@Retention
@Documented
@Inherited
В Java8 добавлены две новые аннотации, о которых я расскажу позже.
- @Target
@Target указывает область объектов, измененных аннотацией: аннотацию можно использовать для пакетов, типов (классы, интерфейсы, перечисления, типы аннотаций), членов типа (методы, конструкторы, переменные-члены, значения перечисления), параметры метода и локальные переменные ( такие как переменные цикла, параметры catch). Использование target в объявлении типа Annotation может быть более явным в отношении цели его модификации.
Роль: используется для описания области применения аннотации (т. е. где можно использовать описанную аннотацию).Значения (ElementType):
тип | использовать |
---|---|
CONSTRUCTOR | Используется для описания конструкторов |
FIELD | используется для описания домена |
LOCAL_VARIABLE | Используется для описания локальных переменных |
METHOD | используется для описания метода |
PACKAGE | используется для описания пакета |
PARAMETER | используется для описания параметров |
TYPE | Используется для описания классов, интерфейсов (включая типы аннотаций) или объявлений перечислений. |
Например, если определена следующая аннотация, ее можно использовать только в методах, поскольку она ограничена аннотацией на уровне метода. Если она используется в классе или другом, на этапе компиляции будет сообщено об ошибке:
@Target({ElementType.METHOD})
public @interface MyMethodAnnotation {
}
Тестовый класс MyClass:
//@MyMethodAnnotation 报错,方法级别注解不能注在类头上
public class MyClass {
@MyMethodAnnotation
public void myTestMethod(){
//
}
}
- @Retention
@Retention определяет, как долго сохраняется аннотация: некоторые аннотации появляются только в исходном коде и отбрасываются компилятором; другие компилируются в файле класса; аннотация, скомпилированная в файле класса, может быть виртуализирована Машина игнорирует, а другие будут читать при загрузке класса (обратите внимание, что это не влияет на выполнение класса, т.к. аннотация и класс используются отдельно). Используйте эту метааннотацию, чтобы ограничить «срок жизни» аннотации.
Функция: указывает, на каком уровне должна быть сохранена информация аннотации для описания жизненного цикла аннотации (т. е.: в какой области действительна описанная аннотация)Значения (RetentionPoicy):
тип | использовать | инструкция |
---|---|---|
SOURCE | Действительно в исходных файлах (т.е. исходные файлы остаются) | Появляется только в исходном коде и отбрасывается компилятором. |
CLASS | Действителен в файлах класса (т.е. сохранение класса) | компилируется в файле класса |
RUNTIME | Действителен во время выполнения (т. е. сохраняется во время выполнения) | Скомпилируйте в файл класса |
Пример:
@Target({ElementType.TYPE}) //用在描述类、接口或enum
@Retention(RetentionPolicy.RUNTIME) //运行时有效
public @interface MyClassAnnotation {
String value(); //这个MyClassAnnotation注解有个value属性,将来可以设置/获取值
}
- @Documented
@Documented используется для описания общедоступного API, в котором другие типы аннотаций должны использоваться в качестве аннотированных членов программы, и поэтому могут быть задокументированы такими инструментами, как javadoc. Documented — это аннотация разметки без членов.
Роль: включить аннотации в javadoc
java.lang.annotation.Documented
@Documented
public @interface MyCustomAnnotation { //Annotation body}
- @Inherited
Это аннотация разметки, которая объясняет, что отмеченный тип наследуется, и тип аннотации, измененный с помощью @Inherited, используется для класса, тогда аннотация будет использоваться для подкласса класса, а тип аннотации @Inherited наследуется подклассы отмеченного класса. Класс не наследует аннотацию от реализованного интерфейса, а метод не наследует аннотацию от своего перегруженного метода.Когда Retention аннотации, помеченной типом аннотации @Inherited, имеет значение RetentionPolicy.RUNTIME, API отражения усиливает это наследование. Если мы используем java.lang.reflect для запроса аннотации типа @Inherited, будет работать проверка кода отражения: проверка класса и его суперкласса до тех пор, пока не будет найден указанный тип аннотации или он не достигнет верхнего уровня структуры наследования класса. .
Роль: разрешить подклассам наследовать аннотации родительских классов.Например, аннотация, используемая MyParentClass здесь, снабжена аннотацией @Inherited, поэтому подклассы могут наследовать эту информацию аннотации:
java.lang.annotation.Inherited
@Inherited
public @interface MyCustomAnnotation {
}
@MyCustomAnnotation
public class MyParentClass {
...
}
public class MyChildClass extends MyParentClass {
...
}
4.3 Пользовательские аннотации
Формат
public @interface 注解名{
定义体
}
Поддерживаемые типы данных для параметров аннотации:
- Все основные типы данных (int, float, double, boolean, byte, char, long, short)
- Тип строки
- Тип класса
- тип перечисления
- Тип аннотации
- Все вышеперечисленные типы массивов
правило
- Модификаторы могут быть только общедоступными или стандартными.
- Элементы параметров могут использовать только базовые типы byte, short, int, long, float, double, boolean, восемь основных типов и String, Enum, Class, аннотации и массивы этих типов.
- Если есть только один элемент параметра, лучше установить имя «значение».
- Элемент аннотации должен иметь определенное значение. Вы можете определить значение по умолчанию в аннотации или указать его при использовании аннотации. Значение небазового типа не может быть нулевым, а в качестве часто используется пустая строка или 0. значение по умолчанию.
- При представлении наличия или отсутствия элемента определите специальное значение для его представления, например пустую строку или отрицательное значение.
Пример:
@Target(ElementType.FIELD)
@Retention(value=RetentionPolicy.RUNTIME)
@Documented
public @interface MyFieldAnnotation {
int id() default 0;
String name() default "";
}
Определяет аннотацию MyFieldAnnotation, которая используется в полях и действительна во время выполнения.Она имеет два атрибута: идентификатор типа int (не забудьте поставить круглые скобки после идентификатора), значение по умолчанию — 0, и имя типа String, которое по умолчанию значение равно "".
4.4 Работа с аннотациями — библиотека классов обработки аннотаций
Мы уже знаем, как настроить аннотацию выше, но определение света бесполезно. Важно то, что я хочу его использовать, и используемый метод также очень прост. Я также упомянул несколько таких методов, когда говорил об отражении в Например: class1.isAnnotation(), на самом деле все они являются методами интерфейса AnnotatedElement в пакете java.lang.reflect. Этот интерфейс в основном имеет следующие классы реализации:
- Класс: определение класса
- Конструктор: определение конструктора
- Поле: усталые определения переменных-членов
- Метод: определение метода класса
- Пакет: определение пакета класса. Пакет java.lang.reflect в основном содержит некоторые классы инструментов, которые реализуют функцию отражения Фактически, все API отражения, предоставляемые пакетом java.lang.reflect, расширяют возможности чтения аннотаций во время выполнения. Когда тип аннотации определен как аннотация времени выполнения, аннотация может быть видна во время выполнения, а аннотация, сохраненная в файле класса, будет прочитана виртуальной машиной при загрузке файла класса. Интерфейс AnnotatedElement является родительским интерфейсом всех элементов программы (класса, метода и конструктора), поэтому после того, как программа получит объект AnnotatedElement определенного класса посредством отражения, программа может вызвать следующие четыре метода объекта для доступа к информации об аннотации:
- Метод 1: T getAnnotation(Class annotationClass): возвращает аннотацию указанного типа, которая существует в программном элементе, или возвращает значение null, если аннотация типа не существует.
- Метод 2: Annotation[] getAnnotations(): возвращает все аннотации, существующие в программном элементе.
- Способ 3: логическое значение AnnotationPresent(Class annotationClass): Определить, содержит ли программный элемент аннотацию указанного типа, и возвращает true, если она существует, в противном случае возвращает false.
- Метод 4: Annotation[] getDeclaredAnnotations(): возвращает все аннотации, существующие непосредственно в этом элементе. В отличие от других методов в этом интерфейсе, этот метод игнорирует унаследованные аннотации. (Если непосредственно для этого элемента не существует аннотации, возвращается массив нулевой длины.) Вызывающие этот метод могут свободно изменять возвращаемый массив по своему желанию; это не влияет на массивы, возвращаемые другими вызывающими объектами.Резюме: аннотация определения — java.lang.annotation.Annotation, а аннотация операции — java.lang.reflect.AnnotatedElement.
Код теста CustomClassAnnotation выглядит следующим образом:
/**
* @author 刘俊重
* @Description 自定义类注解
*/
@Target(ElementType.TYPE) //作用在类,枚举或接口上
@Retention(RetentionPolicy.RUNTIME) //运行时有效
@Documented //文档可见
public @interface CustomClassAnnotation {
String value(); //获取注解名称
}
Класс FruitName выглядит следующим образом:
/**
* @author 刘俊重
* @Description 字段注解(字符串类型的)
*/
@Target(ElementType.FIELD) //用在字段上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String name() default "";
}
Класс FruitColor выглядит следующим образом:
/**
* @author 刘俊重
* @Description 字段注解(枚举类型的)
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
//颜色枚举
enum Color{BLUE,RED,GREEN};
//颜色属性
Color color() default Color.RED;
}
Класс сущности Fruit выглядит следующим образом:
/**
* @author 刘俊重
* @Description Fruit实体类
*/
@CustomClassAnnotation(value="fruit")
public class Fruit{
@FruitName(name="apple")
private String name;
@FruitColor(color= FruitColor.Color.RED)
private String color;
}
Тестовый класс TestAnnotation выглядит следующим образом:
/**
* @author 刘俊重
* @Description 测试类
*/
public class TestAnnotation {
public static void main(String[] args) {
Class<Fruit> clazz = Fruit.class; //反射获取Class对象
CustomClassAnnotation annotation = clazz.getAnnotation(CustomClassAnnotation.class); //拿到Fruit类的注解
if(null!=annotation && "fruit".equals(annotation.value())){
System.out.println("Fruit类的注解名是======"+annotation.value());
//获取所有的属性遍历,拿到每一个属性的值
Field[] allFields = clazz.getDeclaredFields();
for(Field field : allFields){
if(field.isAnnotationPresent(FruitName.class)){
//判断是否存在FruitName注解
FruitName fruitName = field.getAnnotation(FruitName.class);
System.out.println("水果名称====="+fruitName.name());
}
if(field.isAnnotationPresent(FruitColor.class)){
FruitColor fruitColor = field.getAnnotation(FruitColor.class);
System.out.println("水果颜色====="+fruitColor.color());
}
}
}else{
System.out.println("注解值不对,请检查");
}
}
}
Резюме: Имена классов, имена интерфейсов, имена методов и имена атрибутов могут быть получены с помощью аннотаций. С помощью отражения объекты могут быть динамически сгенерированы, а затем с помощью динамических агентов для динамического вызова определенных методов. Это в основном нижний уровень структуры Spring. , Часть принципа реализации. Примечание: часть содержания этой статьи цитируется с http://t.cn/RK8ci8w. Оригинальные авторские размышления и аннотации действительно хороши. Я улучшил часть содержания и переписал некоторые примеры; я добавил динамический прокси часть, учитывая, что объедините отражение, прокси и аннотацию.
Прикрепите личную общедоступную учетную запись WeChat, добро пожаловать на общение со мной.