定义:
注解本质是一个继承了 Annotation
的特殊接口,其具体实现类是Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象$Proxy1
。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler
的invoke
方法。该方法会从memberValues
这个Map 中索引出对应的值。而memberValues
的来源是Java 常量池。
对于注解本质是一个继承了 Annotation
的特殊接口这句,可以这么理解:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
pubic @interface CreateBy{
}
// 相当于
public interface CreateBy extends Annotation{
}
实现:创建成员变量注解并在bean中使用:
package com.example.mybatisinterceptor.MyInterface;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自动设置创建时间
*/
@Retention(RetentionPolicy.RUNTIME)
// FIELD 表示将注解放在 成员变量上
@Target({ElementType.FIELD})
public @interface CreateTime {
String value() default "";
}
package com.example.mybatisinterceptor.bean;
import com.example.mybatisinterceptor.MyInterface.CreateBy;
import com.example.mybatisinterceptor.MyInterface.CreateTime;
import com.example.mybatisinterceptor.MyInterface.UpdateBy;
import com.example.mybatisinterceptor.MyInterface.UpdateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 数据库模型设计时抽出所有通用字段,抽象为父类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class BaseEntity implements Serializable {
@CreateTime(value = "我是@CreateTime注解中的value")
//@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
}
当我们通过反射,使用getAnnotation 方法获取一个注解类实例的时候,其实 JDK 是通过动态代理机制生成一个实现我们注解(接口)的代理类。如下:
package com.example.mybatisinterceptor;
import com.example.mybatisinterceptor.MyInterface.CreateTime;
import java.lang.reflect.Field;
public class test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
// 通过反射获取成员变量使用到注解的类
Class aClass = Class.forName("com.example.mybatisinterceptor.bean.BaseEntity");
/**
* 由于我这里注解是添加在成员变量的,所以用 getDeclaredField("成员变量名")获取成员变量
* 如果使用的是在方法上添加的注解,则使用:
* Method meite = aClass.getDeclaredMethod("方法名", 参数类型.class) 获取方法
*/
Field createTime = aClass.getDeclaredField("createTime");
// 获取到成员变量后,通过 getAnnotation(注解名.class); 获取注解,这里就可以得到注解了!
CreateTime createBy = createTime.getAnnotation(CreateTime.class);
System.out.println(createBy);
}
}
结果:
@com.example.mybatisinterceptor.MyInterface.CreateTime(value=我是@CreateTime注解中的value)
但是这中间,是怎么获取到注解的value值的呐? 这就是用到了 AnnotationInvocationHandler ,它是 java中专门用于处理注解的 Handler。
我们在获取成员变量的时候打断点。
找到AnnotationInvocationHandler 并且在 invoke 方法中添加断点
放行,跟踪查看情况
这里看到var6的值为 RUNTIME 表示获取的是运行时动态代理的对象,接着断点一直往下走,直到 AnnotationParser 这个类, 通过 parseMemberValue 方法获取到注解的value值。
继续往下走:
这里看到最后返回一个var4,里面是完整的注解代理对象。
总结:
一个注解的实例创建,它本质上就是一个代理类,是在调用 getDeclaredField() 方法 (有多个方法,这里我获取成员变量方法,即注解定义在成员变量上,也可以是获取注解方法,即注解定义在方法上) 的时候,返回一个jdk动态代理$Proxy对象,使用Proxy的newProxyInstance方法时候,传入接口 和 InvocationHandler 的一个实例(也就是 AnotationInvocationHandler ) ,调用该代理实例的获取值的方法时,就会执行AnnotationInvocationHandler 中 invoke 方法,实现逻辑是 通过方法名返回注解属性值。最后返回一个代理实例。