CGLib动态代理的介绍及用法(单回调、多回调、不处理、固定值、懒加载)
参照: https://blog.csdn.net/difffate/article/details/70552056
前面介绍了代理模式,讲了动态代理常见的实现方式,包含了JDK的动态代理和CGLib的动态代理。本文将介绍下CGLib动态代理及几种用法。CGLib(Code Generation Library)是一个高效的代码生成库,底层实现是使用asm来转换字节码生成类。在生成代理类的场景中,由于JDK动态代理必须要求源对象有实现接口,而实际场景中,并不是所有类都有实现接口,因此使用CGLib可以用在未实现接口的类上。
值得注意的几点是:
1)使用CGLib代理的类不能是final修饰的,因为代理类需要继承主题类;
2)final修饰的方法不会被切入;
3)如果主题类的构造函数不是默认空参数的,那么在使用Enhancer类create的时候,选择create(java.lang.Class[] argumentTypes, java.lang.Object[] arguments) 方法。
接下来认识实现动态代理最重要的一个接口 MethodInteceptor
-
package net.sf.cglib.proxy;
-
-
/**
-
* General-purpose {@link Enhancer} callback which provides for "around advice".
-
* @author Juozas Baliuka <a href="mailto:baliuka@mwm.lt">baliuka@mwm.lt</a>
-
* @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $
-
*/
-
public interface MethodInterceptor
-
extends Callback
-
{
-
/**
-
* All generated proxied methods call this method instead of the original method.
-
* The original method may either be invoked by normal reflection using the Method object,
-
* or by using the MethodProxy (faster).
-
* @param obj "this", the enhanced object
-
* @param method intercepted Method
-
* @param args argument array; primitive types are wrapped
-
* @param proxy used to invoke super (non-intercepted method); may be called
-
* as many times as needed
-
* @throws Throwable any exception may be thrown; if so, super method will not be invoked
-
* @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
-
* @see MethodProxy
-
*/
-
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
-
MethodProxy proxy) throws Throwable;
-
-
}
MethodInterceptor,从名字上方法拦截器,就是对方法做切入的。intercept方式的4个参数分别对应增强对象、调用方法、方法参数以及调用父类方法的代理。使用MethodProxy速度会更快,所以后面将用
下面介绍几种用法,这里使用spring包中cglib,其实和引单独的cglib包是一样,只不过spring为了版本不冲突,将cglib包含在自己的包中。
先定义一个主题对象
-
/**
-
* Create by zxb on 2017/4/23
-
*/
-
public class DBQuery {
-
-
public DBQuery() {
-
}
-
-
public DBQuery(Integer i) {
-
System.out.println( "Here's in DBQuery Constructor");
-
}
-
-
public String getElement(String id) {
-
return id + "_CGLib";
-
}
-
-
public List<String> getAllElements() {
-
return Arrays.asList( "Hello_CGLib1", "Hello_CGLib2");
-
}
-
-
public String methodForNoop() {
-
return "Hello_Noop";
-
}
-
-
public String methodForFixedValue(String param) {
-
return "Hello_" + param;
-
}
-
-
public final String sayHello() {
-
return "Hello Everyone!";
-
}
-
}
(一)单回调
切入类:
-
/**
-
* Create by zxb on 2017/4/22
-
*/
-
public class DBQueryProxy implements MethodInterceptor {
-
-
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
-
System.out.println( "Here in interceptor !");
-
return methodProxy.invokeSuper(o, objects);
-
}
-
}
测试类:
-
public class TestCGLibProxy {
-
-
public static void main(String[] args) {
-
DBQueryProxy dbQueryProxy = new DBQueryProxy();
-
Enhancer enhancer = new Enhancer();
-
enhancer.setSuperclass(DBQuery.class);
-
enhancer.setCallback(dbQueryProxy);
-
// DBQuery dbQuery = (DBQuery)enhancer.create(new Class[]{Integer.class}, new Object[]{1});
-
DBQuery dbQuery = (DBQuery) enhancer.create();
-
System.out.println(dbQuery.getElement( "Hello"));
-
System.out.println();
-
System.out.println(dbQuery.getAllElements());
-
System.out.println();
-
System.out.println(dbQuery.sayHello());
-
}
-
}
执行结果:
(二)多回调
在前面的基础上,加个切入类,并通过CallbackFilter来决定是使用哪个切入类
-
/**
-
* Create by zxb on 2017/4/22
-
*/
-
public class DBQueryProxy2 implements MethodInterceptor {
-
-
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
-
System.out.println( "Here in interceptor 2!");
-
return methodProxy.invokeSuper(o, objects);
-
}
-
}
测试类:
-
/**
-
* Create by zxb on 2017/4/22
-
*/
-
public class TestCGLibProxy {
-
-
public static void main(String[] args) {
-
DBQueryProxy dbQueryProxy = new DBQueryProxy();
-
DBQueryProxy2 dbQueryProxy2 = new DBQueryProxy2();
-
Enhancer enhancer = new Enhancer();
-
enhancer.setSuperclass(DBQuery.class);
-
enhancer.setCallbacks( new Callback[]{dbQueryProxy, dbQueryProxy2});
-
enhancer.setCallbackFilter( new CallbackFilter() {
-
-
public int accept(Method method) {
-
if (method.getName().equals( "getElement")) {
-
return 0;
-
} else {
-
return 1;
-
}
-
}
-
});
-
DBQuery dbQuery = (DBQuery) enhancer.create();
-
System.out.println( "========Inteceptor By DBQueryProxy ========");
-
System.out.println(dbQuery.getElement( "Hello"));
-
System.out.println();
-
System.out.println( "========Inteceptor By DBQueryProxy2 ========");
-
System.out.println(dbQuery.getAllElements());
-
}
-
}
执行结果:
(三)不处理
利用枚举常量 Callback noopCb = NoOp.INSTANCE;
测试类:
-
public class TestCGLibProxy {
-
-
public static void main(String[] args) {
-
DBQueryProxy dbQueryProxy = new DBQueryProxy();
-
DBQueryProxy2 dbQueryProxy2 = new DBQueryProxy2();
-
Callback noopCb = NoOp.INSTANCE;
-
Enhancer enhancer = new Enhancer();
-
enhancer.setSuperclass(DBQuery.class);
-
enhancer.setCallbacks( new Callback[]{dbQueryProxy, dbQueryProxy2, noopCb});
-
enhancer.setCallbackFilter( new CallbackFilter() {
-
-
public int accept(Method method) {
-
if (method.getName().equals( "getElement")) {
-
return 0;
-
} else if (method.getName().equals( "getAllElements")) {
-
return 1;
-
} else {
-
return 2;
-
}
-
}
-
});
-
DBQuery dbQuery = (DBQuery) enhancer.create();
-
System.out.println( "========Inteceptor By DBQueryProxy ========");
-
System.out.println(dbQuery.getElement( "Hello"));
-
System.out.println();
-
System.out.println( "========Inteceptor By DBQueryProxy2 ========");
-
System.out.println(dbQuery.getAllElements());
-
System.out.println();
-
System.out.println( "========Return Original Value========");
-
System.out.println(dbQuery.methodForNoop());
-
}
-
}
执行结果:
(四)固定值
需要实现FixedValue接口,会忽略原来函数的返回值,使用固定值来替换。
-
/**
-
* 返回固定的值
-
* Create by zxb on 2017/4/23
-
*/
-
public class DBQueryProxyFixedValue implements FixedValue {
-
-
public Object loadObject() throws Exception {
-
System.out.println( "Here in DBQueryProxyFixedValue ! ");
-
return "Fixed Value";
-
}
-
}
测试类:
-
public class TestCGLibProxy {
-
-
public static void main(String[] args) {
-
DBQueryProxy dbQueryProxy = new DBQueryProxy();
-
DBQueryProxy2 dbQueryProxy2 = new DBQueryProxy2();
-
Callback noopCb = NoOp.INSTANCE;
-
Callback fixedValue = new DBQueryProxyFixedValue();
-
Enhancer enhancer = new Enhancer();
-
enhancer.setSuperclass(DBQuery.class);
-
enhancer.setCallbacks( new Callback[]{dbQueryProxy, dbQueryProxy2, noopCb, fixedValue});
-
enhancer.setCallbackFilter( new CallbackFilter() {
-
-
public int accept(Method method) {
-
if (method.getName().equals( "getElement")) {
-
return 0;
-
} else if (method.getName().equals( "getAllElements")) {
-
return 1;
-
} else if (method.getName().equals( "methodForNoop")) {
-
return 2;
-
} else if (method.getName().equals( "methodForFixedValue")) {
-
return 3;
-
} else {
-
return 0;
-
}
-
}
-
});
-
DBQuery dbQuery = (DBQuery) enhancer.create();
-
System.out.println( "========Inteceptor By DBQueryProxy ========");
-
System.out.println(dbQuery.getElement( "Hello"));
-
System.out.println();
-
System.out.println( "========Inteceptor By DBQueryProxy2 ========");
-
System.out.println(dbQuery.getAllElements());
-
System.out.println();
-
System.out.println( "========Return Original Value========");
-
System.out.println(dbQuery.methodForNoop());
-
System.out.println();
-
System.out.println( "========Return Fixed Value========");
-
System.out.println(dbQuery.methodForFixedValue( "myvalue"));
-
}
-
}
执行结果:
(五)懒加载
CGLib的懒加载,可以用在一些不需要立即加载完整对象实例的场景,比如说Hibernate中的查询对象,如果这个对象有关联其他对象,这个时候不会马上将关联对象一起查询出来关联,要等到调用到这个关联对象时才去做查询。利用CGLib的懒加载机制,可以很好的实现这个需求。需要了解2个接口,LazyLoader和Dispatcher。这两个接口的定义如下:
-
public interface LazyLoader extends Callback {
-
Object loadObject() throws Exception;
-
}
-
-
public interface Dispatcher extends Callback {
-
Object loadObject() throws Exception;
-
}
它们都继承了Callback接口,都有一个loadObject的方法,区别在于LazyLoader只有在第一次调用时,会执行loadObject获取对象,而Dispatcher会在每次调用时都触发loadObject方法,不理解?没关系,后面代码示例上可以看到明显的区别。假定有个学生类(Student),学术类包含2门课的课程表对象,分别是英语课程表(EnglishSchedule)和数学课程表(MathSchedule),它们都是课程表类的实例
-
/**
-
* 课程表
-
* Create by zxb on 2017/4/23
-
*/
-
-
-
-
public class Schedule {
-
-
private String courseName;
-
-
private Date courseTime;
-
}
EnglishSchedule属性的加载依赖于ScheduleLazyLoader
-
/**
-
* Create by zxb on 2017/4/23
-
*/
-
public class ScheduleLazyLoader implements LazyLoader {
-
-
public Object loadObject() throws Exception {
-
System.out.println( "before LazyLoader init...you can query from db...");
-
Schedule schedule = new Schedule();
-
schedule.setCourseName( "English");
-
Calendar calendar = Calendar.getInstance();
-
calendar.set( 2017, 3, 28);
-
schedule.setCourseTime(calendar.getTime());
-
System.out.println( "after LazyLoader init...");
-
return schedule;
-
}
-
}
MathSchedule属性的加载依赖于ScheduleDispatcher
-
/**
-
* Create by zxb on 2017/4/23
-
*/
-
public class ScheduleDispatcher implements Dispatcher {
-
-
public Object loadObject() throws Exception {
-
System.out.println( "before Dispatcher init...you can query from db...");
-
Schedule schedule = new Schedule();
-
schedule.setCourseName( "Math");
-
Calendar calendar = Calendar.getInstance();
-
calendar.set( 2017, 4, 1);
-
schedule.setCourseTime(calendar.getTime());
-
System.out.println( "after Dispatcher init...");
-
return schedule;
-
}
-
}
学生类:
定义时,需要对EnglishSchedule和MathSchedule先初始为动态代理的对象
-
package org.zheng.proxy.cglib.lazyload;
-
-
import lombok.Data;
-
import org.springframework.cglib.proxy.Enhancer;
-
-
/**
-
* Create by zxb on 2017/4/23
-
*/
-
-
public class Student {
-
-
private int id;
-
-
private String name;
-
-
/**
-
* 英语课时间表
-
*/
-
private Schedule EnglishSchedule;
-
-
/**
-
* 数学课时间表
-
*/
-
private Schedule MathSchedule;
-
-
public Student(int id, String name) {
-
this.id = id;
-
this.name = name;
-
this.EnglishSchedule = createEnglishSchedule();
-
this.MathSchedule = createMathSchedule();
-
}
-
-
private Schedule createEnglishSchedule() {
-
Enhancer enhancer = new Enhancer();
-
enhancer.setSuperclass(Schedule.class);
-
enhancer.setCallback( new ScheduleLazyLoader());
-
return (Schedule) enhancer.create();
-
}
-
-
private Schedule createMathSchedule() {
-
Enhancer enhancer = new Enhancer();
-
enhancer.setSuperclass(Schedule.class);
-
enhancer.setCallback( new ScheduleDispatcher());
-
return (Schedule) enhancer.create();
-
}
-
}
测试类:
-
/**
-
* 延迟加载属性
-
* Create by zxb on 2017/4/23
-
*/
-
public class LazyLoadTest {
-
-
public static void main(String[] args) {
-
Student student = new Student( 666, "XiaoMing");
-
System.out.println( "id=" + student.getId());
-
System.out.println( "name=" + student.getName());
-
// LazyLoader 只有第一次,Dispatcher是每次都会进loadObject的方法
-
System.out.println( "========First Get EnglishSchedule ========");
-
System.out.println(student.getEnglishSchedule());
-
System.out.println();
-
System.out.println( "========First Get MathSchedule ========");
-
System.out.println(student.getMathSchedule());
-
System.out.println();
-
System.out.println( "========Second Get EnglishSchedule ========");
-
System.out.println(student.getEnglishSchedule());
-
System.out.println();
-
System.out.println( "========Second Get MathSchedule ========");
-
System.out.println(student.getMathSchedule());
-
}
-
}
执行结果:
可以看到第二次取懒加载对象的时候,实现LoadLazy接口不会重新执行loadObject,而实现Dispatcher的会重新执行LoadObject方法:)
以上,就是使用CGLib的几种常见用法。
项目完整代码: