- 概 述
- 运行期扩展Java类与实现Java接口
- 简 称
- cglib
- 解 释
- 一个开源项目
- 外文名
- Code Generation Library
-
cglib
2018-06-15 08:00:55CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟...CGLIB
CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB作为一个开源项目,地址为:https://github.com/cglib/cglib
主要使用方式:
Enhancer:允许代理扩展一个具体的基类,还可以实现实现接口,动态生成的子类覆盖超类的非final方法(final不能被继承,重写),并具有回调用户定义的拦截器的钩子(callback方法)
最常用的callback是MethodInterceptor,实现AOP方法执行前后和抛出异常时执行(Around advice)–在调用超类方法之前之后都可以调用调用自定义代码。另外在调用超类的方法之前可以修改参数,或根本不调用它。Enhancer
public class SampleClass2 { public String test(String input) { return "Hello world!"; } @Test public void testFixedValue() throws Exception { Enhancer enhancer = new Enhancer(); // 设置要代理的目标类 enhancer.setSuperclass(SampleClass2.class); // 设置要代理的拦截器 enhancer.setCallback(new FixedValue() { @Override public Object loadObject() throws Exception { return "Hello cglib!"; } }); // 生成代理类的实例 SampleClass2 proxy = (SampleClass2) enhancer.create(); assertEquals("Hello cglib!", proxy.test(null)); } }
KeyFactory:主要处理Maps 和 Sets集合,适用于多键值对
public class KeySample { //实现一个newInstance的接口,返回值是Object(必须) private interface MyFactory { public Object newInstance(int a, char[] b, String d); } public static void main(String[] args) { MyFactory f = (MyFactory)KeyFactory.create(MyFactory.class); Object key1 = f.newInstance(20, new char[]{ 'a', 'b' }, "hello"); Object key2 = f.newInstance(20, new char[]{ 'a', 'b' }, "hello"); Object key3 = f.newInstance(20, new char[]{ 'a', '_' }, "hello"); System.out.println(key1.equals(key2)); System.out.println(key2.equals(key3)); } }
-
CGlib
2018-05-02 11:54:47CGLIB和Java动态代理的区别 1.Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机 制不允许多重继承) CGLIB能够代理普通类 2.Java动态代理使用J...转载:https://blog.csdn.net/danchu/article/details/70238002
CGLIB和Java动态代理的区别
1.Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机 制不允许多重继承)
CGLIB能够代理普通类2.Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;
CGLIB使用ASM框架直接对字节码进行操作,在 类的执行过程中比较高效 -
CGLIB
2017-11-29 17:01:10CGLIB原理:动态生成一个要代理类的子类,子类重写代理类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入模切逻辑。它比使用java反射的JDK动态代理要快。 CGLIB底层:使用字节...原理
CGLIB原理:动态生成一个要代理类的子类,子类重写代理类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入模切逻辑。它比使用java反射的JDK动态代理要快。
CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因数它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
CGLIB的API
1.jar包
cglib-nodep.jar:不需要关联asm的jar包,jar包内部包含了asm的类。
cglib.jar:需要关联asm的jar包。
2.CGLIB类库
由于基本代码很少,学起来有一定的困难,主要是缺少文档和示例,这也是CGLIB的一个不足之处。
net.sf.cglib.core:底层字节码处理类,他们大部分与ASM有关系 。
net.sf.cglib.transform:编译期或运行期类和类文件的转换。
net.sf.cglib.proxy:实现创建代理和方法拦截器类。
net.sf.cglib.reflect:实现快速反射和C#风格代理的类
net.sf.cglib.util:集合排序等工具类
net.sf.cglibbeans:JavaBean相关的工具类。
下面介绍使用MethodInterceptor和Enhance实现一个动态代理。
一 JDK中的动态代理
JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。
二 使用CGLIB实现
(1)被代理类
package com.zghw.cglib; /** * 没有实现接口,需要CGlib动态代理的目标类 * * @author zghw * */ public class TargetObject { public String method1(String paramName) { return paramName; } public int method2(int count) { return count; } public int method3(int count) { return count; } @Override public String toString() { return "TargetObject []"+ getClass(); } }
(2)拦截器在调用目标方法时,CGLIB会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。
package com.zghw.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * 目标对象拦截器,实现MethodInterceptor * @author zghw * */ public class TargetInterceptor 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("调用前"); Object result = proxy.invokeSuper(obj, params); System.out.println(" 调用后"+result); return result; } }
(3)生成动态代理类
package com.zghw.cglib; 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 TestCglib { public static void main(String args[]) { Enhancer enhancer =new Enhancer(); enhancer.setSuperclass(TargetObject.class); enhancer.setCallback(new TargetInterceptor()); TargetObject targetObject2=(TargetObject)enhancer.create(); System.out.println(targetObject2); System.out.println(targetObject2.method1("mmm1")); System.out.println(targetObject2.method2(100)); System.out.println(targetObject2.method3(200)); } }
这里Enhance类是CGLib中的一个这节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。首先将被代理类TargetObject设置成父类,然后设置拦截器TargetInterceptor,最后执行enhance.create()动态生成一个代理类,并从Object强制转型成父类型 TargetObject。最后,在代理类上调用方法。
(4)回调过滤器
在CGLIB回调时可以设置对不同方法执行不同的回调逻辑,或者根本不执行回调。
package com.zghw.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.CallbackFilter; /** * 回调方法过滤 * @author zghw * */ 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; } }
package com.zghw.cglib; 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 TestCglib { public static void main(String args[]) { Enhancer enhancer =new Enhancer(); enhancer.setSuperclass(TargetObject.class); CallbackFilter callbackFilter = new TargetMethodCallbackFilter(); /** * (1)callback1:方法拦截器 (2)NoOp.INSTANCE:这个NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截。 (3)FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。 */ Callback noopCb=NoOp.INSTANCE; Callback callback1=new TargetInterceptor(); Callback fixedValue=new TargetResultFixed(); Callback[] cbarray=new Callback[]{callback1,noopCb,fixedValue}; //enhancer.setCallback(new TargetInterceptor()); enhancer.setCallbacks(cbarray); enhancer.setCallbackFilter(callbackFilter); TargetObject targetObject2=(TargetObject)enhancer.create(); System.out.println(targetObject2); System.out.println(targetObject2.method1("mmm1")); System.out.println(targetObject2.method2(100)); System.out.println(targetObject2.method3(100)); System.out.println(targetObject2.method3(200)); } }
package com.zghw.cglib; import net.sf.cglib.proxy.FixedValue; /** * 表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。 * @author zghw * */ public class TargetResultFixed implements FixedValue{ /** * 该类实现FixedValue接口,同时锁定回调值为999 * (整型,CallbackFilter中定义的使用FixedValue型回调的方法为getConcreteMethodFixedValue,该方法返回值为整型)。 */ @Override public Object loadObject() throws Exception { System.out.println("锁定结果"); Object obj = 999; return obj; } }
(5)延迟加载对象LazyLoader接口继承了Callback,因此也算是CGLib中的一种Callback类型。另一种延迟加载接Dispatcher,Dispatcher同样继承于Callback,也是一种回调类型。不同的是:LazyLoader只在第一次访问延迟加载属性里触发代理类回调方法,而Dispatcher在每次访问加载属性时都会触发代理类回调方法。
package com.zghw.cglib; import 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) { System.out.println("lazy bean init"); this.name = name; this.age = age; this.propertyBean = createPropertyBean(); this.propertyBeanDispatcher = createPropertyBeanDispatcher(); } /** * 只第一次懒加载 * @return */ private PropertyBean createPropertyBean() { /** * 使用cglib进行懒加载 对需要延迟加载的对象添加代理,在获取该对象属性时先通过代理类回调方法进行对象初始化。 * 在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会被初始化了(在CGLib的实现中只要去访问该对象内属性的getter方法, * 就会自动触发代理类回调)。 */ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PropertyBean.class); PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class, new ConcreteClassLazyLoader()); return pb; } /** * 每次都懒加载 * @return */ private PropertyBean createPropertyBeanDispatcher() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PropertyBean.class); PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class, new ConcreteClassDispatcher()); return pb; } 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; } @Override public String toString() { return "LazyBean [name=" + name + ", age=" + age + ", propertyBean=" + propertyBean + "]"; } }
package com.zghw.cglib; public 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 void setValue(Object value) { this.value = value; } @Override public String toString() { return "PropertyBean [key=" + key + ", value=" + value + "]" +getClass(); } }
package com.zghw.cglib; import net.sf.cglib.proxy.LazyLoader; public class ConcreteClassLazyLoader implements LazyLoader { /** * 对需要延迟加载的对象添加代理,在获取该对象属性时先通过代理类回调方法进行对象初始化。 * 在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会被初始化了(在CGLib的实现中只要去访问该对象内属性的getter方法, * 就会自动触发代理类回调)。 */ @Override public Object loadObject() throws Exception { System.out.println("before lazyLoader..."); PropertyBean propertyBean = new PropertyBean(); propertyBean.setKey("zghw"); propertyBean.setValue(new TargetObject()); System.out.println("after lazyLoader..."); return propertyBean; } }
package com.zghw.cglib; import net.sf.cglib.proxy.Dispatcher; public class ConcreteClassDispatcher implements Dispatcher{ @Override public Object loadObject() throws Exception { System.out.println("before Dispatcher..."); PropertyBean propertyBean = new PropertyBean(); propertyBean.setKey("xxx"); propertyBean.setValue(new TargetObject()); System.out.println("after Dispatcher..."); return propertyBean; } }
(6)接口生成器InterfaceMaker
会动态生成一个接口,该接口包含指定类定义的所有方法。
package com.zghw.cglib; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.InterfaceMaker; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class TestInterfaceMaker { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 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(){ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if(method.getName().equals("method1")){ System.out.println("filter method1 "); return "mmmmmmmmm"; } if(method.getName().equals("method2")){ System.out.println("filter method2 "); return 1111111; } if(method.getName().equals("method3")){ System.out.println("filter method3 "); return 3333; } return "default"; }}); Method targetMethod1=object.getClass().getMethod("method3",new Class[]{int.class}); int i=(int)targetMethod1.invoke(object, new Object[]{33}); Method targetMethod=object.getClass().getMethod("method1",new Class[]{String.class}); System.out.println(targetMethod.invoke(object, new Object[]{"sdfs"})); } }
最后欢迎大家访问我的个人网站:1024s
-
Java两种动态代理JDK动态代理和CGLIB动态代理
2018-08-07 15:33:35cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现...目录
代理模式
代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。
使用代理好处还不止这些,一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳定,经常变更协议,就可以使用一个代理,接口变更时,只需要修改代理,不需要一一修改业务代码。从这个意义上说,所有调外界的接口,我们都可以这么做,不让外界的代码对我们的代码有侵入,这叫防御式编程。代理其他的应用可能还有很多。
上述例子中,类A写死持有B,就是B的静态代理。如果A代理的对象是不确定的,就是动态代理。动态代理目前有两种常见的实现,jdk动态代理和cglib动态代理。
JDK动态代理
jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方。先看下jdk动态代理的使用代码,再理解原理。
首先有个“明星”接口类,有唱、跳两个功能:
package proxy; public interface Star { String sing(String name); String dance(String name); }
再有个明星实现类“刘德华”:
package proxy; public class LiuDeHua implements Star { @Override public String sing(String name) { System.out.println("给我一杯忘情水"); return "唱完" ; } @Override public String dance(String name) { System.out.println("开心的马骝"); return "跳完" ; } }
明星演出前需要有人收钱,由于要准备演出,自己不做这个工作,一般交给一个经纪人。便于理解,它的名字以Proxy结尾,但他不是代理类,原因是它没有实现我们的明星接口,无法对外服务,它仅仅是一个wrapper。
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class StarProxy implements InvocationHandler { // 目标类,也就是被代理对象 private Object target; public void setTarget(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里可以做增强 System.out.println("收钱"); Object result = method.invoke(target, args); return result; } // 生成代理类 public Object CreatProxyedObj() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } }
上述例子中,方法CreatProxyedObj返回的对象才是我们的代理类,它需要三个参数,前两个参数的意思是在同一个classloader下通过接口创建出一个对象,该对象需要一个属性,也就是第三个参数,它是一个InvocationHandler。需要注意的是这个CreatProxyedObj方法不一定非得在我们的StarProxy类中,往往放在一个工厂类中。上述代理的代码使用过程一般如下:
1、new一个目标对象
2、new一个InvocationHandler,将目标对象set进去
3、通过CreatProxyedObj创建代理对象,强转为目标对象的接口类型即可使用,实际上生成的代理对象实现了目标接口。
Star ldh = new LiuDeHua(); StarProxy proxy = new StarProxy(); proxy.setTarget(ldh); Object obj = proxy.CreatProxyedObj(); Star star = (Star)obj;
Proxy(jdk类库提供)根据B的接口生成一个实现类,我们成为C,它就是动态代理类(该类型是 $Proxy+数字 的“新的类型”)。生成过程是:由于拿到了接口,便可以获知接口的所有信息(主要是方法的定义),也就能声明一个新的类型去实现该接口的所有方法,这些方法显然都是“虚”的,它调用另一个对象的方法。当然这个被调用的对象不能是对象B,如果是对象B,我们就没法增强了,等于饶了一圈又回来了。
所以它调用的是B的包装类,这个包装类需要我们来实现,但是jdk给出了约束,它必须实现InvocationHandler,上述例子中就是StarProxy, 这个接口里面有个方法,它是所有Target的所有方法的调用入口(invoke),调用之前我们可以加自己的代码增强。
看下我们的实现,我们在InvocationHandler里调用了对象B(target)的方法,调用之前增强了B的方法。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里增强 System.out.println("收钱"); Object result = method.invoke(target, args); return result; }
所以可以这么认为C代理了InvocationHandler,InvocationHandler代理了我们的类B,两级代理。
整个JDK动态代理的秘密也就这些,简单一句话,动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。
下面看下动态代理类到底如何调用的InvocationHandler的,为什么InvocationHandler的一个invoke方法能为分发target的所有方法。C中的部分代码示例如下,通过反编译生成后的代码查看,摘自链接地址。Proxy创造的C是自己(Proxy)的子类,且实现了B的接口,一般都是这么修饰的:
public final class XXX extends Proxy implements XXX
一个方法代码如下:
public final String SayHello(String paramString) { try { return (String)this.h.invoke(this, m4, new Object[] { paramString }); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); }
可以看到,C中的方法全部通过调用h实现,其中h就是InvocationHandler,是我们在生成C时传递的第三个参数。这里还有个关键就是SayHello方法(业务方法)跟调用invoke方法时传递的参数m4一定要是一一对应的,但是这些对我们来说都是透明的,由Proxy在newProxyInstance时保证的。留心看到C在invoke时把自己this传递了过去,InvocationHandler的invoke的第一个方法也就是我们的动态代理实例类,业务上有需要就可以使用它。(所以千万不要在invoke方法里把请求分发给第一个参数,否则很明显就死循环了)
C类中有B中所有方法的成员变量
private static Method m1; private static Method m3; private static Method m4; private static Method m2; private static Method m0;
这些变量在static静态代码块初始化,这些变量是在调用invocationhander时必要的入参,也让我们依稀看到Proxy在生成C时留下的痕迹。
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]); m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } }
从以上分析来看,要想彻底理解一个东西,再多的理论不如看源码,底层的原理非常重要。
jdk动态代理类图如下
cglib动态代理
我们了解到,“代理”的目的是构造一个和被代理的对象有同样行为的对象,一个对象的行为是在类中定义的,对象只是类的实例。所以构造代理,不一定非得通过持有、包装对象这一种方式。
通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。根据里氏代换原则(LSP),父类需要出现的地方,子类可以出现,所以cglib实现的代理也是可以被正常使用的。
先看下代码
package proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor { // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中 public Object CreatProxyedObj(Class<?> clazz) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { // 这里增强 System.out.println("收钱"); return arg3.invokeSuper(arg0, arg2); } }
从代码可以看出,它和jdk动态代理有所不同,对外表现上看CreatProxyedObj,它只需要一个类型clazz就可以产生一个代理对象, 所以说是“类的代理”,且创造的对象通过打印类型发现也是一个新的类型。不同于jdk动态代理,jdk动态代理要求对象必须实现接口(三个参数的第二个参数),cglib对此没有要求。
cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。
这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。
C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。
这里有个疑问就是intercept的四个参数,为什么我们使用的是arg3而不是arg1?
@Override public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { System.out.println("收钱"); return arg3.invokeSuper(arg0, arg2); }
因为如果我们通过反射 arg1.invoke(arg0, ...)这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, ...)很明显会死循环。
所以调用的是cglib开头的方法,但是,我们使用arg3也不是简单的invoke,而是用的invokeSuper方法,这是因为cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。
fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调用。
对比JDK动态代理,cglib依然需要一个第三者分发请求,只不过jdk动态代理分发给了目标对象,cglib最终分发给了自己,通过给method编号完成调用。cglib是继承的极致发挥,本身还是很简单的,只是fastclass需要另行理解。
参考
https://blog.csdn.net/jiankunking/article/details/52143504
http://www.php.cn/java-article-407212.html
https://www.cnblogs.com/chinajava/p/5880887.html
https://rejoy.iteye.com/blog/1627405
测试
public static void main(String[] args) { int times = 1000000; Star ldh = new LiuDeHua(); StarProxy proxy = new StarProxy(); proxy.setTarget(ldh); long time1 = System.currentTimeMillis(); Star star = (Star)proxy.CreatProxyedObj(); long time2 = System.currentTimeMillis(); System.out.println("jdk创建时间:" + (time2 - time1)); CglibProxy proxy2 = new CglibProxy(); long time5 = System.currentTimeMillis(); Star star2 = (Star)proxy2.CreatProxyedObj(LiuDeHua.class); long time6 = System.currentTimeMillis(); System.out.println("cglib创建时间:" + (time6 - time5)); long time3 = System.currentTimeMillis(); for (int i = 1; i <= times; i++) { star.sing("ss"); star.dance("ss"); } long time4 = System.currentTimeMillis(); System.out.println("jdk执行时间" + (time4 - time3)); long time7 = System.currentTimeMillis(); for (int i = 1; i <= times; i++) { star2.sing("ss"); star2.dance("ss"); } long time8 = System.currentTimeMillis(); System.out.println("cglib执行时间" + (time8 - time7)); }
经测试,jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。
-
CGLIB简介 CGLIB简介 CGLIB简介 CGLIB简介
2011-04-16 12:27:53CGLIB简介CGLIB简介CGLIB简介CGLIB简介CGLIB简介 -
cglib的依赖包
2017-02-23 14:11:14cglib -
java CGlib
2020-12-26 17:28:05asm-2.2.3.jar asm-commons-2.2.3.jar asm-util-2.2.3.jar cglib-nodep-2.1_3.jar ... -
Cglib的jar文件 Cglib.zip
2020-07-16 16:13:37Cglib的jar文件,包含:asm.jar、asm-commons.jar、asm-tree.jar、cglib-2.2.jar 四个jar包 -
Spring的两种代理JDK和CGLIB的区别浅谈
2016-08-04 17:39:49我的店铺 一、原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,...1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP... -
使用 cglib_CGlib
2021-01-13 16:34:17CGLib(Code Generation Library)是一个强大的、高性能、高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。CGLib比Java的java.lang.reflect.Proxy 类更强大的地方在于它不仅可以接管接口类的方法,... -
java通过cglib动态生成实体bean
2021-01-08 15:50:49import net.sf.cglib.beans.BeanGenerator; import org.apache.commons.collections.map.MultiValueMap; import java.lang.reflect.*; import java.util.ArrayList; import java.util.HashMap; import java.util.It -
cglib3.2.5
2018-06-19 10:38:19spring源码需要cglib3.2.5,欢迎下载使用。确认好版本再下载 -
cglib3.2.10
2018-12-20 22:19:44cglib的最新版本3.2.10,稳定的字节码生成工具,通过继承方式动态生成代理类的工具,很多应用在拦截、切片中。 -
【Spring基础】CGLIB动态代理实现原理
2018-06-09 18:11:19一 CGLIB介绍 CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库, 它可以在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO(Persistent Object 持久化对象)...