Java 注解工作原理

大纲

注解的本质

这里先得出结论,注解的工作就是通过动态代理实现的。

定义和使用注解

下面来定义一个最简单的注解,并通过这个注解引出注解的本质。下面编写的类并没有实际意义,只是为了演示而已。

定义注解类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAnnotation {
    String value();
}

应用注解

public class Animal {
    @MyAnnotation("susu")
    public String name;
}

获取被注解的信息

public class AnnotationTest {


    public static void main(String[] args) {

        
        try {
            Field field = null;
            field = Animal.class.getField("name");
            //得到属性上的注解 MyAnnotation
            MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
}

好了,上面就是使用注解的最基本的 3 个步骤了。

验证注解的工作就是通过动态代理实现的。

断点验证我们的结论

下面我们断点执行我们的 AnnotationTest 的入口函数

注解的本质

看到了吗?红色圈中的就是$Proxy1类就是系统帮我们生成的代理类的名字了,看到这里优点迷糊的读者可以看看我之前写过的一篇博客,里面描述了 Java 动态代理的底层实现劣实基础–Java 动态代理机制,看到这里基本就可以知道,我们通过反射得到 annotation 这个引用指向的对象就是一个动态代理对象了。

将动态代理类写入到磁盘

在上面的断点验证中我们并不知道 $Proxy1 类是干嘛的,只是知道它是一个代理类,现在我们进一步的来验证这个问题。

  • generateProxy 方法

这个方法是在我另一篇博客劣实基础–Java 动态代理机制挪过来的,读者可以去阅读这篇博客。这个方法主要的作用是给定一个类名字 proxyClazzName,和需要写入到磁盘中的类的字节码 clazz 对象。

注意:ProxyGenerator是在 Java 工程中的,如果你创建的是 Android 工程可能不能引入这个类哦

/**
 * 在磁盘种生成代理类
 *
 * @param proxyClazzName
 * @param clazz
 */
public static void generateProxy(String proxyClazzName, Class clazz) {
    byte[] data = ProxyGenerator.generateProxyClass(proxyClazzName, new Class[]{clazz});
    String path = clazz.getResource(".").getPath();
    ///Users/liaowj/Documents/code/study4Java/proxy/build/classes/java/main/com/example/proxy/_static/
    System.out.println(path);
    try (FileOutputStream out = new FileOutputStream(new File(path, proxyClazzName + ".class"));) {
        out.write(data);
        out.flush();
    } catch (Exception e) {
        e.printStackTrace();
    }
  • 调用 generateProxy 方法
public static void main(String[] args) {
    generateProxy("MyAnnotationProxy", MyAnnotation.class);
}

下面就是在以下截图路径吓生成了 MyAnnotationProxy.class

动态代理类

打开 MyAnnotationProxy.class,代码如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.example.annotation.MyAnnotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class MyAnnotationProxy extends Proxy implements MyAnnotation {
    private static Method m1;
    private static Method m2;
    private static Method m4;
    private static Method m0;
    private static Method m3;

    public MyAnnotationProxy(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final Class annotationType() throws  {
        try {
            return (Class)super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String value() throws  {
        try {
            return (String)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.example.annotation.MyAnnotation").getMethod("annotationType");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m3 = Class.forName("com.example.annotation.MyAnnotation").getMethod("value");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

总结

通过 @interface 描述的注解类,它就相当于一个接口,通过动态代理获取到的代理对象是会实现这个通过 @interface 描述的注解类,并继承 Proxy 类。

public final class MyAnnotationProxy extends Proxy implements MyAnnotation{...}

在本文中通过断点和将注解类写入到磁盘中两种方式来了解注解的本质。

参考

  • 劣实基础–Java 动态代理机制

  • 项目源码

本文是笔者学习之后的总结,方便日后查看学习,有任何不对的地方请指正。

记录于 2019年4月21号

你可能感兴趣的:(Java 注解工作原理)