cglib的动态代理不但可以代理接口,还可以代理类
打开cglib的依赖可以发现其依赖了asm框架来实现动态代理
我们先使用cglib的动态代理来代理实现打印调用方法的耗时。
先创建类UserService,实现登录逻辑
package asm;
public class UserService {
public boolean login(String username, String password) {
return "admin".equals(username) && "admin".equals(password);
}
}
实现MethodInterceptor回调类型,打印耗时。
package asm;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class UserMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
long start = System.currentTimeMillis();
try {
return proxy.invokeSuper(obj, args);
} catch (Exception e) {
throw e;
} finally {
System.out.println("invoke-" + obj.getClass().getName() + "." + method.getName() + ":" + (System.currentTimeMillis() - start) + "ms");
}
}
}
然后使用Enhancer创建代理类,使用cglib非常方便:
- 创建Enhancer实例
- 设置需要代理的基类
- 设置回调类型
package asm;
import net.sf.cglib.proxy.Enhancer;
public class Main {
public static void main(String[] args) {
System.out.println("cglib动态代理开始");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new UserMethodInterceptor());
UserService userService = (UserService) enhancer.create();
System.out.println(userService.login("admin", "admin"));
System.out.println(userService.login("admin", "admin1"));
}
}
打印结果:
接下来探索一下动态代理生成的类到底长什么样
打开DebuggingClassWriter类,可以发现输出class文件的目录是读取系统配置DEBUG_LOCATION_PROPERTY的值
那先设置DEBUG_LOCATION_PROPERTY的值为我们方便打开的文件目录,这里我设置为'./cglib'
然后执行main方法,发现生成了三个class文件,看控制台输出,我们调用的类是后缀为 $$6a598181的类
接下来看看生成的类UserService6a598181
- 继承了我们设定的父类UserService
- 实现了接口Factory
先看一下接口Factory
Factory接口描述
- Enhancer类返回的所有增强实例都实现了这个接口。
- 将此接口用于新实例比通过Enhancer接口或使用反射更快。
- 此外,要拦截对象构造期间调用的方法,您必须使用这些方法而不是反射。
Factory的方法定义
提供了获取实例对象和添加回调
- 无参构造
- 有参构造
- 设置回调类型
- 获取回调类型
public interface Factory {
/**
* 使用无参数构造函数创建相同类型的新实例。 该对象的类必须是使用单一回调类型创建的。 如果需要多个回调,则会抛出异常。
* 参数:
* 回调 - 要使用的新拦截器
* 返回:相同类型的新实例
*/
Object newInstance(Callback callback);
/**
* 使用无参数构造函数创建相同类型的新实例。
* 参数:
* callbacks – 要使用的新回调
* 返回:相同类型的新实例
*/
Object newInstance(Callback[] callbacks);
/**
* 使用与给定签名匹配的构造函数创建相同类型的新实例。
* 参数:
* types - 构造函数参数类型
* args – 构造函数参数
* callbacks – 要使用的新拦截器
* 返回:相同类型的新实例
*/
Object newInstance(Class[] types, Object[] args, Callback[] callbacks);
/**
* 返回指定索引处的Callback实现
* 参数:index – 回调索引
* 返回:回调实现
*/
Callback getCallback(int index);
/**
* Set the callback for this object for the given type.
* 参数:
* index – 要替换的回调索引
* callback – 新的回调
*/
void setCallback(int index, Callback callback);
/**
* 一次替换此对象的所有回调
* 参数:
* callbacks – 要使用的新回调
*/
void setCallbacks(Callback[] callbacks);
/**
* 获取 this 对象的当前回调集。
* 返回:一个新的数组实例
*/
Callback[] getCallbacks();
}
看完Factory,再回过头来看类UserService6a598181
可以看到生成的类中,方法CGLIB0调用的就是父类UserService的login方法,而login方法里面会调用回调方法intercept进行代理逻辑处理。
调用intercept方法的入参:
- obj: 生成的代理类对象UserService6a598181
- method:UserService.login方法
- args:方法调用参数
- proxy:封装CGLIB0方法的代理