java注解的理解及源码分析

一、概述

注解在Java开发中常见,但是很多人不知其中的实现原理,接下来谈谈我对注解的理解和认识

二、首先,注解的出现是从Java1.5开始的,注解的分类本人分为了大致的三类:

a、Java自带的标准注解:@Override、@deprecated、@supporswarning
@Override:表示方法的重写
@deprecated:表示方法过时、过时的方法会有横杠,但是不影响使用
@supporswarning:表示忽略警告信息

b、元注解:@Retention 、@Target 、 @Documented 、 @Inherited、@Native、@Repeate
@Retention :表示注解的生效范围
@Target :表示注解作用的对象
@Documented :表示可以生成文档
@Inherited:表示注解可以遗传
@Native:表示该注解是使用的本地的方法
@Repeate:表示该注解在同一位置可以重用

c、自定义注解:可以通过元注解声明,其中@Retention和@Target注解是必不可少的

三、下面我们讲一下自定义注解如何定义

实现自定义注解必不可少的是@Retention和@Target,同时,需要使用@interface来表示这是一个注解类,@Interface可以理解为是注解类的标志,没有其他含义。使用@interface,可以让JVM知道这是一个注解类。如下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMethodAnno {
        public String title() default "cheng";
        public String profile() default "world";
}

该注解表示的含义是作用在类,接口或枚举上的,作用范围是运行期间,同时有两个属性。

再来看看它的应用,如下

@MyMethodAnno(title = "xixi",profile = "name")
public class Test1 {
    public static void main(String[] args) {
        //Test1.handleAnno();
        Class cl  = Test1.class;
        //Annotation[] annotations = cl.getDeclaredAnnotations();
        Annotation ann = cl.getDeclaredAnnotation(MyMethodAnno.class);
        System.out.println(ann);
    }
}

我们会很好奇,它是怎么运作的,又是怎么解析的呢?带着疑问,我们继续往下看。通过idea的字节码工具:view->show ByteCode,我们看一下MyMethodAnno注解的字节码,如下图
在这里插入图片描述
MyMethodAnno注解的字节码中其实是实现了Annotation接口,所以,MyMethodAnno也是一个接口,属性变成了抽象方法。到这里,我们可以推断出,从JVM的角度来看,注解就是接口!
Annotation接口是所有注解的一个顶层接口,所有注解都必须要实现这个接口。

接下来我们通过断点的方式来看看它底层是怎么运行的,如下图
在这里插入图片描述
我们可以看到,通过反射,返回的注解对象是一个代理对象Proxy。同时看到处理类有AnnotationInvocationHandle这个类。顺藤摸瓜,我们来看下AnnotationInvocationHandle这个类,根据字面意思,也能清楚的知道,这个类是注解调用处理类。如下
在这里插入图片描述
这个类中的invoke方法中打断点,因为这个方法一看就是核心的执行方法。继续执行断点,会跳转到AnnotaitonParse类中的parseAnnotation方法,然后执行annotationForMap方法
parseAnnotation方法

static Annotation parseAnnotation(ByteBuffer var0, ConstantPool var1, Class<?> var2, boolean var3) {
        return parseAnnotation2(var0, var1, var2, var3, (Class[])null);
    }

annotationForMap方法

public static Annotation annotationForMap(final Class<? extends Annotation> var0, final Map<String, Object> var1) {
        return (Annotation)AccessController.doPrivileged(new PrivilegedAction<Annotation>() {
            public Annotation run() {
                return (Annotation)Proxy.newProxyInstance(var0.getClassLoader(), new Class[]{var0}, new AnnotationInvocationHandler(var0, var1));
            }
        });
    }

在annotationForMap方法中,调用Proxy的newProxyInstance来创建注解的代理对象,这就能很好的解释上述中出现的代理对象。

AnnotationInvocationHandle类的invoke执行完毕后,会进入到Class类中执行createAnnotationData方法,这个方法是用来创建注解的数据并保存

private AnnotationData createAnnotationData(int classRedefinedCount) {
        Map<Class<? extends Annotation>, Annotation> declaredAnnotations =
            AnnotationParser.parseAnnotations(getRawAnnotations(), getConstantPool(), this);
        Class<?> superClass = getSuperclass();
        Map<Class<? extends Annotation>, Annotation> annotations = null;
        if (superClass != null) {
            Map<Class<? extends Annotation>, Annotation> superAnnotations =
                superClass.annotationData().annotations;
            for (Map.Entry<Class<? extends Annotation>, Annotation> e : superAnnotations.entrySet()) {
                Class<? extends Annotation> annotationClass = e.getKey();
                if (AnnotationType.getInstance(annotationClass).isInherited()) {
                    if (annotations == null) { // lazy construction
                        annotations = new LinkedHashMap<>((Math.max(
                                declaredAnnotations.size(),
                                Math.min(12, declaredAnnotations.size() + superAnnotations.size())
                            ) * 4 + 2) / 3
                        );
                    }
                    annotations.put(annotationClass, e.getValue());
                }
            }
        }
        if (annotations == null) {
            // no inherited annotations -> share the Map with declaredAnnotations
            annotations = declaredAnnotations;
        } else {
            // at least one inherited annotation -> declared may override inherited
            annotations.putAll(declaredAnnotations);
        }
        return new AnnotationData(annotations, declaredAnnotations, classRedefinedCount);
    }

在这个方法中,返回的值是AnnotationData对象,这个对象中又有两个重要的属性:declaredAnnotations和annotations
之前学习过反射,应该都知道,带declared前缀的,是本类中声明的,不带的,表示父类和本类的。在这个方法中,就能够体现。
当执行完这个方法后,整个注解的执行解析就结束了。
同时,注意这个方法,调用parseAnnotations方法时传入了getConstantPool,意味着注解的数据是从JVM中的常量池中取得。

我们再来回顾一下整个执行的过程:AnnotationInvocationHandler 类中的invoke方法开始执行注解的逻辑处理-》调用AnnotationParser类的parseAnnotations方法开始解析-》执行AnnotationParser类中的annotationForMap方法来生成注解的代理对象-》invoke方法执行完毕-》Class类中的createAnnotationData方法封装注解的数据,返回AnnotationData对象-》结束

再回到最初的使用反射的地方,如下

@MyMethodAnno(title = "xixi",profile = "name")
public class Test1 {
    public static void main(String[] args) {
        //Test1.handleAnno();
        Class cl  = Test1.class;
        //Annotation[] annotations = cl.getDeclaredAnnotations();
        Annotation ann = cl.getDeclaredAnnotation(MyMethodAnno.class);
        System.out.println(ann);
    }
}

通过字节码对象调用getDeclaredAnnotation方法,我们进去看看,如下
在这里插入图片描述
其实就是从AnnotationData对象中拿到declaredAnnotations属性,这个属性上述也说过,是一个map类型,我们传入注解对象后,就能得到注解对象的所有数据value。

上述就是注解的整个解析,运行和调用的一个闭环,也是本人对注解的整体认识,如有差错,望不吝赐教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值