java - CGLIB无法拦截静态方法
大家讲道理
大家讲道理 2017-04-17 13:29:04
0
2
620

我在用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,打印:
目标对象/真实对象 方法调用之前...
目标对象/真实对象中的 业务逻辑处理
目标对象/真实对象 方法调用之后...

大家讲道理
大家讲道理

光阴似箭催人老,日月如移越少年。

reply all(2)
迷茫

Static proxy uses AspectJ. I specifically tried it with AspectJ, but it cannot be achieved by simple AspectJ. However, it can be implemented using asm. It can be implemented by modifying the class bytecode during runtime, but it is very troublesome. If you have no special needs, just write it normally. I will post a piece of asm later on how to change it

Anyway, I will post the implementation of asm below

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;
    }
}
小葫芦

Why cglib cannot intercept static methods is because of the way Cglib is implemented: the generated proxy class inherits from the proxy class/target class. When requested, it executes its own woven enhancements and then executes the target method, so it is created using inheritance. The proxy class cannot proxy any final methods and classes, as well as static methods.

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!