1. Class<?> clz = bean.getClass();
2. Method[] methods = clz.getMethods();
3. for (Method method : methods) {
4. if (method.isAnnotationPresent(Encrypt.class)) {
5. String uri = method.getAnnotation(Encrypt.class).value();
6. }
7. }
正在我沾沾自喜的时候,面试官又乘胜追击了,那么在读取注解的时候,有没有什么情况会导致刚刚你说的方式是不能成功判断和读取的呢?
这我一下蒙圈了,还会有读取不到的情况么?之前没遇到过啊,于是我斩钉截铁的回答面试官,不可能读取不到的,面试官笑了笑............
在我的加密框架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上的注解。
1. Class<?> clz = bean.getClass();
2. String fullName = clz.getName();
3. if (fullName.contains("EnhancerBySpringCGLIB") || fullName.contains("$$")) {
4. fullName = fullName.substring(0, fullName.indexOf("$$"));
5. try {
6. clz = Class.forName(fullName);
7. } catch (ClassNotFoundException e) {
8. throw new RuntimeException(e);
9. }
10. }
11. Method[] methods = clz.getMethods();
12. for (Method method : methods) {
13. if (method.isAnnotationPresent(Encrypt.class)) {
14. String uri = method.getAnnotation(Encrypt.class).value();
15. }
16. }
解决方案二
虽然问题解决了,但是还是觉得不够优雅,有没有更好的方式呢?我们可以用Spring里面提供的AnnotationUtils来读取注解。
1. Encrypt encrypt = AnnotationUtils.findAnnotation(method, Encrypt.class);
2. if (encrypt != null) {
3. String uri = encrypt.value();
4. }
AnnotationUtils.findAnnotation()原理是什么呢?为什么它可以获取到被代理后方法上的注解呢?
要想知道原理,那就只能看源码啦,源码多,不贴出来了,贴一点点关键的就行了
首先会构建一个AnnotationCacheKey,从本地缓存中获取,如果有的话直接返回,也就意味着只要读取过就会被缓存起来:
1. AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType);
2. A result = (A) findAnnotationCache.get(cacheKey);
然后就是判断是否桥接方法,如果不是就直接返回,是的话则获取桥接方法的注解,如果还获取不到就通过接口来获取。
1. Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
2. result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType);
3. if (result == null) {
4. result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces());
5. }
后面就不继续下去了,最关键的代码其实是这句:
1、从前端到后端玩转Spring Cloud
2、实战分库分表中间件Sharding-JDBC
3、实战分布式任务调度框架Elastic Job
4、配置中心Apollo实战
5、高并发解决方案之缓存
6、更多课程等你来解锁,20+课程
尹吉欢
我不差钱啊
钟意作者
原文:https://blog.51cto.com/14888386/2515777