读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。