前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试官问我注解的使用有没有踩过坑

面试官问我注解的使用有没有踩过坑

作者头像
猿天地
发布2019-05-23 18:27:43
6070
发布2019-05-23 18:27:43
举报
文章被收录于专栏:猿天地猿天地

问题背景

很久很久前,在我还是青铜的时候(现在依旧是青铜段位)去面试,面试官问我怎么获取类,方法上的注解。

当时的我也算用过注解,顺口就回答了,用 isAnnotationPresent判断是否加了注解, getAnnotation获取注解对象,然后获取注解中的值。

大致的代码是这样子的:

代码语言:javascript
复制
Class<?> clz = bean.getClass();Method[] methods = clz.getMethods();for (Method method : methods) {    if (method.isAnnotationPresent(Encrypt.class)) {         String uri = method.getAnnotation(Encrypt.class).value();    }}

正在我沾沾自喜的时候,面试官又乘胜追击了,那么在读取注解的时候,有没有什么情况会导致刚刚你说的方式是不能成功判断和读取的呢?

这我一下蒙圈了,还会有读取不到的情况么?之前没遇到过啊,于是我斩钉截铁的回答面试官,不可能读取不到的,面试官笑了笑............

在我的加密框架monkey-api-encrypt(https://github.com/yinjihuan/monkey-api-encrypt)中,支持了注解标识加解密的功能,其实是通过读取注解,转换成uri的操作。

一开始也是用的上面的方式进行注解的读取操作,当我们程序中的Controller被AOP切入后,注解读取不到了,这就是今天要分享的问题。

正常情况下,我们的class是 com.cxytiandi.eureka_client.controller.ArticleController这种形式,如果用了AOP后,那么就会变成 com.cxytiandi.eureka_client.controller.ArticleController$$EnhancerBySpringCGLIB$$3323dd1e这样了。

解决方案一

这种情况下拿到的Method也是被代理了的,所以Method上的注解自然获取不到,既然知道原因了,最简单快速的解决方法就是将多余的内容截取掉,然后重新得到一个没有被代理的Class对象,通过这个Class对象来获取Method,这样就可以获取到Method上的注解。

代码语言:javascript
复制
Class<?> clz = bean.getClass();String fullName = clz.getName();if (fullName.contains("EnhancerBySpringCGLIB") || fullName.contains("$$")) {    fullName = fullName.substring(0, fullName.indexOf("$$"));    try {        clz = Class.forName(fullName);    } catch (ClassNotFoundException e) {        throw new RuntimeException(e);    }}Method[] methods = clz.getMethods();for (Method method : methods) {    if (method.isAnnotationPresent(Encrypt.class)) {         String uri = method.getAnnotation(Encrypt.class).value();    }}

解决方案二

虽然问题解决了,但是还是觉得不够优雅,有没有更好的方式呢?我们可以用Spring里面提供的AnnotationUtils来读取注解。

代码语言:javascript
复制
Encrypt encrypt = AnnotationUtils.findAnnotation(method, Encrypt.class);if (encrypt != null) {    String uri = encrypt.value();}

AnnotationUtils.findAnnotation()原理是什么呢?为什么它可以获取到被代理后方法上的注解呢?

要想知道原理,那就只能看源码啦,源码多,不贴出来了,贴一点点关键的就行了

首先会构建一个AnnotationCacheKey,从本地缓存中获取,如果有的话直接返回,也就意味着只要读取过就会被缓存起来:

代码语言:javascript
复制
AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType);A result = (A) findAnnotationCache.get(cacheKey);

然后就是判断是否桥接方法,如果不是就直接返回,是的话则获取桥接方法的注解,如果还获取不到就通过接口来获取。

代码语言:javascript
复制
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType);if (result == null) {    result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces());}

后面就不继续下去了,最关键的代码其实是这句:

代码语言:javascript
复制
clazz = clazz.getSuperclass();

因为CGLIB代理会为目标类动态生成一个子类,所以我们要获取最原始的类,直接使用getSuperclass就可以了,跟第一种方案是一致的,只是第一种看起来有点那啥哈.....

推荐大家用AnnotationUtils去获取,这里面封装了很多的逻辑,考虑了很多场景下的问题,切莫重复造轮子。

加入星球特权

1、从前端到后端玩转Spring Cloud

2、实战分库分表中间件Sharding-JDBC

3、实战分布式任务调度框架Elastic Job

4、配置中心Apollo实战

5、高并发解决方案之缓存

6、更多课程等你来解锁,20+课程

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 猿天地 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题背景
  • 解决方案一
  • 解决方案二
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档