「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
这篇文章接上一篇: java 注解自定义的方法及注意事项
此篇主要介绍一下如何通过自定义注解来实现自己预设的逻辑。同时通过上篇文章我们知道,在使用自定义注解的时候,一般都是设置RetentionPolicy 为RetentionPolicy.RUNTIME。
我们想通过自定义注解来完成一些自定义逻辑的集成,那么就需要使用到反射的技术。而java 的反射机制就是在Runtime 期间来生效的。
下文我们就以反射机制作为入口,围绕自定义注解的实现,举个例子,来更好地说明自定义注解的使用。
反射机制介绍
静态语言和动态语言的区别
要了解反射机制,我们就要清楚动态语言和静态预言的区别。
动态语言在运行时可以改变结构。比如新的函数、对象,新的代码都可以被引进;同时已有的函数可以被删除,或者发生其他结构上的变化。也即动态语言在运行时,代码可以根据某些条件改变自身的结构。(c#,python,php,js 等都是典型的动态语言)
静态语言就是和动态语言相反,运行时结构不可变。(java,c,c++ 等)
java 不是动态语言,但是java 自带“反射机制”,利用反射机制,可以获取类似于动态语言的特性,可以让变成更加灵活。
反射机制概述
既然世界上有“反射”机制的存在,那么也一定会有“正射”机制的存在。
例如这种写法,就是“正射”。
List<Integer> list = new ArrayList();
而对于反射,就是在程序运行期间,可以通过Reflection API 取得任何类内部的信息,同时可以直接操作任意对象的内部属性及方法。例如:
Class clazz = Class.forName("java.util.List");
Class clazz1 = List.class;
Class clazz2 = list.getClass();
(注意,Class 和class 的区别。class 是声明一个类时的关键字;Class 是一个类的类型。细品)
程序向下执行的时候,加载完成之后,在方法区就会产生一个Class 类型的对象(Class 是类的类型),这个对象包含了类的完整的结构信息,比如各种字段、方法、构造函数等等内容。通过这个类对象,就可以看到类的结构信息。于是我们可以形象地理解到,这个对象就像一面镜子,透过这个镜子就可以看到类的具体的结构和信息,所以我们形象地称这个操作为“反射”。
graph LR
正射: --> 引入需要的包类名称 --> new实例化 --> 得到实例化的对象
反射: --> 实例化对象 --> getClass方法 --> 得到完整的包类名称
反射机制的应用场景
反射应用的场景都是在“运行时”。
比如:
- 在运行时判断任意一个对象所属的类,判断一个类所具有的成员变量和方法
- 在运行时构造任意一个类的对象,调用任意一个对象的成员变量和方法(包括private 类型)
- 运行时获取泛型信息
- 运行时处理注解
- 生成动态代理
我们要理解,Class 类时描述类的类。一个类在内存中只有一个Class 对象。当一个类被加载后,类的整体结构都会被封装在Class 对象中。
Class 本身也是一个类,一个Class 对象对应的是一个加载到JVM 中的一个.class 文件。通过Class 可以完整地得到一个类中所有被加载的结构。
Class 是反射的出发点,针对任何想动态加载、运行的类,唯有先获得对应的Class 对象才可向下继续执行。
即通过反射,可以在运行时,获取运行时类的完整结构,类的完整结构包含的内容包括:
- 所有实现的接口
- 所继承的父类
- 全部的构造器
- 所有的方法
- 所有的Field
- 注解
注解和反射配合
这里随便举个例子,直接看代码:
// 首先自定义一个注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnno {
}
//自定义一个类,在这个类中的方法上使用这个注解
public class Test {
@TestAnno
public void testAnnotation() {
System.out.println(123);
}
}
// main 方法中获取这个方法,同时获取这个方法的注解
public class JApplication {
public static void main(String[] args) throws NoSuchMethodException {
Test test = new Test();
Class clz = test.getClass();
Annotation[] list = clz.getMethod("testAnnotation").getDeclaredAnnotations();
System.out.println(list.length);
}
}
// 输出:
// 1
通过这个例子,我们可以知道,注解就是一个标注,可以通过反射来获取到这个标注的位置,以及是什么标注。
这个例子能比较好地解释并跑通“注解+反射”的组合。
下一篇文章将介绍,如果通过“注解+aop” 的配合来实现定制化的业务逻辑。