CGLIB 是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为 JDK 的动态代理提供了很好的补充。通常可以使用 Java 的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB 是一个好的选择。
CGLIB 被广泛的运用在许多 AOP 的框架中,例如 Spring AOP 和 dynaop。Hibernate 使用 CGLIB 来代理单端 single-ended(多对一和一对一)关联。
CGLIB 作为一个开源项目,其代码托管在 github,地址为:https://github.com/cglib/cglib
CGLIB 的原理是动态生成一个被代理类的子类,子类重写被代理的类的所有非 final
的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,织入横切逻辑。
CGLIB 底层使用字节码处理框架 ASM,来转换字节码并生成新的类。不鼓励直接使用 ASM,因为它要求必须对 JVM 内部结构包括 class 文件的格式和指令集都很熟悉。
CGLIB 优点:比 JDK 动态代理效率更高
CGLIB 缺点:对于 final
类和方法,无法进行代理。
代码示例
有一个原始类,没有实现任何接口
public class Azh3ngService {
public String method1(String param) {
return param;
}
public String method2(String param) {
return param;
}
public String method3(String param) {
return param;
}
}
拦截器
定义一个拦截器,在调用目标方法时,CGLib 会回调 MethodInterceptor
接口方法拦截,来实现自定义的代理逻辑,类似于 JDK 中的 InvocationHandler
接口
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 自定义目标对象拦截器
*/
public class Azh3ngInterceptor implements MethodInterceptor{
/**
* 重写方法拦截在方法前和方法后加入业务
* Object obj为目标对象
* Method method为目标方法
* Object[] params 为参数,
* MethodProxy proxy CGlib方法代理对象
*/
@Override
public Object intercept(Object obj, Method method, Object[] params, MethodProxy proxy) throws Throwable {
System.out.println("before...");
Object result = proxy.invokeSuper(obj, params);
System.out.println("after...");
return result;
}
}
参数:Object obj
为由 CGLib 动态生成的代理类实例;Method method
为上文中实体类所调用的被代理的方法引用;Object[] params
是参数值列表;MethodProxy proxy
是生成的代理类对方法的代理引用。
返回:从代理实例的方法调用返回的值。
其中,proxy.invokeSuper(obj,arg)
表示调用代理类实例的父类的方法(即实体类 Azh3ngService
中对应的方法)
生成代理类
使用main
方法进行测试
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
public class TestCglibProxy {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Azh3ngService.class);
enhancer.setCallback(new Azh3ngInterceptor());
Azh3ngService azh3ngService = (Azh3ngService) enhancer.create();
System.out.println(azh3ngService);
System.out.println("result: " + azh3ngService.method1("test"));
/*
output:
before...
result: test
after...
*/
}
}
Enhancer
类是 CGLib 中的一个字节码增强器。
首先将被代理类 Azh3ngService
设置成父类,然后设置拦截器 Azh3ngInterceptor
,最后执行 enhancer.create()
动态生成一个代理类,并强制转型成父类 Azh3ngService
,最后,在代理类上调用方法
回调过滤器CallbackFilter
CallbackFilter 的作用:在 CGLib 回调时可以设置对不同方法执行不同的回调逻辑,或者不执行回调。
在 JDK 动态代理中没有类似的功能,对 InvocationHandler 接口方法的调用对代理类内的所以方法都有效。
代码示例:
定义实现过滤器 CallbackFilter
接口的类:
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
/**
* 回调方法过滤
*/
public class TargetMethodCallbackFilter implements CallbackFilter {
/**
* 过滤方法
* 返回的值为数字,代表了 Callback 数组中的索引位置,要到用的 Callback
*/
@Override
public int accept(Method method) {
if(method.getName().equals("method1")){
System.out.println("filter method1 == 0");
return 0;
}
if(method.getName().equals("method2")){
System.out.println("filter method2 == 1");
return 1;
}
if(method.getName().equals("method3")){
System.out.println("filter method3 == 2");
return 2;
}
return 0;
}
}
其中 return 值为被代理类的各个方法在回调数组 Callback[]
中的位置索引
使用 main
方法测试
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
import net.sf.cglib.proxy.FixedValue;
public class TestCglib {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Azh3ngService.class);
CallbackFilter callbackFilter = new TargetMethodCallbackFilter();
/*
* 1. callback1:方法拦截器
* 2. NoOp.INSTANCE:这个NoOp表示 no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截。
* 3. FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。
*/
Callback azh3ngInterceptor = new Azh3ngInterceptor();
Callback noopCb = NoOp.INSTANCE;
Callback fixedValue = new TargetResultFixed();
Callback[] cbArray = new Callback[]{azh3ngInterceptor, noopCb, fixedValue};
enhancer.setCallbacks(cbArray);
enhancer.setCallbackFilter(callbackFilter);
Azh3ngService azh3ngService = (Azh3ngService) enhancer.create();
System.out.println(azh3ngService);
// method1 方法会被 callbackFilter 匹配到 cbArray 索引为 0 的 azh3ngInterceptor
System.out.println(azh3ngService.method1("test"));
// method2 方法会被 callbackFilter 匹配到 cbArray 索引为 1 的 noopCb
System.out.println(azh3ngService.method2("1"));
// method3 方法会被 callbackFilter 匹配到 cbArray 索引为 2 的 fixedValue
System.out.println(azh3ngService.method3("1"));
System.out.println(azh3ngService.method3("2"));
/*
output:
filter method1 == 0
before...
result: test
after...
result: 1
锁定结果
result: 999
锁定结果
result: 999
*/
}
public class TargetResultFixed implements FixedValue {
/**
* 实现 FixedValue 接口,锁定回调值为 999
* (整型,CallbackFilter中定义的使用FixedValue型回调的方法为 getConcreteMethodFixedValue,该方法返回值为整型)
*/
@Override
public Object loadObject() throws Exception {
System.out.println("锁定结果");
return 999;
}
}
}