项目中spring proxy遇到的bug
1、类被代理导致无法正确获取到注解
简介:mq消息消费接口,有多个实现类,每个实现类都有Tag注解,Tag注解上有关键字段名称,消息过来根据Tag标签寻找实现类进行解析处理,所以在程序初始化的时候需要又一个map,存储着tag标签名以及其对应的实现类,问题出在初始化的时候,实现类上的Tag注解没有正确获取到。
以下为自己测试所用代码:
public interface MessageComsumer {
public void consume(Record record);
}
@Component
@Tag(name = "taoge2222")
public class MagComsumer implements MessageComsumer {
@Override
public void consume(Record record) {
start();
}
public void start() {
}
}
@Component
@Tag(name = "taoge")
public class FagComsumer implements MessageComsumer {
@Override
public void consume(Record record) {
start();
}
@Async
public void start() {
}
}
@RequestMapping("/test1")
@ResponseBody
public String test1(){
Map<String, MessageComsumer> beansOfType = applicationContext.getBeansOfType(MessageComsumer.class);
for (MessageComsumer messageProcessor : beansOfType.values()) {
Tag annotation = messageProcessor.getClass().getAnnotation(Tag.class);
if (annotation != null) {
String tag = annotation.name();
System.out.println(tag);
}
}
return "test1";
}
结果打印实现类Tag注解名称时,只成功打印了一个,FagComsumer未成功打印,原因就出在@Async,因为@Async的原理也是通过代理,所以获取到的是代理类,所以没有办法成功获取到实现类上的注解
所以在遇到这种情况的时候需要判断是否是代理类
@RequestMapping("/test2")
@ResponseBody
public String test2(){
Map<String, MessageComsumer> beansOfType = applicationContext.getBeansOfType(MessageComsumer.class);
for (MessageComsumer messageProcessor : beansOfType.values()) {
Tag annotation = messageProcessor.getClass().getAnnotation(Tag.class);
if (annotation != null) {
String tag = annotation.name();
System.out.println(tag);
}else{
if(AopUtils.isAopProxy(messageProcessor)){
try {
Object obj;
//判断是jdk还是cglib代理
if (AopUtils.isJdkDynamicProxy(messageProcessor)) {
obj = getJdkDynamicProxyTargetObject(messageProcessor);
} else {
obj = getCglibDynamicProxyTargetObject(messageProcessor);
}
Tag annotation2 = obj.getClass().getAnnotation(Tag.class);
if(annotation2!=null){
System.out.println(annotation2.name());
}
}catch (Exception e){
}
}
}
}
return "test2";
}
private static Object getCglibDynamicProxyTargetObject(Object obj) throws Exception {
Field h = obj.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(obj);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
return target;
}
private static Object getJdkDynamicProxyTargetObject(Object obj) throws Exception {
Field h = obj.getClass().getSuperclass().getDeclaredField("h");
h.setAccessible(true);
AopProxy aopProxy = (AopProxy)h.get(obj);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
return target;
}
这样就可以成功获取,当然这种方案可能还是有问题,因为如果遇到多重代理的问题,需要递归获取。
2、对同一个方法多个aop实现的问题
简介:repo层更新时,原本已经对该方法进行有过切面主要用于信息同步到第三方系统(AfterReturing),现在需要对该方法再次进行切面,主要是使用AfterReturing对更新之后的模型进行处理,结果出现了bug,对他的数据同步造成了影响,我把自己的切面关掉之后就正常了。
解决方法就是保证对方的aop实现先执行,所以就需要搞清楚多个aop谁先执行的问题,其实Order注解可控制。
spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfter、doAfterReturn方法。也就是说对多个AOP来说,先before的,一定后after。