0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Java注解及其底层原理解析2

jf_78858299 来源:小牛呼噜噜 作者:小牛呼噜噜 2023-02-09 14:18 次阅读

最后我们写一个测试类

public class TestAn {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {

        //获取Person class 实例
        Class c1 = Person.class;

        //反射获取 类上的注解
        MyAnnotation classAnnotation = c1.getAnnotation(MyAnnotation.class);
        System.out.println(classAnnotation.msg());

        //反射获取 private属性上的注解
        Field we = c1.getDeclaredField("weight");
        MyAnnotation fieldAnnotation = we.getAnnotation(MyAnnotation.class);
        System.out.println(fieldAnnotation.msg());

        //反射获取 public属性上的注解
        Field he = c1.getDeclaredField("height");
        MyAnnotation field2Annotation = he.getAnnotation(MyAnnotation.class);
        System.out.println(field2Annotation.msg());

        //反射获取 方法上的注解
        Method me = c1.getMethod("dance",null);
        MyAnnotation methodAnnotation = me.getAnnotation(MyAnnotation.class);
        System.out.println(methodAnnotation.msg());
        

    }
}

结果:

this person class this person field private this person field public this person method

我们通过反射读取api时,一般会先去校验这个注解存不存在:

if(c1.isAnnotationPresent(MyAnnotation.class)) {
    //存在 MyAnnotation 注解
}else {
    //不存在 MyAnnotation 注解
}

我们发现反射真的很强大,不仅可以读取类的属性、方法、构造器等信息,还可以读取类的注解相关信息。

那反射是如何实现工作的?

我们来看下源码:

c1.getAnnotation(MyAnnotation.class);通过idea点进去查看源码,把重点的给贴出来,其他的就省略了

Map<Classextends Annotation>, Annotation> declaredAnnotations =
            AnnotationParser.parseAnnotations(getRawAnnotations(), getConstantPool(), this);

parseAnnotations()去 分析注解 ,其第一个参数是 获取原始注解,第二个参数是获取常量池内容

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

Proxy._newProxyInstance_(var0.getClassLoader(), new Class[]{var0}, new AnnotationInvocationHandler(var0, var1)创建动态代理,此处var0参数是由常量池获取的数据转换而来。

我们监听此处的var0:

图片

image-20220616225518195

可以推断出注解相关的信息 是存放在常量池中的

我们来总结一下,反射调用getAnnotations(MyAnnotation.class)方法的背后主要操作:

解析注解parseAnnotations()的时候 从该注解类的常量池中取出注解相关的信息,将其转换格式后,通过newProxyInstance(注解的类加载器,注解的class实例 ,AnotationInvocationHandler实例)来创建代理对象,作为参数传进去,最后返回一个代理实例。

其中AnotationInvocationHandler类是一个典型的动态代理类, 这边先挖个坑,暂不展开,不然这篇文章是写不完了

关于动态代理类我们只需先知道: 对象的执行方法,交给代理来负责

class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    ...
    private final Map<String, Object> memberValues;//存放该注解所有属性的值
    private transient volatile Method[] memberMethods = null;

    AnnotationInvocationHandler(Classextends Annotation> var1, Map<String, Object> var2) {
    ...
    }

    public Object invoke(Object var1, Method var2, Object[] var3) {
    ...
     //调用委托类对象的方法,具体等等一些操作
    }
    ...
}

反射调用getAnnotations(MyAnnotation.class),返回一个代理实例,我们可以通过这个实例来操作该注解

示例:注解 模拟访问权限控制

当我们引入springsecurity来做安全框架,然后只需添加@PreAuthorize**(**"hasRole('Admin')"**)**注解,就能实现权限的控制,简简单单地一行代码,就优雅地实现了权限控制,觉不觉得很神奇?让我们一起模拟一个出来吧

@Retention(RetentionPolicy.RUNTIME)
public @interface MyPreVer {
    String value() default "no role";
}
public class ResourceLogin {
    private String name;

    @MyPreVer(value = "User")
    private void rsA() {
        System.out.println("资源A");
    }
    @MyPreVer(value = "Admin")
    private void rsB() {
        System.out.println("资源B");
    }
}
public class TestLogin {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        //模拟 用户的权限
        String role = "User";
        //模拟 需要的权限
        final String RoleNeeded = "Admin";

        //获取Class实例
        Class c1 = ResourceLogin.class;

        //访问资源A
        Method meA = c1.getDeclaredMethod("rsA",null);
        MyPreVer meAPre = meA.getDeclaredAnnotation(MyPreVer.class);
        if(meAPre.value().equals(RoleNeeded)) {//模拟拦截器
            meA.setAccessible(true);
            meA.invoke(c1.newInstance(),null);//模拟访问资源
        }else {
            System.out.println("骚瑞,你无权访问该资源");
        }

        //访问资源B
        Method meB = c1.getDeclaredMethod("rsB",null);
        MyPreVer meBPre = meB.getDeclaredAnnotation(MyPreVer.class);
        if(meBPre.value().equals(RoleNeeded)) {//模拟拦截器
            meB.setAccessible(true);
            meB.invoke(c1.newInstance());//模拟访问资源

        }else {
            System.out.println("骚瑞,你无权访问该资源");
        }

    }
}

结果:

骚瑞,你无权访问该资源 资源B

尾语

注解 是一种 标记、标签 来修饰代码, 但它不是代码本身的一部分,即 注解本身对代码逻辑没有任何影响 ,如何使用注解完全取决于我们开发者Java反射来读取和使用。

我们发现反射真的很强大,不仅可以读取类的属性、方法、构造器等信息,还可以读取类的注解相关信息,以后还会经常遇到它。

注解一般用于

  • 编译器可以利用注解来探测错误和检查信息,像@override检查是否重写
  • 适合工具类型的软件用的,避免繁琐的代码,生成代码配置,比如jpa自动生成sql,日志注解,权限控制
  • 程序运行时的处理:某些注解可以在程序运行的时候接受代码的读取,比如我们可以自定义注解

平时我们只知道如何使用注解,却不知道其是如何起作用的,理所当然的往往是我们所忽视的。

参考资料

《Java核心技术 卷一》

https://blog.csdn.net/qq_20009015/article/details/106038023

https://zhuanlan.zhihu.com/p/258429599

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2967

    浏览量

    104744
  • spring
    +关注

    关注

    0

    文章

    340

    浏览量

    14341
  • 注解
    +关注

    关注

    0

    文章

    18

    浏览量

    2674
收藏 人收藏

    评论

    相关推荐

    如何通过注解来优化我们的Java代码

    Java注解可以说是我们编码过程中最常用的。本篇文章将给大家介绍Java注解的概念、作用以及如何使用注解来提升代码的可读性和灵活性,并介绍如
    的头像 发表于 09-30 11:39 639次阅读

    java经典面试题深度解析

    免费视频教程:java经典面试题深度解析对于很多初学者来说,学好java在后期面试的阶段都没什么经验,为了让大家更好的了解面试相关知识,今天在这里给大家分享了一个java经典面试题深度
    发表于 06-20 15:16

    详细介绍了Java泛型、注解、并发编程

    介绍了Java泛型、注解、并发编程、数据传输与序列化、高效IO、容器集合、反射与类加载以及JVM重点知识线程、内存模型、JVM运行时内存、垃圾回收与算法、Java中四种引用类型、GC 分代收集算法
    发表于 08-20 06:09

    HarmonyOS注解的使用方法分享

    定义我们的注解自定义注解1、声明注解功能:检测类中是否有规范的get方法新建java libray的module,命名为annotation,创建
    发表于 03-28 14:04

    微信ANR原理解析与解决方案

    理解析 CSDN 博客专家@三精-大精wing 对于导致微信 ANR 的根本原因进行了解析(以下为授权发布): 本文目的在于学习研究Android技术,若有侵犯,联系作者将及时删除。 首先,微信
    发表于 09-26 17:09 0次下载
    微信ANR原<b class='flag-5'>理解析</b>与解决方案

    分析java注解基本概念

    什么是注解(Annotation): Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。Annotion(注解
    发表于 09-27 14:53 0次下载

    Java底层实现,CPU还有10个术语!

    Java底层实现——CPU的10个术语
    的头像 发表于 03-28 14:14 6257次阅读

    注解定义Bean及开发

    注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类。
    发表于 08-02 10:26 442次阅读

    Java注解及其底层理解析 1

    什么是注解? 当我们开发SpringBoot项目,我们只需对启动类加上`@SpringBootApplication`,就能自动装配,不需要编写冗余的xml配置。当我们为项目添加lombok
    的头像 发表于 02-09 14:18 763次阅读
    <b class='flag-5'>Java</b><b class='flag-5'>注解</b><b class='flag-5'>及其</b><b class='flag-5'>底层</b>原<b class='flag-5'>理解析</b> 1

    容器配置及Spring Boot注解

    Autowired注解用于标记Spring将要解析和注入的依赖项。此注解可以作用在构造函数、字段和setter方法上。
    的头像 发表于 04-07 11:45 577次阅读
    容器配置及Spring Boot<b class='flag-5'>注解</b>

    SpringBoot的核心注解2

    今天跟大家来探讨下SpringBoot的核心注解@SpringBootApplication以及run方法,理解下springBoot为什么不需要XML,达到零配置
    的头像 发表于 04-07 14:34 1963次阅读
    SpringBoot的核心<b class='flag-5'>注解</b><b class='flag-5'>2</b>

    JAVA注解是怎么做到的(上)

    。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。那么你知道JDK什么是元注解吗?注解有哪些分类吗?以及注解Java中最本质究竟是什么东西,
    的头像 发表于 05-11 10:57 645次阅读

    JAVA注解是怎么做到的(下)

    。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。那么你知道JDK什么是元注解吗?注解有哪些分类吗?以及注解Java中最本质究竟是什么东西,
    的头像 发表于 05-11 10:57 571次阅读
    <b class='flag-5'>JAVA</b>中<b class='flag-5'>注解</b>是怎么做到的(下)

    3分钟纯Java注解搭个管理系统

    Erupt一个通用后台管理框架,据说有 超低代码量 、 零前端代码 、零 CURD操作 、无需建表 ,纯Java注解开发 等特色,号称三分钟就可以搭建一个完整的后台管理系统。
    的头像 发表于 07-28 11:27 1110次阅读
    3分钟纯<b class='flag-5'>Java</b><b class='flag-5'>注解</b>搭个管理系统

    springmvc常用5种注解

    SpringMVC是一种基于Java的Web框架,使用注解可以更加方便灵活地开发和管理控制器,实现请求的映射和处理。在SpringMVC中,有许多常用的注解,本文将详细介绍其中的五种注解
    的头像 发表于 11-22 16:51 898次阅读