CGLIB无法拦截静态方法

发布于 2022-09-01 06:24:23 字数 2496 浏览 24 评论 0

我在用cglib实现动态代理功能时,发现无法拦截静态方法,而是直接调用的静态方法。
追踪了一下源码,发现把静态方法过滤掉了,没做拦截。

代理模式中的真实角色和代理角色都继承或实现一个抽象类或接口,甚至普通的类也行,都是代理模式的大致意思,代理角色中传入真实角色实例后,在调用真实方法前后或做处理。
那么,既然抽象出来的那“哥们”,可以是类,比如cglib中就是利用继承,对pojo进行动态字节码生成其子类,即生成的代理角色,但是不代理static普通方法,而我自己写一个靠继承实现的代理的话,显然能实现,即重写抽象出的类的static普通方法。

想问问,为什么会这样,为什么要这样?恳请各位解答,不甚感激!

下面是做的例子,比较了static和非static方法被拦截的情况。
真实对象

public class TargetObject {
    public  static   void businessLogic() {
        System.out.println("目标对象/真实对象中的      业务逻辑处理");
    }
}

具体代理控制逻辑,实现MethodInterceptor接口

public class AroundAdvice implements MethodInterceptor {
    /*
     * 重写方法
     * 
     * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object,
     * java.lang.reflect.Method, java.lang.Object[],
     * net.sf.cglib.proxy.MethodProxy)
     */
    /**
     * Parameters: obj - "this", the enhanced object
     * 
     * method - intercepted Method args - argument array; primitive types are
     * wrapped
     * 
     * proxy - used to invoke super (non-intercepted method); may be called as
     * many times as needed
     * 
     * Returns: any value compatible with the signature of the proxied method.
     * Method returning void will ignore this value.
     */
    @Override
    public Object intercept(Object target, Method method, Object[] arg2,
            MethodProxy methodProxy) throws Throwable {
        System.out.println("目标对象/真实对象      方法调用之前...");
        Object result=methodProxy.invokeSuper(target,arg2);
        System.out.println("目标对象/真实对象      方法调用之后...");
        return result+"<--真实对象的返回值。    \"通知\"中的新加内容";
    }
}

生成代理对象的工具类

public class ProxyUtil {
    public static TargetObject getProxy() {
        // 增强处理器:拦截的方法插入代理控制逻辑的处理器
        Enhancer enhancer = new Enhancer();
        // 设置要代理的目标类
        enhancer.setSuperclass(TargetObject.class);
        // 设置要代理的拦截器
        enhancer.setCallback(new AroundAdvice());
        // 生成并返回代理对象
        return (TargetObject) enhancer.create();
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        TargetObject targetObject = ProxyUtil.getProxy();
        targetObject.businessLogic();
    }
}

TargetObject中的方法为static修饰时,打印:
目标对象/真实对象中的 业务逻辑处理

去掉static,打印:
目标对象/真实对象 方法调用之前...
目标对象/真实对象中的 业务逻辑处理
目标对象/真实对象 方法调用之后...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

耀眼的星火 2022-09-08 06:24:23

静态代理用AspectJ,我特地用AspectJ试了下,单纯的AspectJ也实现不了。不过用asm可以实现,运行期间修改class字节码实现的,不过很麻烦啊,你要是没什么特殊需求还是规规矩矩的正常写吧。稍后我放一段asm怎么改的

不管怎样,下面我还是贴一下asm的实现

public class TargetObject {

    public static void businessLogic() {
        System.out.println("static目标对象/真实对象中的      业务逻辑处理");
    }
}
public class ProxyUtil {

    public void businessLogic() {}

    public static void before() {  
        System.out.println("目标对象/真实对象      方法调用之前...");
    }  

    public static void after() {
        System.out.println("目标对象/真实对象      方法调用之后...");
    }
}
public class Test extends ClassLoader implements Opcodes{
    public static void main(String[] args) throws Exception {
        ClassReader cr = new ClassReader("TargetObject"); 
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 
        ClassVisitor classAdapter = new TargetObjectAdapter(Opcodes.ASM4,cw);
        cr.accept(classAdapter, ClassReader.SKIP_DEBUG); 
        byte[] data = cw.toByteArray(); 
        Test loader = new Test();
        Class<?> appClass=loader.defineClass(null, data, 0,data.length);
        appClass.getMethods()[0].invoke(appClass.newInstance(), new Object[]{});
    }
}
public class TargetObjectAdapter extends ClassVisitor {

    private MethodVisitor mv;

    public TargetObjectAdapter(int api, ClassVisitor cw) {
        super(api, cw);
    }

    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
            String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
        if (name.equals("businessLogic")) {
            return new MethodVisitor(this.api, mv) {
                public void visitCode() {
                    super.visitCode();
//                    this.visitMethodInsn(Opcodes.INVOKESTATIC, "ProxyUtil", "before", "()V");
                    mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
                    mv.visitLdcInsn("before");
                    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
                }

                public void visitInsn(int opcode) {
                    if (opcode == Opcodes.RETURN) {
                        mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
                        mv.visitLdcInsn("after");
                        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
//                        this.visitMethodInsn(Opcodes.INVOKESTATIC, "ProxyUtil", "after", "()V");
                    }
                    super.visitInsn(opcode);
                }
            };
        }
        return mv;
    }
}
木森分化 2022-09-08 06:24:23

为什么cglib不能拦截静态方法是因为Cglib的实现方式:生成的代理类继承自被代理类/目标类,请求时,执行自己织入的增强,然后再执行目标方法,因而使用继承方式创建代理类不能代理任何final方法和类,以及静态方法。

爱给你人给你 2022-09-08 06:24:23
  1. 这就要从原理说起了,cglib生成的代理对象只是单纯的继承被代理对象,然后重写每个方法去调用MethodInterceptor的intercept()方法,继承嘛,只能重写普通方法,而不能对final方法和static方法进行操作
  2. 还有那位说单纯的AspectJ不能处理static方法是错误的,单纯的AspectJ可通过后编译织入处理static方法(post compile weaving),你用的使用是spring aop(即不单纯的AspectJ),他是通过动态代理实现的,也就是jdk代理或者cglib代理,此时又绕回上述1中了
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文