静态代理是在编译期已经创建好了代理类,并生产 class 文件,详见代理模式。动态代理是在代码运行过程中,动态生成代理类,其优势在于可以方便地对代理类的所有函数做统一的处理,而无需手动修改每个方法。
Java 动态代理主要包括:
- 基于 JDK 代理
- 基于 CGlib 代理
基于 JDK 代理
编写一个 Handler ,实现InvocationHandler
,在invoke
方法中增加代理逻辑:
public class DynamicInvocationHandler implements InvocatioHandler {
private static Logger LOGGER =
LoggerFactory.getLogger(DynamicInvocationHandler.class);
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable{
LOGGER.info("Invoked method: {}", method.getName());
return 42;
}
}
创建代理对象:
Map proxyInstance = (Map) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(),
new Class[] { Map.class },
new DynamicInvocationHandler());
)
Jdk为我们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时默认不会保存在文件,放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建代理对象实例。通过对这个生成的代理类源码的查看,我们很容易能看出,动态代理实现的具体过程。
可以把 InvocationHandler
看做一个中介类,中介类持有一个被代理对象,被 Proxy 类回调。在invoke 方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把客户端对invoke 的调用最终都转为对被代理对象的调用。
因为生成的代理类继承了Proxy
类,由Java的继承机制决定了,JDK 代理只能代理接口。
JDK 代理自动代理 Object 的 hashCode
, equals
和 toString
方法。
基于 CGlib
Cglib是针对类来实现代理的,原理是对代理的目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理类的缺陷。
但因为采用的是继承,所以不能对final修饰的类进行代理。final修饰的类不可继承。
业务类:
public class Dog {
public String call() {
System.out.println("wang wang");
return "Dog wang";
}
}
拦截器实现 MethodInterceptor
接口:
public class CglibMethodInterceptor implements MethodInterceptor {
public Object CglibProxyGeneratory(Class target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before");
MonitorUtil.start();
Object result = proxy.invokeSuper(obj, args);
System.out.println("after");
MonitorUtil.finish(method.getName());
return result;
}
}
切面,实现增强逻辑:
public class MonitorUtil {
private static ThreadLocal<Long> tl = new ThreadLocal<>();
public static void start() {
tl.set(System.currentTimeMillis());
}
public static void finish(String methodName) {
long finishTime = System.currentTimeMillis();
System.out.println(methodName + "方法执行耗时" + (finishTime - tl.get()) + "ms");
}
}
测试类:
public class CglibTest {
@Test
public void testCglib() throws Exception {
System.out.println(System.getProperty("user.dir"));
saveGeneratedCGlibProxyFiles(System.getProperty("user.dir"));
/** 第一种方法: 创建cglib 代理类 start */
// 创建加强器,用来创建动态代理类
Enhancer enhancer = new Enhancer();
// 为代理类指定需要代理的类,也即是父类
enhancer.setSuperclass(Dog.class);
// new 一个新的方法拦截器
CglibMethodInterceptor cglibMethodInterceptor =
new CglibMethodInterceptor();
// 设置方法拦截器回调引用,对于代理类上所有方法的调用,都会调用CallBack
// ,而Callback则需要实现intercept() 方法进行拦截
enhancer.setCallback(cglibMethodInterceptor); // 获取动态代理类对象并返回
Dog dog = (Dog) enhancer.create(); /** 创建cglib 代理类 end */
System.out.println(dog.call());
// 对于上面这几步,可以新增一个工具方法 放置在 CglibMethodInterceptor 里面
// ;也就有了第二种方法
// new 一个新的方法拦截器,该拦截器还顺带一个用于创建代理类的工具方法。
// 看起来简单很多
cglibMethodInterceptor = new CglibMethodInterceptor();
dog = (Dog) cglibMethodInterceptor.CglibProxyGeneratory(Dog.class);
System.out.println(dog.call());
}
/**
* 设置保存Cglib代理生成的类文件。
* @throws Exception
*/
public void saveGeneratedCGlibProxyFiles(String dir) throws Exception {
Field field = System.class.getDeclaredField("props");
field.setAccessible(true);
Properties props = (Properties) field.get(null);
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, dir);
//dir为保存文件路径
props.put("net.sf.cglib.core.DebuggingClassWriter.traceEnabled"
, "true");
}
}
CGlib 默认代理 Object 中的 equals
,toString
,hashCode
, clone
等方法,比 JDK 代理多了 clone
。