自定义注解实现原理

定义:

        注解本质是一个继承了 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 方法,实现逻辑是 通过方法名返回注解属性值。最后返回一个代理实例。

至此结束,大体有个概念,还是没有完全理解。这里可能我描述的有问题,欢迎大家指正。 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值