CGLIB是一个功能强大的高性能代码生成库。
它被广泛应用于许多AOP的框架使用,例如Spring AOP和dynaop。
Hibernate使用CGLIB来代理单端single-ended(多对一和一对一)关联。
CGLIB底层是基于ASM
实现的。
通过maven引入依赖,来使用cglib:
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
cglib的包结构如下:
- net.sf.cglib.core :底层字节码处理类,他们中的大多数都与ASM有关。
- net.sf.cglib.transform:用于class文件运行时转换或编译时转换
- net.sf.cglib.proxy:代理的创建和方法拦截相关。
- net.sf.cglib.reflect:该包中的类用于快速反射,并提供了C#风格的委托。
- net.sf.cglib.util:集合排序工具类
- net.sf.cglib.beans:javabean相关工具
Proxy APIs
API
cglib可以为没有implement interfaces
的non-final类
生成代理类。它比JDK动态代理方法更快。
net.sf.cglib.proxy.Callback Interface
是一个标记接口。- 所有被
net.sf.cglib.proxy.Enhancer class
调用的类都要事先CallBack
接口。 net.sf.cglib.proxy.MethodInterceptor
是所有CallBack
中最通用的回调类型。
public interface MethodInterceptor extends Callback {
/**
*
* @param proxyObject 代理对象
* @param method 被代理方法
* @param objects 参数
* @param methodProxy 代理后方法
* @return
* @throws Throwable
*/
Object Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable ;
}
net.sf.cglib.proxy.MethodInterceptor能够满足任何的拦截需要。但对有些情况下overkill(杀鸡焉用宰牛刀)
了。为了简化和提高性能,CGLIB包提供了一些专门的回调(callback)类型:
net.sf.cglib.proxy.FixedValue
:为了提升性能,强制特定方法返回固定值net.sf.cglib.proxy.NoOp
:nothing,直接调用父类方法。net.sf.cglib.proxy.LazyLoader
:当实际的对象需要延迟装载时,可以使用LazyLoader回调。在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会被初始化。在第一次访问之时会初始化,然后之后的操作都不会触发。
net.sf.cglib.proxy.Dispatcher
:Dispathcer回调和LazyLoader回调有相同的特点,不同的是,当代理方法被调用时,Dispatcher .loadObject()
也总要被调用.
public interface Dispatcher extends Callback {
Object loadObject() throws Exception;
}
net.sf.cglib.proxy.ProxyRefDispatcher
:类似Dispatcher
,不同的是它可以把代理对象作为装载对象方法的一个参数传递。
public interface ProxyRefDispatcher extends Callback {
Object loadObject(Object o) throws Exception;
}
例
为User.eat()方法添加记录时间的功能
public class User {
public void eat(String food){
System.out.printf("i am eat %s now.....\n",food);
}
}
public class TimeMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object proxyObject, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("loging start...");
//这里不能使用 method.invoke(),会出现死循环
Object result = methodProxy.invokeSuper(proxyObject,args);
System.out.printf("%s() cost :%d ms\n",method.getName(),(System.currentTimeMillis() - start));
return result;
}
}
测试用例:
@Test
public void test02(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(User.class);//设置代理类,只能是non-final方法
enhancer.setCallback(new TimeMethodInterceptor());
User proxy = (User) enhancer.create();
//只能是non-final方法,否则代理将失效,直接调用原逻辑
proxy.eat("banana");
//打印代理类名称
System.out.println(proxy.getClass().getName());
}
执行结果:
loging start...
i am eat banana now.....
eat() cost :38 ms
cn.jhs.cglib.User$$EnhancerByCGLIB$$cbe988a
源码解析
如何查看cglib动态代理产生的类呢?我们可以在生成代理类之前增加一个系统变量,即可在工程根目录/proxy_out获得产生的代理类.
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "proxy_out");
1.
查看代理类User$$EnhancerByCGLIB$$cbe988a
:
public class User$$EnhancerByCGLIB$$cbe988a extends User implements Factory {
final void CGLIB$eat$0(String var1) {
super.eat(var1);
}
public final void eat(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if(this.CGLIB$CALLBACK_0 == null) {
/*
* 1.绑定callback
*
* var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
*/
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if(var10000 != null) {
/**
* 2.直接调用MethodInterceptor的interceptor方法
*
* CGLIB$eat$0$Method = ReflectUtils.findMethods(new String[]{"eat", "(Ljava/lang/String;)V"}, (var1 = Class.forName("cn.jhs.cglib.User")).getDeclaredMethods())
* CGLIB$eat$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "eat", "CGLIB$eat$0");
*/
var10000.intercept(this, CGLIB$eat$0$Method, new Object[]{var1}, CGLIB$eat$0$Proxy);
} else {
super.eat(var1);
}
}
}
2.
由eat(String var1)方法可知,最终调用TimeMethodInterceptor.intercept()
,然后调用方法:
//这里不能使用 method.invoke(),会出现死循环
Object result = methodProxy.invokeSuper(proxyObject,args);
3.
查看MethodProxy.invokeSuper()
方法:
4.
查看User$$EnhancerByCGLIB$$cbe988a$$FastClassByCGLIB$$d80fe27c.invoke()
,从上图可知参数为(18,User
public class User$$EnhancerByCGLIB$$cbe988a$$FastClassByCGLIB$$d80fe27c extends FastClass {
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
cbe988a var10000 = (cbe988a)var2;
int var10001 = var1;
try {
switch(var10001) {
//...........省略
case 18:
//var1=18,执行此行逻辑
var10000.CGLIB$eat$0((String)var3[0]);
return null;
case 19:
return cbe988a.CGLIB$findMethodProxy((Signature)var3[0]);
//...........省略
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
}
CallbackFilter
net.sf.cglib.proxy.CallbackFilter
可以有选择的对一些方法使用回调。当Enhancer.setCallbacks(Callback[])
设置多个callback时,必须设置CallbackFilter
来确保一个代理类只能接受一个拦截。
如果未设置,会报错:java.lang.IllegalStateException: Multiple callback types possible but no filter specified
@Test
public void test03(){
MethodInterceptor timeMethodInterceptor = new TimeMethodInterceptor();
MethodInterceptor interceptor2 = (proxyObject, method, args,methodProxy) ->{
System.out.println("interceptor2");
return methodProxy.invokeSuper(proxyObject,args);
};
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(User.class);//设置代理类,只能是non-final方法
//设置多个Callback
enhancer.setCallbacks(new Callback[]{timeMethodInterceptor,interceptor2});
//设置CallbackFilter
enhancer.setCallbackFilter((method) ->{
String methodName = method.getName();
if ("eat".equals(methodName)) {
return 1; // eat()方法使用callbacks[1]对象拦截。
}
return 0; //其他方法使用callbacks[0]对象拦截。
});
User proxy = (User) enhancer.create();
//只能是non-final方法,否则代理将失效,直接调用原逻辑
proxy.eat("banana");
}
Mixin
Minix将多个对象&接口绑定到一个单独的对象上,变相的实现了java的多继承,其实依旧是implements 接口s
。
public abstract class Mixin {
/**
* delegates必须都implements Interfaces;
**/
public static Mixin create(Object[] delegates) {
Mixin.Generator gen = new Mixin.Generator();
gen.setDelegates(delegates);
return gen.create();
}
/**
* delegates[0]实现的接口必须包含interfaces[0]
* delegates[....]实现的接口必须包含interfaces[....]
* delegates[n]实现的接口必须包含interfaces[n]
*/
public static Mixin create(Class[] interfaces, Object[] delegates) {
Mixin.Generator gen = new Mixin.Generator();
gen.setClasses(interfaces);
gen.setDelegates(delegates);
return gen.create();
}
}
Beans APIs
BeanCopier
cglib提供的能够从一个bean复制到另一个bean中通过getter,setter复制,必须提供getter,setter方法。
public abstract class BeanCopier {
/**
* source : 源头,即提供copy信息的class
* target : 目标,即需要cpoy的class
* useConverter 是否强转换标识,如果强制转换需要在copy()时加入Converter参数
*/
public static BeanCopier create(Class source, Class target, boolean useConverter) {
BeanCopier.Generator gen = new BeanCopier.Generator();
gen.setSource(source);
gen.setTarget(target);
gen.setUseConverter(useConverter);
return gen.create();
}
public abstract void copy(Object var1, Object var2, Converter var3);
//BeanCopier 默认实现的子类
public static class Generator extends AbstractClassGenerator {
}
}
例,假设有U1,U2,现将U1实例source的属性copy给U2对象target
class U1{
private int age;
private String name
//getter,setter
}
class U2{
private Date age;
private String name;
private double sale;
//getter,setter
}
测试
:
@Test
public void test05_BeanCopier() {
//构建BeanCopier,并copy对象的属性值。
Class sourceClazz = U1.class;
Class targetClazz = U2.class;
BeanCopier copier = BeanCopier.create(sourceClazz, targetClazz, false);
U1 source = new U1(1,null);
U2 target = new U2(new Date(),"u2",999.99);
copier.copy(source, target, null);
System.out.println(source);
System.out.println(target);
}
执行结果
,age由于类型不同没有覆盖
:
U1{age=1, name='null'}
U2{age=Thu Jul 26 22:33:06 CST 2018, name='null', sale=999.99}
BeanGenerator
@Test
public void test05_BeanGenerator(){
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "proxy_out");
BeanGenerator generator = new BeanGenerator();
generator.addProperty("age", int.class);
generator.addProperty("name", String.class);
//设置父类为java.util.Vector
generator.setSuperclass(java.util.Vector.class);
Object obj = generator.create();
System.out.println(obj.getClass().getName());
}
生成/package为 $java.util下的$java.util.Vector$$BeanGeneratorByCGLIB$$5a78c5e0
,如下:
package $java.util;
import java.util.Vector;
public class Vector$$BeanGeneratorByCGLIB$$5a78c5e0 extends Vector {
private String $cglib_prop_name;
private int $cglib_prop_age;
public Vector$$BeanGeneratorByCGLIB$$5a78c5e0() {
}
public String getName() {
return this.$cglib_prop_name;
}
public void setName(String var1) {
this.$cglib_prop_name = var1;
}
public int getAge() {
return this.$cglib_prop_age;
}
public void setAge(int var1) {
this.$cglib_prop_age = var1;
}
BeanMap
//实现了java.util.Map接口
public abstract class BeanMap implements Map {
public static BeanMap create(Object bean) {
BeanMap.Generator gen = new BeanMap.Generator();
gen.setBean(bean);
return gen.create();
}
}
BeanMap类实现了Java Map,将一个bean对象中的所有属性转换为一个String-to-Obejct的Java Map.
这里介绍一个关于Cglib的工具类,以后直接使用
CglibBean bean = new CglibBean(new HashMap());调用即可。
public class CglibBean {
/**
* 实体Object
*/
public Object object = null;
/**
* 属性map
*/
public BeanMap beanMap = null;
public CglibBean() {
super();
}
@SuppressWarnings("unchecked")
public CglibBean(Map propertyMap) {
this.object = generateBean(propertyMap);
this.beanMap = BeanMap.create(this.object);
}
/**
* 给bean属性赋值
* @param property 属性名
* @param value 值
*/
public void setValue(String property, Object value) {
beanMap.put(property, value);
}
/**
* 通过属性名得到属性值
* @param property 属性名
* @return 值
*/
public Object getValue(String property) {
return beanMap.get(property);
}
/**
* 得到该实体bean对象
* @return
*/
public Object getObject() {
return this.object;
}
@SuppressWarnings("unchecked")
private Object generateBean(Map propertyMap) {
BeanGenerator generator = new BeanGenerator();
Set keySet = propertyMap.keySet();
for (Iterator i = keySet.iterator(); i.hasNext();) {
String key = (String) i.next();
generator.addProperty(key, (Class) propertyMap.get(key));
}
// generator.setSuperclass(java.util.Vector.class);
return generator.create();
}
}
BulkBean
BulkBean类似于BeanCopier
,它将BeanCopier.copy()
分解为两个动作:getPropertyValues和setPropertyValues()
public abstract class BulkBean {
public static BulkBean create(Class target, String[] getters, String[] setters, Class[] types) {
BulkBean.Generator gen = new BulkBean.Generator();
gen.setTarget(target);
gen.setGetters(getters);
gen.setSetters(setters);
gen.setTypes(types);
return gen.create();
}
public abstract void setPropertyValues(Object var1, Object[] var2);
public Object[] getPropertyValues(Object bean) {
Object[] values = new Object[this.getters.length];
this.getPropertyValues(bean, values);
return values;
}
}
例:
public void test06_BulkBean(){
Class target = U1.class;
String[] getters = new String[]{"getAge","getName"};
String[] setters = new String[]{"setAge","setName"};
Class[] types = new Class[]{int.class, String.class};
BulkBean bulkBean = BulkBean.create(U1.class, getters, setters, types);
U1 bean = new U1(1,"trump");
Object[] propertyValues = bulkBean.getPropertyValues(bean);
System.out.println(Arrays.asList(propertyValues));
bulkBean.setPropertyValues(bean,new Object[]{22,null}); //[1, trump]
System.out.println(Arrays.asList(bulkBean.getPropertyValues(bean))); //[22, null]
}