读mybatis源代码时,看到mybatis通过jdk动态代理mapper来实现它的CRUD。因为日常工作中比较少用到代理模式。所以对这一块并不熟悉。
闲暇之余,翻阅了一些资料和例子了解了一下。做了个demo、记录一点笔记。
package com.boot.demo.test.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author braska
* @date 2020/3/13
**/
public class ProxyTest {
interface IService {
void call(String word);
}
static class ServiceImpl implements IService {
@Override
public void call(String word) {
System.out.println(word);
}
}
/**
* 静态代理
*/
static class ServiceStaticProxy implements IService {
private IService service;
public ServiceStaticProxy(IService service) {
this.service = service;
}
@Override
public void call(String word) {
System.out.println("static: before call().");
service.call(word);
System.out.println("static: after call().");
}
}
/**
* jdk动态代理的方法处理器
* @param <T>
*/
static class ServiceHandler<T> implements InvocationHandler {
private T t;
public ServiceHandler(T t) {
this.t = t;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk: before call().");
Object obj = method.invoke(t, args);
System.out.println("jdk: after call().");
return obj;
}
}
/**
* jdk动态代理类
* @param <T>
*/
static class ServiceJdkProxy<T> {
private T t;
private ServiceHandler handler;
public ServiceJdkProxy(T t, ServiceHandler handler) {
this.t = t;
this.handler = handler;
}
public T newProxyInstance() {
return (T)Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), handler);
}
}
/**
* cglib动态代理,需引入cglib依赖包
* @param <T>
*/
static class ServiceCglibProxy<T> implements MethodInterceptor {
private T t;
public ServiceCglibProxy() {}
public ServiceCglibProxy(T t) {
this.t = t;
}
public T newProxyInstance() {
Enhancer en = new Enhancer();
en.setSuperclass(t.getClass());
en.setCallback(this);
return (T)en.create();
}
public T newProxyInstance(T t) {
Enhancer en = new Enhancer();
en.setSuperclass(t.getClass());
en.setCallback(this);
return (T)en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib: before call().");
//Object obj = method.invoke(t, objects);
Object obj = methodProxy.invokeSuper(o, objects); //推荐
System.out.println("cglib: after call().");
return obj;
}
}
public static void main(String[] args) {
IService service = new ServiceImpl();
service.call("normal called.");
// static static.
long time1 = System.currentTimeMillis();
ServiceStaticProxy staticProxy = new ServiceStaticProxy(service);
staticProxy.call("static proxy called.");
System.out.println("静态代理调用时间:" + (System.currentTimeMillis() - time1));
// jdk proxy
time1 = System.currentTimeMillis();
ServiceHandler handler = new ServiceHandler(service);
IService proxyService = new ServiceJdkProxy<>(service, handler).newProxyInstance();
proxyService.call("jdk proxy called.");
System.out.println("jdk动态代理调用时间:" + (System.currentTimeMillis() - time1));
// cglib proxy
time1 = System.currentTimeMillis();
//IService cgLibProxyService = new ServiceCglibProxy<>(service).newProxyInstance();
IService cgLibProxyService = new ServiceCglibProxy<IService>().newProxyInstance(service);
cgLibProxyService.call("cglib proxy called.");
System.out.println("cglib动态代理调用时间:" + (System.currentTimeMillis() - time1));
}
}
控制台输出:
normal called. static: before call(). static proxy called. static: after call(). 静态代理调用时间:1 jdk: before call(). jdk proxy called. jdk: after call(). jdk动态代理调用时间:8 cglib: before call(). cglib proxy called. cglib: after call(). cglib动态代理调用时间:121
从结果可以看出jdk动态代理整个过程调用耗时远低于gclib动态代理。但这并不能表示jdk性能比cglib动态代理好。事实上,cglib执行速度略快于jdk代理。jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。