Java的动态代理真的很是重要,特别是想要了解一些框架的原理的时候,若是对Java动态代理不熟悉,甚至不了解Java动态代理,那基本上就只能说一句“我太难了”。java
好比想要知道MyBatis为何不用实现方法,只须要一个接口和一个对应的xml文件就能实现数据库操做。git
Spring中为何咱们使用一个@Transactional注解就能实现事务自动回滚。github
固然了解了Java的动态代理,咱们本身也能够实现一些无侵入,对用户透明友好的附加功能。spring
因此想要了解这些原理,就先来了解一下Java动态代理吧。数据库
首先咱们来看一些JDK的动态代理怎样实现。app
要理解JDK的动态代理,只须要理解Proxy类和InvocationHandler接口就能够了。框架
不过在这以前咱们先介绍2个概念: 目标对象(target):被代理的对象 代理对象(proxy):Proxy动态生成并加载的对象ide
首先咱们来看InvocationHandler,InvocationHandler接口很是简单就一个方法,以下所示:测试
public Object invoke(Object proxy, Method method, Object[] args)
这个方法就是实现JDK代理逻辑的地方,proxy是代理对象,这个是在Proxy类中建立的。this
Method就是目标对象要执行的方法,args就是调用Method的时候传入的参数。
咱们在invoke中的逻辑通常是这样的;
//原方法执行以前的代理逻辑 method.invoke(target, args);//原方法 //原方法执行以后的代理逻辑
咱们能够看到,基本没有使用到代理对象proxy,而是使用了目标对象,由于代理部分通常仍是会执行原对象的逻辑。
注意:必定不要在method.invoke方法上使用proxy,而要使用target对象
InvocationHandler实现了,他会在什么地方被调用呢?
咱们来看一些Proxy,就清楚了。
Proxy关注一个重要的静态方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
上面的方法就是建立一个代理对象proxy方法。
仔细想一下,建立对象得先有类啊,Proxy使用newProxyInstance生成的代理对象是一个什么类?
这里直接说结论:Proxy使用newProxyInstance建立对象使用的类是动态生成并建立的
这个类继承了Proxy类,并实现了newProxyInstance方法的第二个参数传入的interfaces接口
生成字节码的是ProxyGenerator.generateProxyClass方法,有兴趣能够跟一下newProxyInstance方法的getProxyClass0方法。
字节码使用的加载器是newProxyInstance方法的第一个参数传入的ClassLoader。
那我要这InvocationHandler有何用?别急,咱们看一下生成的代理对象中的方法大概长什么样的
public final int insert(User paramUser) throws { try{ return ((Integer)this.h.invoke(this, m3, new Object[] { paramUser })).intValue(); } catch (Error|RuntimeException localError){ throw localError; } catch (Throwable localThrowable){ throw new UndeclaredThrowableException(localThrowable); } }
上面的this.h就是newProxyInstance建立代理对象传入的参数InvocationHandler,咱们能够看到实现interfaces的接口中的方法实际上都是调用InvocationHandler的invoke方法。
m3是Method对象,是经过Class.forName获取,参数就是interfaces的对应的全限定名称。
m3 = Class.forName("xxxx.UserMapper").getMethod("insert", new Class[] { Class.forName("xxxx.User") });
这里就不给具体的例子了,能够参考一下后面的其它文章,或者看BeanPostProcessor与Spring无侵入扩展,顺便还能够了解一下BeanPostProcessor。
cglib和JDK的动态代理很类似,cglib最让人想吐槽的地方就是没有文档,不过也没有太大关系,咱们主要看MethodInterceptor这个接口就能够了。
Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy)
先看参数:
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class DoSomethingMethodInterceptor implements MethodInterceptor { public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { // return method.invoke(o,objects); // System.out.println(proxy); return methodProxy.invokeSuper(proxy,objects); } }
在MethodInterceptor中调用目标对象使用的是在代理对象proxy上调用代理方法methodProxy,使用的也是invokeSuper,而不是invoke,这容易让人困惑。
用英语老师的话说,下面是固定用法,记住就能够了:
methodProxy.invokeSuper(proxy,objects);
@Test public void testBusinessDoSomethingMethodInterceptor() { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\tmp\\code"); BusinessServiceImpl target = new BusinessServiceImpl(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(new DoSomethingMethodInterceptor()); BusinessService bs = (BusinessService) enhancer.create(); System.out.println(bs.doSomething(1, "curitis")); }
设置系统属性,是为了让cglib把生成的类写到指定目录,这样调试或者分析的时候能够看一下生成的类的逻辑。
重要的类Enhancer,使用Enhancer分3步走就能够了:
javassist的强大之处在于它操做字节码的能力,能够动态的修改类,加载类,添加删除字段、方法等操做。
固然也能够实现动态代理,这里咱们就介绍一下经过javassist实现动态代理。
javassist代理逻辑能够是在MethodHandler接口中,MethodHandler只有一个方法:
Object invoke(Object self, Method thisMethod, Method proceed,Object[] args)
self:生成的代理类 thisMethod:目标类的方法 proceed:代理类的方法 args:执行方法的参数
javassist的执行逻辑和cglib的很像,是在代理类实例上调用代理方法:
proceed.invoke(self, args)
import javassist.util.proxy.MethodHandler; import java.lang.reflect.Method; public class DoSomethingMethodHandler implements MethodHandler { @Override public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { System.out.println(self.getClass()); System.out.println(thisMethod.getName()); System.out.println(proceed.getName()); Object result = proceed.invoke(self, args); return result; } }
javassist建立代理类经过ProxyFactory,分4步走:
import javassist.util.proxy.ProxyFactory; public class JavassistProxyFactory<T> { private T target; public JavassistProxyFactory(T target) { this.target = target; } @SuppressWarnings( {"deprecation","uncheked"}) public T getProxy() throws InstantiationException, IllegalAccessException { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setSuperclass(target.getClass()); proxyFactory.setHandler(new DoSomethingMethodHandler()); return (T) proxyFactory.createClass().newInstance(); } @SuppressWarnings( {"deprecation","uncheked"}) public static <T> T getProxy(Class<T> clazz) throws InstantiationException, IllegalAccessException { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setSuperclass(clazz); proxyFactory.setHandler(new DoSomethingMethodHandler()); return (T) proxyFactory.createClass().newInstance(); } }
import org.curitis.service.BusinessService; import org.curitis.service.impl.BusinessServiceImpl; import org.junit.Test; public class JavasistTest { @Test public void test() throws IllegalAccessException, InstantiationException { BusinessService proxy = JavassistProxyFactory.getProxy(BusinessServiceImpl.class); proxy.doSomething(1,"curitis"); } }
<properties> <javassist.version>3.12.1.GA</javassist.version> <cglib.version>3.2.5</cglib.version> </properties> <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>${javassist.version}</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>${cglib.version}</version> </dependency>
public interface BusinessService { String doSomething(Integer id,String name); }
import org.springframework.stereotype.Service; @Service("businessService") public class BusinessServiceImpl implements BusinessService{ @Override public String doSomething(Integer id, String name) { return id + " " + name; } }