接着上篇博客的代理模式,我们继续,上篇博客介绍了JDK的动态代理,但是JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,这样就存在一定的局限性。对于这种情况,我们采用CGLIB来实现。
一、CGLIB动态代理
cglib是针对类来实现代理的,其实现原理:CGLIB的底层采用ASM字节码生成框架,使用字节码技术生成代理,比使用反射生成代理的效果要高,是对指定的目标类生成一个子类,并覆盖其中方法实现增强。但是也有一点点不足,因为采用的是继承,所以不能对final修饰的类进行代理。
还是使用以前的代码,依然还是简单的三步来实现。第一:建立一个普普通通的业务类;第二:写CGLIB代理类;第三:写测试代码或者客户端调用。这里的不同是第一步中,我们不需要在建接口了,只是一个普普通通的java类。
看代码:
<span style="font-family:KaiTi_GB2312;font-size:18px;"> /**
* 业务类(包含业务逻辑)
* 即:委托类
* @author Cassie
*/
public class Account{
@Override
public void queryAccount() {
System.out.println("查询方法...");
}
@Override
public void updateAccount() {
System.out.println("修改方法...");
}
}</span>
然后,我们来写CGLIB代理类:
<span style="font-family:KaiTi_GB2312;font-size:18px;">import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 使用cglib动态代理
* @author Cassie
* 实现MethodInterceptor 接口
*/
public class CglibProxy implements MethodInterceptor {
//委托对象,运行时定类型
private Object target;
/**
* 创建代理对象
*
* @param target
* @return
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
// 回调方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("before");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after");
return result
}
} </span>
最后写测试类
调用
<span style="font-family:KaiTi_GB2312;font-size:18px;">public class TestCglib {
public static void main(String[] args) {
//实例化代理
CglibProxy cglib=new CglibProxy();
//通过代理拿到对象
Account account = (Account)cglib.getInstance(new Account());
account.query();
}
}</span>
通过以上代码,我们发现proxy.invokeSuper(obj,arg)是执行的关键。
使用CGLIB,需要实现 CGLib 给我们提供的 MethodInterceptor 实现类,并填充 intercept() 方法。方法中最后一个 MethodProxy 类型的参数 proxy,值得注意!CGLib 给我们提供的是方法级别的代理,也可以理解为对方法的拦截。我们直接调用 proxy 的 invokeSuper() 方法,将被代理的对象 obj 以及方法参数 args 传入其中即可。
至此,CGLIB代理也实现了。