1 .CGLIB动态代理
在《六》中,我们分析了JDK提供的动态代理的缺陷,并且提出了一种解决思路,并简单地对其进行了实现。其实《六》中的简单实现其实是对一种叫cglib动态代理的简单模拟。
什么是从CGLIB?
CGLIB(CodeGeneration Library代码生产库)是一个开源项目,是一个强大的,高性能,高质量的代码生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO(Persistant Object 持久化对象)字节码的动态生成。
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
上图显示了和CGLIB包和一些框架和语言的关系图。一些框架如Spring AOP和 Hibernate,它们为了满足需要经常同时使用JDK的动态代理和CGLIB包。Hiberater使用JDK的动态代理实现一个专门为WebShere应用服务器的事务管理适配器;Spring AOP,如果不强制使用CGLIB包,默认情况是使用JDK的动态代理来代理接口。
CGLIB包的基本代码很少,当学起来有一定的困难,主要是缺少文档,这也是开源软件的一个不足之处。目前CGLIB的版本(2.1.2),主要由以下部分组成:
· net.sf.cglib.core
Low-level bytecode manipulation classes; Most of them are related to ASM.
· net.sf.cglib.transform
Classes for class file transformations at runtime or build time
· net.sf.cglib.proxy
Classes for proxy creation and method interceptions
· net.sf.cglib.reflect
Classes for a faster reflection and C#-style delegates
· net.sf.cglib.util
Collection sorting utilities
· net.sf.cglib.beans
JavaBean related utilities
大多时候,仅仅为了动态地创建代理,你仅需要使用到代理包中很少的一些API。通过使用CGLIB包可以为那些没有接口的类动态地创建代理对象,当然CGLIB也可以用来代理接口。
1.1 CGLIB动态代理的实现前分析
CGLIB动态代理类的实现和《五》中介绍JDK动态代理实现非常相似,可参考《五》再阅读本文。
首先CGLIB也提供了一个处理器接口(这里成为回调接口)net.sf.cglib.proxy.MethodInterceptor(相当于JDK代理的InvocationHandler接口),它自定义了一个intercept方法(相当于JDK代理的invoke方法),用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数是代理类对象,第二个参数是委托类被调用的方法的类对象
// 第三个是该方法的参数,第四个是生成在代理类里面,除了方法名不同,其他都和被代理方法一样的(参数,方法体里面的东西)一个方法的类对象。
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodproxy) throws Throwable
然后CGLIB也提供了一个生成代理类的类net.sf.cglib.proxy.Enhancer(相当于JDK代理的java.lang.reflect.Proxy),它提供了一组静态方法来为一组接口动态地生成代理类及其对象。本文创建代理用到的Enhancer的静态方法如下:
public Object intercept(Object proxy,Method method, Object[] args, MethodProxy methodproxy) throws Throwable
//为指定类装载器、接口及调用处理器生成动态代理类实例
public static Object create(Class class ,Callback callback h){}
public static Object create(Class class,Class[] interfaces,Callback h){}
public static Object create(Classclass, Class[] interfaces, CallbackFilter filter, Callback[] hs)
这个create方法相当于JDK代理的newProxyInstance方法,该方法的参数具体含义如下:
Class class:指定一个被代理类的类对象。
Class[]interfaces:如果要代理接口,指定一组被代理类实现的所有接口的类对象。
Callback h:指定一个实现了处理器接口(这里称回调接口)的对象。
CallbackFilter filter:指定一个方法过滤器
Callback[]hs:指定一组实现了处理器接口的对象。
下图就是用CGLIB创建动态代理的UML图
1.2 如何使用 Java 动态代理:简单解释为五个步骤:
1. 通过实现 MethodInterceptor接口创建自己的处理器;
2. 通过给Enhancer类的create()方法传进被代理类的类对象、实现了MethodInterceptor接口的处理器对象,得到一个代理类对 象。
3. 其中Enhancer类通过反射机制获得代理类的构造函数,有一个参数类型是处理器接口类型。
4. Enhancer类再通过构造函数对象创建动态代理类实例,构造时处理器对象作为参数被传入。
5. 当客户端调用了代理类的方法时,该方法调用了处理器的intercept()方法并给intercept()方法传进委托类方法的类对象,intercept方法再调用委托类的真实方法。
(1)建一个reallyCglibProxy包,所有程序都放在该包下,我在Spring的包库里面找到两个包:asm.jar和cblib-2.1.3.jar,最好在Spring里面同时拷贝这两个包到项目的类库下,不要分别从网上下载,可能会冲突。
(2)建一个被代理类(RealSubject.java)。
package reallyCglibProxy;
//被代理类
public class RealSubject {
public void print() {
System.out.println("被代理的人郭襄");
}
}
(3)建一个用户自定义的处理器类LogHandler.java,需要实现处理接口,在intercept()方法里写上被代理类的方法调用前后要进行的动作。这个intercept()方法我们不用直接调用,是让将来自动生成的代理类去调用的,
把《五》的工具类LonHanderReflectTool.java复制过来,用来反射生成的代理类。
package reallyCglibProxy;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import jdkDynamicProxy.LonHanderReflectTool;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//相当于实现jdk的InvocationHandler接口
public class LogHandler implements MethodInterceptor{
private Object delegate; //被代理类的对象
//绑定被代理类的对象
public Object bind(Object delegate)throws Exception{
this.delegate=delegate;
return Enhancer.create(delegate.getClass(),this);
}
//相当于InvocationHandler接口里面的invoke()方法
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodproxy) throws Throwable {
Object result=null;
System.out.println("我是代理人郭靖,开始代理");
//method.invoke()或者methodproxy.invoke()都可以
result=method.invoke(delegate,args);
//result=methodproxy.invoke(delegate,args);
System.out.println("我是代理人郭靖,代理完毕");
//调用工具类反射jdk的Proxy生成的代理类,可参考《五》中这个工具类
LonHanderReflectTool.ReflectClass(proxy.getClass().getName());
return result;
}
}
(4)编写测试客户端(TestReallyCglibProxy.java)。
package reallyCglibProxy;
public class TestReallyCglibProxy {
public static void main(String[] args)throws Exception {
RealSubject sub1=new RealSubject();
LogHandler hander=new LogHandler();
RealSubject sub2=(RealSubject)hander.bind(sub1);
sub2.print();
}
}
输出结果:
我是代理人郭靖,开始代理
被代理的人郭襄
我是代理人郭靖,代理完毕
public class RealSubject$$EnhancerByCGLIB$$48574fb2 extends RealSubject implements Factory{
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$print$0$Method;
private static final MethodProxy CGLIB$print$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$finalize$1$Method;
private static final MethodProxy CGLIB$finalize$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;
public RealSubject$$EnhancerByCGLIB$$48574fb2(){}
public void setCallback(int args0,Callback args1){}
public void setCallbacks(Callback; args0){}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback; args0){}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback; args0){}
public Callback getCallback(int args0){}
public Callback[] getCallbacks(){}
public static MethodProxy CGLIB$findMethodProxy(Signature args0){}
static void CGLIB$STATICHOOK1(){}
final void CGLIB$print$0(){}
private static final void CGLIB$BIND_CALLBACKS(Object args0){}
final void CGLIB$finalize$1()throws Throwable{}
final boolean CGLIB$equals$2(Object args0){}
final String CGLIB$toString$3(){}
final int CGLIB$hashCode$4(){}
final Object CGLIB$clone$5()throws CloneNotSupportedException{}
protected final void finalize()throws Throwable{}
public final boolean equals(Object args0){}
public final String toString(){}
public final int hashCode(){}
protected final Object clone()throws CloneNotSupportedException{}
public Object newInstance(Callback; args0){}
public Object newInstance(Class; args0,Object; args1,Callback; args2){}
public Object newInstance(Callback args0){}
public final void print(){}
从结果可以看出,成功自动生成了代理类RealSubject$$EnhancerByCGLIB$$48574fb2,并成功实现了代理的效果,而且还代理了RealSubject类从父类继承的finalize、equals、toString、hashCode、clone
这几个方法。
1.3 CBLIB方法过滤器
如果现在有个要求,被代理类的print方法不用处理。当最简单的方式是修改方法拦截器(即intercept方法),在里面进行判断,如果是print()方法就不做任何逻辑处理,直接调用,这样子使得编写拦截器(相当于JDK代理里的处理器)逻辑变复杂,不易维护。我们可以使用CGLIB提供的方法过滤器(CallbackFilter),使得被代理类中不同的方法,根据我们的逻辑要求,调用不同的处理器,从而使用不同的拦截方法(处理器方法)。
1.3.1使用CBLIB方法过滤器
(1)修改一下被代理类(RealSubject.java),增加一个方法print2。
package reallyCglibProxy;
//被代理类
public class RealSubject {
public void print() {
System.out.println("被代理的人郭襄");
}
public void print2() {
System.out.println("我是print2方法哦");
}
}
(2)新建一个用户自定义的方法过滤器类MyProxyFilter.java。
package reallyCglibProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.NoOp;
//自定义方法过滤器类
public class MyProxyFilterimplements CallbackFilter {
public int accept(Method method) {
/**
*这句话可发现,所有被代理的方法都会被过滤一次。Enhancer的源代码中看到下面一段代码
while(it1.hasNext()){
MethodInfomethod=(MethodInfo)it1.next();
MethodactualMethod=(it2!=null)?(Method)it2.next():null;
intindex=filter.accept(actualMethod);
if(index>=callbackTypes.length){
thrownewIllegalArgumentException("Callbackfilterreturnedanindexthatistoolarge:"+index);
}
上段代码可以看到所有的被代理的方法,本例子中就是print、print2、从父类继承的finalize、equals、toString、
hashCode、clone这几个方法)都被调用accept方法进行过滤,给每个方法返回一个整数,
本例子是0或者1,从而选择不同的处理器。
*/
System.out.println(method.getDeclaringClass()+"类的"+method.getName()+"方法被检查过滤!");
/*
如果调用是print方法,则要调用0号位的拦截器去处理
*/
if("print".equals(method.getName()))
//0号位即LogHandler里面 new Callback[]{this,NoOp.INSTANCE}中的this,它在这个数组第0位置
return 1;
/*
1号位即LogHandler里面 new Callback[]{this,NoOp.INSTANCE}中的NoOp.INSTANCE,它在这个数组第1位置。
NoOp.INSTANCE是指不做任何事情的拦截器。
在这里就是任何人都有权限访问print方法,即这个方法没有代理,直接调用
*/
return 0;
}
}
(3)修改一下用户自定义的处理器类LogHandler.java
package reallyCglibProxy;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import jdkDynamicProxy.LonHanderReflectTool;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;
//相当于实现jdk的InvocationHandler接口
public class LogHandler implements MethodInterceptor{
private Object delegate; //被代理类的对象
//绑定被代理类的对象
public Object bind(Object delegate)throws Exception{
this.delegate=delegate;
/**
传进不同的拦截器(相当于JDK代理里面的处理器),NoOp.INSTANCE是cglib已经写好的不做任何事情的拦截器,传进去的new Callback[]是一个数组,现在数组有两个拦截器对象,分别是this,和NoOp.INSTANCE,它们分别在数组的第0位和第一位对应着自定义过滤器MyProxyFilter里的accept方法返回的整数,如果是0就调用this拦截器,如果是1就调用NoOp.INSTANCE所以自定义过滤器MyProxyFilter里的accept方法返回的整数最大就是拦截器数组的长度,比如本例子当中,只能是0或者1,不能是2,因为这个数组只有两个元素,最大位置就是1号位。
*/
return Enhancer.create(delegate.getClass(), null, new MyProxyFilter(), new Callback[]{this,NoOp.INSTANCE}); }
//相当于InvocationHandler接口里面的invoke()方法
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodproxy) throws Throwable {
Object result=null;
System.out.println("我是代理人郭靖,开始代理");
//method.invoke()或者methodproxy.invoke()都可以
result=method.invoke(delegate,args);
//result=methodproxy.invoke(delegate,args);
System.out.println("我是代理人郭靖,代理完毕");
//调用工具类反射jdk的Proxy生成的代理类,可参考《五》中这个工具类
//LonHanderReflectTool.ReflectClass(proxy.getClass().getName());
return result;
}
}
(4) 修改测试客户端(TestReallyCglibProxy.java)。
package reallyCglibProxy;
publicclass TestReallyCglibProxy {
publicstaticvoid main(String[] args)throws Exception {
RealSubject sub1=new RealSubject();
LogHandler hander=new LogHandler();
RealSubject sub2=(RealSubject)hander.bind(sub1);
sub2.print();
sub2.print2();
}
}
输出结果:
class reallyCglibProxy.RealSubject类的print2方法被检查过滤!
class reallyCglibProxy.RealSubject类的print方法被检查过滤!
class java.lang.Object类的finalize方法被检查过滤!
class java.lang.Object类的equals方法被检查过滤!
class java.lang.Object类的toString方法被检查过滤!
class java.lang.Object类的hashCode方法被检查过滤!
class java.lang.Object类的clone方法被检查过滤!
被代理的人郭襄
我是代理人郭靖,开始代理
我是print2方法哦
我是代理人郭靖,代理完毕
从结果可以看出,print方法没有被代理,print2方法被代理了,有了方法过滤器,被代理类被代理的方法(本文例子中就是print、print2、从父类继承的finalize、equals、toString、hashCode、clone这几个方法)都被调用accept方法进行过滤,给每个方法返回一个整数,本例子是0或者1,从而选择不同的处理器。
推荐文章:
浅析JAVA设计模式之代理模式(一)
http://blog.csdn.net/minidrupal/article/details/24807835
浅析JAVA设计模式之代理模式(二)
http://blog.csdn.net/minidrupal/article/details/24888271
浅析JAVA设计模式之代理模式(三)
http://blog.csdn.net/minidrupal/article/details/24985737
浅析JAVA设计模式之代理模式(四)
http://blog.csdn.net/minidrupal/article/details/25058433
浅析JAVA设计模式之代理模式(五)
http://blog.csdn.net/minidrupal/article/details/25093563
浅析JAVA设计模式之代理模式(六)
http://blog.csdn.net/minidrupal/article/details/27948355
Author: Piano
Introduction: 师者
Sign: 读书得可道之道,实践悟不可道之道