1. cglib 介绍
cglib是一个功能强大,高性能的代码生成包,它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。
1.1 cglib原理
- 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
- 底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
- 缺点:对于final方法,无法进行代理。
1.2 cglib的应用
广泛的被许多AOP的框架使用,例如Spring AOP
和dynaop
。Hibernate
使用CGLIB
来代理单端single-ended
(多对一和一对一)关联。
1.3 为什么使用cglib
CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了
1.4 cglib 组成结构
CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy何BeanShell)也使用ASM生成字节码。ASM使用类似SAX的解析器来实现高性能。我们不鼓励直接使用ASM,因为它需要对Java字节码的格式足够的了解。
1.5 cglib的API
1、Jar包:
- cglib-nodep-2.2.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
- cglib-2.2.jar:使用此jar包需要关联asm的jar包,否则运行时报错.
2、CGLIB类库:
由于基本代码很少,学起来有一定的困难,主要是缺少文档和示例,这也是CGLIB的一个不足之处。
本系列使用的CGLIB版本是2.2。
- net.sf.cglib.core: 底层字节码处理类,他们大部分与ASM有关系。
- net.sf.cglib.transform: 编译期或运行期类和类文件的转换
- net.sf.cglib.proxy: 实现创建代理和方法拦截器的类
- net.sf.cglib.reflect: 实现快速反射和C#风格代理的类
- net.sf.cglib.util: 集合排序等工具类
- net.sf.cglib.beans: JavaBean相关的工具类
2. cglib使用
上面讲了一大段cglib
的基本概念以及Api等,那么下面开始介绍通过MethodInterceptor
和Enhancer
实现一个动态代理,看下cglib
底层是如何实现动态代理的。
被代理类:
1
2
3
4
5
6
7
8
9
10
11
12public class TargetObject{
public String method(){
return "method";
}
public void voidMethod(){
System.out.println("-----void-----");
}
public String method1(String name){
return name;
}
}定义一个拦截器,在调用目标方法时,cglib会回调
MethodInterceptor
接口方法拦截,来实现自己的代理逻辑,类似于JDK中的InvocationHandler
接口1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25/**
* 目标对象拦截器实现, 实现MethodInterceptor
*
* @author xiaoyuge
*/
public class TargetInterceptor implements MethodInterceptor {
/**
* 重写方法拦截在方法前和方法后加入业务
*
* @param o 目标对象, 由cglib动态生成的代理类示例
* @param method 目标方法,实体类所调用的被代理的方法引用
* @param objects 参数值列表
* @param methodProxy cglib方法代理对象
* @return 结果
* @throws Throwable 异常信息
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("--------调用前-------");
//调用代理类示例上的proxy 方法的弗雷方法(即实体类TargetObject中对应的方法)
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("------调用后:" + result);
return result;
}
}生成动态代理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import net.sf.cglib.proxy.Enhancer;
public class TestCglib {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetObject.class);
enhancer.setCallback(new TargetInterceptor());
TargetObject target = (TargetObject) enhancer.create();
System.out.println(target);
System.out.println(target.method1("xiaoyuge"));
System.out.println(target.method());
target.voidMethod();
}
}输出:
1
2
3
4
5
6
7
8
9org.example.TargetObject$$EnhancerByCGLIB$$8db9696e@5e91993f
--------调用前-------
------调用后:xiaoyuge
xiaoyuge
--------调用前-------
------调用后:method
--------调用前-------
-----void-----
------调用后:null- Enhancer类:cglib中的一个字节码增强器,他可以方便的对你想要处理的类进行扩展。
- 首先将被代理类设置为父类,然后设置拦截器
TargetInterceptor
,最后执行enhancer.create()
动态生成一个代理类,并从Object强转为弗类型TargetObject
,最后在代理类上调用方法
- 回调过滤器CallbackFilter
在cglib回调时可以设置不同方法执行不同的回调逻辑,或者根本不执行回调,在JDK动态代理中并没有类似的功能,对InvocationHandler接口方法的调用对代理类内的所以方法都有效。
定义实现过滤器CallbackFilter接口的类:其中return值为被代理类的各个方法在回调数组Callback[]中的位置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35import net.sf.cglib.proxy.CallbackFilter;
import java.lang.reflect.Method;
/**
* 回调方法过滤器
*
* @author xiaoyuge
*/
public class TargetMethodCallbackFilter implements CallbackFilter {
/**
* 过滤方法,
* 返回的值为数字,代表类Callback数组中的索引位置
*
* @param method 方法
* @return 结果
*/
public int accept(Method method) {
if ("method1".equals(method.getName())) {
System.out.println("filter method1 ==0");
return 0;
}
if ("method".equals(method.getName())) {
System.out.println("filter method1 ==1");
return 1;
}
if ("voidMethod".equals(method.getName())) {
System.out.println("filter method1 ==2");
return 2;
}
return 0;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import net.sf.cglib.proxy.FixedValue;
/**
* 表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。
* @author xiaoyuge
*
*/
public class TargetResultFixed implements FixedValue{
/**
* 该类实现FixedValue接口,同时锁定回调值为123
* (整型,CallbackFilter中定义的使用FixedValue型回调的方法为getConcreteMethodFixedValue,该方法返回值为整型)。
*/
public Object loadObject() throws Exception {
System.out.println("锁定结果");
return 123;
}
}输出:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28import 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 TestCglib {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetObject.class);
CallbackFilter callbackFilter = new TargetMethodCallbackFilter();
//NoOp.INSTANC:表示no operator, 即什么操作也不做,代理类直接调用被代理的方法不进行拦截
Callback noopCb = NoOp.INSTANCE;
Callback callback = new TargetInterceptor();
//FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。
Callback fixedValue = new TargetResultFixed();
Callback[] array = new Callback[]{callback, noopCb, fixedValue};
enhancer.setCallbacks(array);
enhancer.setCallbackFilter(callbackFilter);
TargetObject targetObject = (TargetObject) enhancer.create();
System.out.println(targetObject);
System.out.println(targetObject.method());
System.out.println(targetObject.method1("xiaoyuge"));
targetObject.voidMethod();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13filter method1 ==2
filter method1 ==0
filter method1 ==1
--------调用前-------
--------调用前-------
------调用后:359023572
------调用后:org.example.TargetObject$$EnhancerByCGLIB$$92522e49@156643d4
org.example.TargetObject$$EnhancerByCGLIB$$92522e49@156643d4
method
--------调用前-------
------调用后:xiaoyuge
xiaoyuge
锁定结果 - 延迟加载对象
LazyLoader接口实现了Callback,因此也算是cglib中的一种Callback类型
另一种延迟加载接口Dispatcher,同样也继承于Callback,也是一种回调类型。
两者的区别在于:LazyLoader只在第一次访问延迟加载属性时除法代理类的回调方法,而Dispatcher在每次访问延迟加载属性时都会触发代理类回调方法
延迟加载类 PropertyBean :1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84import net.sf.cglib.proxy.Enhancer;
public class LazyBean {
private String name;
private int age;
/**
* 延迟加载属性
*/
private PropertyBean propertyBean;
private PropertyBean propertyBeanDispatcher;
public LazyBean(String name, int age, PropertyBean propertyBean, PropertyBean propertyBeanDispatcher) {
this.name = name;
this.age = age;
this.propertyBean = propertyBean;
this.propertyBeanDispatcher = propertyBeanDispatcher;
}
/**
* 只有第一次懒加载
* 使用cglib进行懒加载,对需要延迟加载的对象添加代理,在获取该对象属性时先通过代理类回调方法进行对象初始化
* 在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会补初始化类(在cglib的实现中只要去访问该对象内属性的getter方法就会触发代理类回调)
* @return 对象
*/
private PropertyBean createPropertyBean() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PropertyBean.class);
return (PropertyBean) Enhancer.create(PropertyBean.class, new ConcreteClassLazyLoader());
}
/**
* 每次都懒加载
*
* @return 对象
*/
private PropertyBean createPropertyBeanDispatcher() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PropertyBean.class);
return (PropertyBean) Enhancer.create(PropertyBean.class,
new ConcreteClassDispatcher());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public PropertyBean getPropertyBean() {
return propertyBean;
}
public void setPropertyBean(PropertyBean propertyBean) {
this.propertyBean = propertyBean;
}
public PropertyBean getPropertyBeanDispatcher() {
return propertyBeanDispatcher;
}
public void setPropertyBeanDispatcher(PropertyBean propertyBeanDispatcher) {
this.propertyBeanDispatcher = propertyBeanDispatcher;
}
public String toString() {
return "LazyBean{" +
"name='" + name + '\'' +
", age=" + age +
", propertyBean=" + propertyBean +
", propertyBeanDispatcher=" + propertyBeanDispatcher +
'}';
}
}延迟加载器:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class PropertyBean {
private String key;
private Object value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Object getValue() {
return value;
}
public String toString() {
return "PropertyBean{" +
"key='" + key + '\'' +
", value=" + value +
'}';
}
public void setValue(Object value) {
this.value = value;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import net.sf.cglib.proxy.LazyLoader;
/**
* @author xiaoyuge
*/
public class ConcreteClassLazyLoader implements LazyLoader {
/**
* 对于需要延迟加载的对象添加代理,在获取对象属性时先通过代理类回调方法进行对象初始化
* 在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会补初始化类(在cglib的实现中只要去访问该对象内属性的getter方法就会触发代理类回调)
* @return 延迟加载对象
* @throws Exception 异常信息
*/
public Object loadObject() throws Exception {
System.out.println("before lazyLoader...");
PropertyBean propertyBean = new PropertyBean();
propertyBean.setKey("xiaoyuge");
propertyBean.setValue(new TargetObject());
System.out.println("after lazyLoader...");
return propertyBean;
}
}测试方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14/**
* @author xiaoyuge
*/
public class ConcreteClassDispatcher implements Dispatcher {
public Object loadObject() throws Exception {
System.out.println("before Dispatcher...");
PropertyBean propertyBean = new PropertyBean();
propertyBean.setKey("nb");
propertyBean.setValue(new TargetObject());
System.out.println("after Dispatcher...");
return propertyBean;
}
}输出:1
2
3
4
5
6
7
8
9
10
11
12public class TestCglib2 {
public static void main(String[] args) {
LazyBean bean = new LazyBean("xiaoyuge", 18);
//LazyLoader只在第一次访问延迟加载属性时触发代理类回调方法
System.out.println("延迟加载" + bean.getPropertyBean().toString());
System.out.println("延迟加载" + bean.getPropertyBean().toString());
//Dispatcher在每次访问延迟加载属性时都会触发代理类回调方法。
Object obj = bean.getPropertyBeanDispatcher().getValue();
Object obj1 = bean.getPropertyBeanDispatcher().getValue();
}
}从上面可以看出:Dispatcher在每次访问延迟加载属性时都会触发代理类回调方法,LazyLoader只在第一次访问延迟加载属性时触发代理类回调方法1
2
3
4
5
6
7
8
9
10before lazyLoader...
after lazyLoader...
before Dispatcher...
after Dispatcher...
延迟加载PropertyBean{key='xiaoyuge', value=org.example.TargetObject@45fe3ee3}
延迟加载PropertyBean{key='xiaoyuge', value=org.example.TargetObject@45fe3ee3}
before Dispatcher...
after Dispatcher...
before Dispatcher...
after Dispatcher...
- 接口生成器
InterfaceMaker 会动态生成一个接口,该接口包含指定类的所有方法
输出:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InterfaceMaker;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.jupiter.api.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class InterfaceMakerDemo {
public void test() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
InterfaceMaker interfaceMaker = new InterfaceMaker();
//抽取某个类的方法生成接口方法
interfaceMaker.add(TargetObject.class);
Class<?> targetInterface = interfaceMaker.create();
for (Method method : targetInterface.getMethods()) {
System.out.println(method.getName());
}
//接口代理并设置代理接口方法拦截
Object object = Enhancer.create(Object.class, new Class[]{targetInterface}, new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if ("method".equals(method.getName())) {
System.out.println("filter method");
return "method";
}
if ("method1".equals(method.getName())) {
System.out.println("filter method1");
return "method1";
}
if ("voidMethod".equals(method.getName())) {
System.out.println("filter voidMethod");
return "filter voidMethod";
}
return "default";
}
});
Method targetMethod = object.getClass().getMethod("method1", String.class);
String value = (String) targetMethod.invoke(object, new Object[]{"xiaoyuge"});
System.out.println("----"+value);
Method targetMethod1 = object.getClass().getMethod("method");
String value1 = (String) targetMethod1.invoke(object, new Object[] {});
System.out.println("----"+value1);
}
}1
2
3
4
5
6
7method1
voidMethod
method
filter method1
----method1
filter method
----method
3. 底层源码分析
上面讲了通过MethodInterceptor
和Enhancer
实现一个动态代理,那么它的底层是如何实现的呢?
我们都知道,既然要生成一个代理对象,那么肯定需要一个代理类,只不过当我们使用cglib时,这个代理类是由cglib生成的,那么我们要看到这个代理类是怎么生成的,只需要在运行时加上:
1 | -Dcglib.debugLocation=/Users/xiaoyuge/Desktop/classes |
cglib就会将生成的代理类放到上面所指定的路径上。
先看原来的类:
1 | public class TargetObject { |
测试方法:
1 |
|
cglib代理类:
1 | public class TargetObject$$EnhancerByCGLIB$$8db9696e extends TargetObject implements Factory { |
可以看到代理类继承了TargetObject
,然后重写了TargetObject
里面的方法,通过代理对象去调用,代理对象中包含了一个CGLIB$method1$0
和一个method1
方法。
method1
方法内会调用所设置的Callbacks中的intercept(), 相当于执行增强逻辑,如果没有Callbacks,则会执行super.method1()
;
那么如果不设置Callbacks
,是否能够正常执行,接着往下看
1 |
|
运行时,cglib在构造对象时就会报一个没有Callbacks的控制针异常。
1 | java.lang.NullPointerException |
再来看代理类中的另一个方法:
1 | final String CGLIB$method1$0(String var1) { |
这个方法我们不能直接调用,要通过所设置的Callback,也就是MethodInterceptor
中MethodProxy
对象来调用,MethodProxy对象表示方法代理。
假如TargetObject
代理对象在执行method1方法,那么当执行流程进入到intercept()
方法时,MethodProxy
对象表示的就是method1()方法,但是我们现在知道了在TargetObject
类和代理类都有method1
方法,所以MethodProxy
对象代理的就是这两个method1
方法。
比如:
1 | // o 表示当前代理对象, target表示被代理对象 |
所以在执行methodProxy.invokeSuper()
方法时,就会去执行CGLIB$method1$0()
方法。
总结一下cglib的大概工作原理是:cglib会根据所设置的Superclass,生成代理类作为其子类,并且会重写Superclass中的方法,Superclass中的某一个方法,比如method1(),相应的在代理类中会对应两个方法,一个是重写的method1(),用来执行增强逻辑,一个是CGLIB$method1$0(),会直接调用super.test(),是让MethodProxy对象来用的。
4. 流程总结
- 首先生成代理对象。创建增强类
Enhancer
,设置代理类的父类,设置回调拦截方法,返回创建的代理对象;
- 调用代理类中的方法。这里调用的代理类中的方法实际上是重写的父类的拦截。重写的方法中会去调用
intercept
方法;
- 调用
intercept
,方法中会对调用代理方法中的invokeSuper
方法。而在invokeSuper
中维护了一个FastClassInfo类
,其包含四个属性字段:- FastClass f1(目标类)、
- FastClass f2 (代理类)、
- int i1(目标类要执行方法的下标)、
- int i2(代理类要执行方法的下标);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
private FastClassInfo() {
}
}
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
invokeSuper
中会调用的为代理类中的对应方法(代理类继承于父类的时候,对于其父类的方法,自己会生成两个方法,一个是重写的方法,一个是代理生成的方法,这里调用的即是代理生成的方法);
- 调用代理类中的代理方法。代理方法中通过
super.xxxx(string)
来实际真正的调用要执行的方法;
5. JDK与cglib的区别
JDK动态代理
1 | public static Object getProxy(Object proxyObj) { |
cglib动态代理
1 | private static Enhancer enhancer = new Enhancer(); |
个人理解两者的区别在于:JDK动态代理是基于委托思想,而CGLib动态代理是基于继承的思想!!!
- 基于委托思想,JDK生成动态代理类的时候,需要传入被代理类(被委托类)的对象,可以看作是对象级别的重用机制
- 基于继承思想,动态代理类继承了被代理类,理论上父类的所有开放方法对于子类都是可见的,可以看作是类级别的重用机制;
动态代理 = 拦截器机制 + 回溯到被代理类的能力
对于JDK动态代理:
JDK动态代理 = 拦截器机制(InvocationHandler) + 回溯到被代理类的能力(反射调用被代理类对象相关方法)
在JDK动态代理中,生成的代理类的全限定类名是com.sun.proxy.$ProxyN(N是正整数,比如$Proxy0),它继承了com.sun.proxy类,该类中存在一个InvocationHandler类型的h成员变量,它就是拦截器。但这里会存在一个问题,由于我们希望代理类和被代理类在行为上是一致的(具有相同的类型),所以JDK动态代理需要引入接口的概念,代理对象和被代理对象需要具有相同的接口定义。
所以,在我们使用JDK动态代理的过程中,我们需要自定义拦截器,实现InvocationHandler 接口,然后将被代理对象(被委托对象)注入到拦截器中。当调用接口方法时,会首先调用拦截器的invoke方法,拦截器invoke方法内部,会经过反射去调用被代理对象的相应方法。
1
2
3
4
5
6
7
8
9
10
11
12public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行目标方法对象
Object result = method.invoke(target, args);
return result;
}
}对于CGLIB动态代理:
CGLIB动态代理 = 拦截器机制(MethodInterceptor) + 回溯到被代理类的能力 (FastClass辅助类、MethodProxy类)
在CGLIB动态代理中,生成的代理类的全限定类名是很自由的。因为它是基于继承机制,代理类继承了被代理类。 在生成的代理类中,会存在一个MethodInterceptor类型的CGLIB$CALLBACK_0成员变量,它就是拦截器。由于是继承,代理类天然就可以调用到父类(被代理类)的方法,因此这里不再需要注入被代理类的对象实例了。但这里仍然存在一个很核心的问题:代理类看起来,既要能够调用到拦截器,又要可以回溯到父类(被代理类)的原始方法,这看起来很矛盾。怎么解决呢?
其实很简单,CGLIB生成的代理,对于被代理类的原有方法(比如上面的method1方法),会调用到拦截器。而与此同时,CGLIB还增加了隐藏的能够回溯到原始方法的传送门方法(比如CGLIB$method1$0)
1
2
3
4
5
6
7
8
9public class CglibProxy implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//执行目标方法对象
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
}
两者区别
JDK动态代理
基于接口实现,必须先定义接口;CGLib动态代理
基于被代理类实现,可以直接定义类或者实现接口的类;
JDK动态代理
需要实现InvocationHanlder接口,加上反射机制实现代理类CGLib动态代理
需要实现MethodInterceptor接口,对于代理类不可使用final修饰
JDK动态代理
是委托机制,委托hanlder,生成新的委托类,调用实现类方法;CGLib动态代理
则使用继承机制,被代理类和代理类是继承关系,直接调用其中方法;