CGLib动态代理

本文介绍了CGLib动态代理的实现原理与实践应用。通过具体案例展示了如何使用CGLib生成代理对象,并深入探讨了不同回调接口的应用场景,包括FixedValue、InvocationHandler及MethodInterceptor等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 参考链接:cglib: The missing manual


 CGLib(Code Generation Library)同JDK动态代理的目的一致,在运行时生成代理类字节码加载到JVM中,并生成代理对象。CGLib底层基于ASM(一个Java字节码操控框架),即能够为接口生成代理,又能为类生成代理。首先看一个例子:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>3.1</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

public class ProxyTarget {
    public String method1(String param) {
        System.out.println("ProxyTarget:method(), param: " + param);
        return param;
    }
}

@Test(expected = ClassCastException.class)
public void testFixedValue() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(ProxyTarget.class);
    enhancer.setCallback(new FixedValue() {
        @Override
        public Object loadObject() throws Exception {
            return "Hello, CGLib!";
        }
    });
    ProxyTarget proxy = (ProxyTarget) enhancer.create();

    System.out.println(proxy.method1(null)); // Hello, CGLib!
    System.out.println(proxy.toString()); // Hello, CGLib!
    // Object.getClass()是final方法,不进行代理
    System.out.println(proxy.getClass()); // ProxyTarget$ $EnhancerByCGLIB$$c1696218
    System.out.println(proxy.hashCode()); // ClassCastException: String cannot be cast to Number
}
 Enhancer类相当于JDK中的Proxy类,用来生成代理对象的核心类。第一步:设置了要代理的类:setSuperclass()。第二步设置代理回调处理接口:setCallback()。第三步:生成代理对象。第二步中,设置的回调匿名内部类FixedValue接口继承了Callback接口。表明对代理的方法均返回固定值。

 CGLib生成的代理对象不能够对final修饰的方法进行代理(如Object的getClass方法),执行这些方法跟直接执行效果相同。而执行Object的hashCode()方法(不是final的)时,可以在运行时得到ClassCastException异常。


 回调接口还可以设置为 InvocationHandler(CGLib库中作为JDK的InvocationHandler的替代)、MethodInterceptor等。使用InvocationHandler时要注意会导致递归调用,因为每一次对方法的调用都会被转发到同一个InvocationHandler对象的invoke方法中。

@Test(expected = RuntimeException.class)
public void testInvocationHandler() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(ProxyTarget.class);
    enhancer.setCallback(new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 此方法里不能调用method.invoke,会导致递归调用
            // Object result = method.invoke(proxy, method);
            if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
                return "Hello, CGLib!";
            } else {
                throw new RuntimeException("Don not know what to do.");
            }
        }
    });
    ProxyTarget proxy = (ProxyTarget) enhancer.create();

    System.out.println(proxy.method1(null)); // Hello, CGLib!
    System.out.println(proxy.hashCode()); // RuntimeException: Don not know what to do.
}

 来看看设置MethodInterceptor接口的回调:

@Test
public void testMethodInterceptor() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(ProxyTarget.class);
    enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
                return "Hello, CGLib!";
            } else {
                return proxy.invokeSuper(obj, args); // 执行原方法(被代理的方法)
            }
        }
    });
    ProxyTarget proxy = (ProxyTarget) enhancer.create();

    System.out.println(proxy.method1(null)); // Hello, CGLib!
    System.out.println(proxy.toString()); // ProxyTarget$ $EnhancerByCGLIB$$1de8b511@5d3411d
    System.out.println(proxy.hashCode()); // 97730845
}
 该接口中的interceptor接口接收四个参数:被代理对象、被代理的方法、传入方法的参数和方法代理(MethodProxy)。 其中invokeSuper()方法用来执行被代理对象上的方法,不会发生递归调用。


 如果要通过代理对不同的方法执行不同的回调该怎么做呢?CGLib提供了CallbackFilter接口CallbackHelper(实现了 CallbackFilter接口)来达到这个目的:


@Test
public void testCallbackHelper() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(ProxyTarget.class);
    CallbackHelper callbackHelper = new CallbackHelper(ProxyTarget.class, new Class[0]) {
        @Override
        protected Object getCallback(Method method) {
            if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
                return new FixedValue() {
                    @Override
                    public Object loadObject() throws Exception {
                        return "Hello, CGLib!";
                    }
                };
            } else {
                return NoOp.INSTANCE;
            }
        }
    };
    enhancer.setCallbackFilter(callbackHelper);
    enhancer.setCallbacks(callbackHelper.getCallbacks());
    ProxyTarget proxy = (ProxyTarget) enhancer.create();

    System.out.println(proxy.method1(null)); // Hello, CGLib!
    System.out.println(proxy.toString()); // ProxyTarget$ $EnhancerByCGLIB$$a66a4ff1@44e81672
}

@Test
public void testCallbackFilter() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(ProxyTarget.class);
    Callback[] callbacks = {new MethodInterceptor() {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            return "proxy - " + proxy.invokeSuper(obj, args);
        }
    }, new FixedValue() {
        @Override
        public Object loadObject() throws Exception {
            return 100;
        }
    }, NoOp.INSTANCE};
    enhancer.setCallbackFilter(new CallbackFilter() {
        @Override
        public int accept(Method method) {
            if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class)
                return 0;
            if (method.getReturnType() == Integer.class || method.getReturnType() == int.class)
                return 1;
            return 2;
        }
    });
    enhancer.setCallbacks(callbacks);
    ProxyTarget proxy = (ProxyTarget) enhancer.create();

    System.out.println(proxy.method1("OK")); // proxy - OK
    System.out.println(proxy.hashCode()); // 100
    System.out.println(proxy.toString()); // ProxyTarget$ $EnhancerByCGLIB$$89c0dffe@64
}


 推荐博文: 入浅出CGlib-打造无入侵的类代理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值