CGLIB 实现动态代理

发表于 2020-07-10  33 次阅读


CGLIB 简介

CGlib是一个强大的,高性能,高质量的Code生成类库;它可以在运行期扩展Java类与实现Java接口。
CGlib对 ASM 进行封装,简化了ASM的操作,实现了在运行期动态生成新的class。

  • spring的aop底层实现(非接口代理)
  • hibernate使用cglib动态生成VO/PO(接口层对象)

CGLIB开源项目

CGLIB与JDK动态代理对比

代理方式实现优点缺点
JDK动态代理实现被代理对象的接口运行期生成字节码,直接写Class字节码,效率高只能代理实现了接口的类
CGLIB动态代理继承被代理类通过FastClass机制调用方法,比JDK动态代理的反射机制效率高;被代理类无需实现接口运行期生成字节码,通过ASM写Class字节码,效率低;不能对final类及final方法进行代理

CGLIB动态代理的简单实现

添加maven依赖

<!-- CGLIB动态代理依赖 -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

构建方法拦截器 MethodInterceptor

package 设计模式.动态代理.CGLIB;

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

import java.lang.reflect.Method;

public class TestMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] orgs, MethodProxy methodProxy) throws Throwable {
        System.out.println("before execute ...");
        Object result = methodProxy.invokeSuper(o, orgs);
        System.out.println("after execute ...");
        return result;
    }
}

构建代理工具类 TestCGLIBProxy

package 设计模式.动态代理.CGLIB;

import net.sf.cglib.proxy.Enhancer;

public class TestCGLIBProxy {
    public static Test creatProxy() {
        Enhancer enhancer = new Enhancer();
        // 设置被代理类
        enhancer.setSuperclass(Test.class);
        // 设置方法拦截器
        enhancer.setCallback(new TestMethodInterceptor());
        return ( Test ) enhancer.create();
    }
}

构建 Test 测试类

package 设计模式.动态代理.CGLIB;

import net.sf.cglib.core.DebuggingClassWriter;

public class Test {

    public static void main(String[] args) {
        // 设置CGLIB的字节码文件存放路径
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\代码\\git-demo\\jdk-demo\\target");

        TestCGLIBProxy.creatProxy().sayHello();
    }

    public String sayHello() {
        return "hello world!";
    }
}

只代理满足指定条件的方法

有些时候我们可能只想对特定的方法进行拦截,对其他的方法直接放行,不做任何操作,使用Enhancer处理这种需求同样很简单,只需要一个CallbackFilter即可:

package 设计模式.动态代理.CGLIB;

import net.sf.cglib.proxy.CallbackHelper;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.FixedValue;
import net.sf.cglib.proxy.NoOp;

import java.lang.reflect.Method;

public class TestCallbackFilter {
    public static void main(String[] args) {
        CallbackHelper callbackHelper = new CallbackHelper(Test.class, null) {

            @Override
            protected Object getCallback(Method method) {
                // 根据条件返回不同的 Callback
                if (method.getName().equals("sayHello")) {
                    return ( FixedValue ) () -> "test fixed value!";
                }
                return NoOp.INSTANCE;
            }
        };

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Test.class);
        enhancer.setCallbackFilter(callbackHelper);
        enhancer.setCallbacks(callbackHelper.getCallbacks());
        Test testProxy = ( Test ) enhancer.create();
        System.out.println(testProxy.sayHello());
        System.out.println(testProxy.toString());

    }
}

注:由于CGLIB的实现原理是创建一个被代理类的子类,所以所有的final修饰的类无法实现代理,同理所有final修饰的方法无法实现代理,测试代码如下:

package 设计模式.动态代理.CGLIB;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.FixedValue;

public class TestFixedValue {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Test.class);
        enhancer.setCallback(( FixedValue ) () -> "test fixed value!");
        Test testProxy = ( Test ) enhancer.create();

        System.out.println(testProxy.sayHello());
        System.out.println(testProxy.toString());
        System.out.println(testProxy.getClass());// final 方法无法继承,因此无法拦截
        System.out.println(testProxy.hashCode());// 报错因为hashCode()回参是int,方法拦截后返回的是String
    }
}


从业时长3年半的佛系码农,并不会唱跳、rap和篮球