CGlib,即Code Generation Library,是Java中普遍使用的动态代理类库,尤为是AOP框架。相比于JDK动态代理,它不要求被代理的类实现一个或多个接口,它的底层经过一个小而快的字节码处理框架ASM来转换字节码生成新的类,并且正是由于它直接生成字节码,因此效率比JDK动态代理要高。 java
CGlib提供了好几种产生动态代理类的方法,基本都是是利用net.sf.cglib.proxy.Enhancer这个类。首先建立要代理的类: web
public class Student { String name; public Student() {} public Student(String n) { name = n; } public void setName(String name) { this.name = name; } public void printName() { System.out.println("Student name is: " + name); } public final void finalTest() { System.out.println("This is a final method"); } }
方法一:直接用Enhancer的静态方法生成 数据库
// 定义一个实现了MethodInterceptor接口的回调类,相似JDK动态代理的InvocationHandler class CGLibProxy implements MethodInterceptor { @SuppressWarnings("unchecked") public <T> T getProxy(Class<T> cls) { // 传入两个参数分别表明被代理类和代理运做时的回调类,就相似给一个按钮绑定listener同样 return (T) Enhancer.create(cls, this); } // 实现回调处理方法 @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("~~~ Before Log ~~~"); System.out.println("calling method: " + proxy.getSignature().getName()); /* * proxy是代理方法,因此这里必需要经过proxy.invokeSuper(obj, args)来调用原来Student类 * 中的方法,若是这里是proxy.invoke(obj, args),则调用的仍是proxy方法自己,从而致使无限 * 递归,注意必定不要调用错误了 */ Object result = proxy.invokeSuper(obj, args); System.out.println("~~~ After Log ~~~"); return result; } }当须要建立一个动态代理对象的时候,首先初始化一个CGLibProxy的实例,而后调用它的getProxy方法,传入的参数是被代理类的Class,在咱们的例子中就是Student.class,测试代码以下:
public static void main(String[] args) { CGLibProxy cgLibProxy = new CGLibProxy(); // 这里返回的student就是一个动态代理类了,其实它指向的是Student的一个子类 Student student = cgLibProxy.getProxy(Student.class); student.setName("Joey"); student.printName(); student.finalTest(); }输出结果以下:
~~~ Before Log ~~~ calling method: setName ~~~ After Log ~~~ ~~~ Before Log ~~~ calling method: printName Student name is: Joey ~~~ After Log ~~~ This is a final method能够看到在调用setName()和printName()方法的先后都打印了日志,说明动态代理是成功的,这里有两点要注意:
方法二:经过Enhancer的对象来建立动态代理类 数组
这个方法跟方法一大体相同,只是须要稍微修改一下getProxy()方法的代码: 框架
public <T> T getProxy(Class<T> cls) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(cls); enhancer.setCallback(this); return (T) enhancer.create(); }在方法一中咱们直接传入了要被代理类的Class和回调处理类,而在方法二中,须要经过Enhancer的对象来set这两个参数值,最后经过无参的create方法来产生动态代理类,这个测试的输出结果是同样的。
须要注意的是,无参的create()方法默认调用被代理类Student的无参构造函数来初始化,若是但愿经过有参的构造函数初始化Student,也能够调用create()的一个重载版本: ide
enhancer.create(new Class<?>[]{String.class}, new Object[]{"Jack"});
须要传入两个参数,第一个是Student构造函数的参数列表的Class数组,第二个是对应的值。
CGlib除了效率比JDK动态代理更高之外,它还增长了一些实用的功能,拦截器就是其中之一。以前的例子中咱们对于被代理类Student中的每个非final方法都是采用同一套代理逻辑,即在方法执行先后都打印一下输出,可是在实际的应用中可能须要对不一样的方法产生不一样的代理效果。举个例子,在web开发中,咱们但愿每个service方法能够被事务管理,即每个方法执行前要开始事务,执行后要提交事务,可是对于另外的一些辅助方法,如isEmpty()这种,咱们并不须要作这些数据库事务操做,也许只要打印日志就能够了,若是咱们对于这些类全都采用一样的代理逻辑去生成代理类明显是没有意义的,也很容易产生问题,因此咱们须要一个拦截器,对不一样的方法产生不一样代理逻辑。 函数
首先给Student类增长两个方法用于测试: 测试
public void myservice() { //咱们但愿这种以service结尾的方法在调用前打开事务,调用后提交事务 System.out.println("\t***Student service method***"); } public boolean hasName() { //咱们但愿这种辅助方法不参与事务的管理,只要打印日志便可 System.out.println("\t***Student hasName() method***"); return null == name; }
从新写一个产生动态代理对象的类: this
public class MultipleProxy { // 这两个常数分别对应于callbackClasses数组的下标 private static final int TRANSACTION_MANAGE = 0; private static final int LOG_RECORD = 1; // 把全部回调类都放到一个数组中,拦截器根据获得的返回结果来从数组中获取对应的回调逻辑类 private static Callback[] callbackClasses = new Callback[]{new TransactionManagerProxy(), new LogRecordProxy()}; // 静态内部类,处理跟事务相关逻辑的动态代理 private static class TransactionManagerProxy implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("~~~ Begin Transaction ~~~"); Object result = proxy.invokeSuper(obj, args); System.out.println("~~~ Commit Transaction ~~~"); return result; } } // 静态内部类,处理跟日志相关、跟事务无关的动态代理 private static class LogRecordProxy implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("~~~ Before invoking method ~~~"); Object result = proxy.invokeSuper(obj, args); System.out.println("~~~ After invoking method ~~~"); return result; } } // 获取代理类的方法 public static <T> T getProxy(Class<?> cls) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(cls); // 把全部回调类的数组给它保存 enhancer.setCallbacks(callbackClasses); // 经过匿名内部类设置过滤器,它将accept方法的返回值做为数组下标去获取对应的callback类 enhancer.setCallbackFilter(new CallbackFilter() { @Override public int accept(Method method) { String methodName = method.getName(); // 方法名以service结尾的话则使用事务相关的回调类 if (methodName.endsWith("service")) { return TRANSACTION_MANAGE; } else { // 不然使用日志记录的回调类 return LOG_RECORD; } } }); // 构造动态代理类 return (T) enhancer.create(); } }
Student student = MultipleProxy.getProxy(Student.class); student.myservice(); student.hasName();
~~~ Begin Transaction ~~~ ***Student service method*** ~~~ Commit Transaction ~~~ ~~~ Before invoking method ~~~ ***Student hasName() method*** ~~~ After invoking method ~~~
使用CGlib产生动态代理对象主要有两种方法: spa
CGlib使用中的一些注意点: