自定义注解举例和java反射机制的介绍

304 阅读4分钟

「这是我参与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” 的配合来实现定制化的业务逻辑。