前言
与 jdk 自带代理只能代理 实现了接口对象不同,cglib可以代理任意一类,当然该类不能为 final 类型。本文主要研究一下 Cglib 是什么、如何一些基本使用、底层实现原理。
Generates dynamic subclasses to enable method interception.This class started as a substitute for the standard Dynamic Proxy support included with JDK 1.3, but one that allowed the proxies to extend a concrete base class, in addition to implementing interfaces.
Cglib 是什么
Cglib 是一个强大的 高性能的字节码生成工具,它被广泛的用于许多AOP框架中,为他们提供拦截方法,参考下图:
- 最底层时字节码,一次编译,到处运行的虚拟字节码指令
- ASM 直接操作字节码的框架,需要对字节码的结构比较熟悉
- Groovy BeanShell 说明运行在虚拟机上的不一定是Java语言生成的字节码,这也是虚拟机当初涉及的初衷,因为才分为 java 语言规范 和 虚拟机规范 两部分。java 代码 是通过编译器直接生成的 字节码
- Hibername Spring AOP 等 大量运用 cglib 提供的反射功能
- 最上层 具体 项目应用
使用Cglib做代理
1、引入依赖最新依赖查询
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2、被代理的类
public class Dao {
public void query(Long id) {
System.out.println("Query method is invoked");
}
public void update(String key) {
System.out.println("Update method is invoked");
}
}
3、代理类
public class Client {
public static void main(String[] args) {
DaoProxy proxy = new DaoProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallback(proxy);
Dao dao = (Dao) enhancer.create();
dao.query(1111L);
dao.update("hello");
}
}
4、输出:
Before method invoked,method name :query
Before method invoked,method name :toString
Before method invoked,method name :hashCode
After method invoked----------------
Query method is invoked
After method invoked----------------
Before method invoked,method name :update
Update method is invoked
After method invoked----------------
对不同方法分别做代理
那么问题来了,如果我想对不同的方法分别做不同处理,该如何呢?
比如:query 做拦截输出 query , update 拦截输出 update
先看代码改造如下:
增加 filter
public class DaoFilter implements CallbackFilter {
@Override
public int accept(Method method) {
if ("query".equals(method.getName())) {
return 0;
}
if ("update".equals(method.getName())) {
return 1;
}
return 2;
}
}
分别增加 对应的 proxy 处理:
public class DaoForQueryProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Method query filter is interceptored!!");
proxy.invokeSuper(obj, args);
System.out.println("Method query filter is interceptored over!!");
return obj;
}
}
public class DaoForUpdateProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Method update filter is interceptored!!");
proxy.invokeSuper(obj, args);
System.out.println("Method update filter is interceptored over!!");
return obj;
}
}
客户端调用变更:
public class Client {
public static void main(String[] args) {
DaoProxy proxy = new DaoProxy();
DaoForQueryProxy queryProxy = new DaoForQueryProxy();
DaoForUpdateProxy updateProxy = new DaoForUpdateProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
//enhancer.setCallback(proxy);
enhancer.setCallbacks(new Callback[] { queryProxy, updateProxy, proxy, NoOp.INSTANCE });
enhancer.setCallbackFilter(new DaoFilter());
Dao dao = (Dao) enhancer.create();
dao.query(1111L);
dao.update("hello");
}
}
用法解释如下:
自定义一个filter 实现 CallbackFilter,根据不同方法名称 返回一个 从0 开始的顺序号。
针对不同的方法 分别实现 不同的 proxy。
enhancer.setCallbacks(new Callback[] { queryProxy, updateProxy, proxy, NoOp.INSTANCE });
set 的数组顺序就代表了 需要跟 filter 中顺序保持一致。
如上 就可以了,还有个设置 enhancer.setInterceptDuringConstruction(false);
作用就是如果拦截方法出现在构造方法中不执行输出
源码方面可以debug 自己跟进,涉及到底层 字节码的一些生成
我相信乔布斯说的,只有那些疯狂到认为自己可以改变世界的人才能真正地改变世界。
附:代理模式姐妹篇