aop切面调用失效问题排查

  应用里有较多的地方访问外部供应商接口,由于公网网络不稳定或者外部接口不稳定(重启,发版,ip切换)的原因,经常报502或者504错误。为了解决HTTP调用的500报错,选择使用spring的retryable注解进行重试。给所有的RestTemplate调用的函数都加上了@Retryable注解,本地测试发现好多都不起效果。
  网上搜索有很多帖子反馈@Retryable注解不生效,不生效的点在于:切面切的方法,是在所在的类里被另外的方法调用的,而不是被外部类直接调用。比较难以理解,直接上代码演示:

public class AopClass {
    
    

	public void methodA() {
    
    
		methodB();
	}
	
	@Retryable
	public void methodB() {
    
    
		System.out.println();
	}
}

public class Client{
    
    
	public static void main(String[] args) {
    
    
		AopClass aop = new AopClass();
		// 切面不会起效
		aop.methodA();
	}
}

  不仅仅是@Retryable有这个问题,所有的切面实现的功能,如果这样调用都有问题。这里通过分析cglib的动态生成的字节码类来分析:

aop代理类

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {
    
    

	private Enhancer enhancer = new Enhancer();

	public Object getProxy(Class clazz) {
    
    
		//设置需要创建子类的类
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		//通过字节码技术动态创建子类实例
		return enhancer.create();
	}

	//实现MethodInterceptor接口方法
	public Object intercept(Object obj, Method method, Object[] args,
	                        MethodProxy proxy) throws Throwable {
    
    
		System.out.println("前置代理");
		//通过代理类调用父类中的方法
		Object result = proxy.invokeSuper(obj, args);
		System.out.println("后置代理");
		return result;
	}
}

被代理类

public class SayHello {
    
    

	public void callee() {
    
    
		System.out.println("hello everyone");
	}
	
	public void caller() {
    
    
		this.callee();
	}
}

配置好-Dcglib.debugLocation参数

查看动态生成的类(第三个)
在这里插入图片描述

通过反编译工具查看class文件

public class SayHello$$EnhancerByCGLIB$$11ca72e0 extends SayHello implements Factory {
    
    
	private MethodInterceptor CGLIB$CALLBACK_0;
	
    public final void caller() {
    
    
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
    
    
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
		
		// 如果被代理
        if (var10000 != null) {
    
    
            var10000.intercept(this, CGLIB$caller$0$Method, CGLIB$emptyArgs, CGLIB$caller$0$Proxy);
        } else {
    
    
        // 如果没被代理
            super.caller();
        }
    }
}

  通过生成的类可以发现,如果直接调用没被代理的方法,那么调用的是父类SayHello的方法,也就是没被加强的方法。

猜你喜欢

转载自blog.csdn.net/bruce128/article/details/130496845