一、动态代理
在使用 spring 进行开发时,经常会使用到动态代理。当我们要实现某个功能时,我们会编写一个 Service 接口,和一个(或者多个)实现该接口的 ServiceImpl 类。这么做的好处是可以使得避免耦合。我们再需要使用该方法是,并不会去显性的创建一个 ServiceImpl,我们会直接通过 Service.Method() 的方式来调用该方法,在普通的程序里,这样是会报错的,毕竟 Service 并没有真的完成该方法的实现。但是,我们只需要添加几个注解,就能完成 ServiceImpl 和 Service 的绑定,从而使用 ServiceImpl 里实现的方法。
这就是动态代理,Service 是 方法调用者和实现者(Impl)之间的代理商。
那么如何实现这样的功能?
二、JDK 动态代理
java 的 java.lang.reflect.* 给我们提供了一种动态代理的实现方案。它可以帮助我们完成接口和接口实现类之间的绑定。
首先,我们创建一个用于测试的 Service:
1 package SSMTest.JProxyTest; 2 3 public interface ProxyService { 4 void HelloWorld(); 5 6 }
然后是它的实现:
1 package SSMTest.JProxyTest; 2 import static MyTools.PrintTools.*; 3 public class ProxyServiceImpl implements ProxyService { 4 5 @Override 6 public void HelloWorld() { 7 println("Hello World"); 8 } 9 10 }
如何将这两者绑定在一起,首先,jdk 提供的方法是:
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), invocationHandler);
第一个参数是 Impl 类的加载器,第二个参数是 Impl 的接口也就是 Service,第三个参数是一个 InvocationHandler 接口,它的 invoke() 方法将提供方法的具体实现。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
该方法的第一个参数是 Proxy 是代理对象,也就是 Proxy.newProxyInstance(...) 方法返回的对象,第二个是当前调度的方法,第三个是调度方法的参数。
1 package SSMTest.JProxyTest; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 import static MyTools.PrintTools.*; 8 9 public class ProxyJdkExample implements InvocationHandler{ 10 11 private Object target = null; 12 13 public Object bind(Object target) { 14 this.target = target; 15 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); 16 } 17 @Override 18 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 19 println("proxy method"); 20 println("before proxy"); 21 Object obj = method.invoke(target, args); 22 println("after proxy"); 23 return null; 24 } 25 26 }
在 invoke 里,我们使用的 method.invoke() 调用了 Impl 的方法。
以下是测试用例:
1 package SSMTest.JProxyTest; 2 3 public class ProxyJdkTest { 4 5 public static void main(String[] args) { 6 ProxyJdkExample jdk = new ProxyJdkExample(); 7 ProxyService proxy = (ProxyService)jdk.bind(new ProxyServiceImpl()); 8 proxy.HelloWorld(); 9 } 10 11 } 12 /*output 13 proxy method 14 before proxy 15 Hello World 16 after proxy*/
如何去理解 jdk 动态代理的实现?
我们假设,A 需要调用 某个方法,这个方法是 接口 B 的方法,而实现这个方法的类是 C?
在程序开始,A(也就是上述的 mian() 方法)定义了一个 B
ProxyService proxy = (ProxyService)jdk.bind(new ProxyServiceImpl());
单从这个语句来看,我们似乎有点多此一举,因为上面以及它背后的大串的代码完全等价于下面这段代码:
ProxyService proxy = new ProxyServiceImpl();
但是我们发现,我们使用动态代理后调用的方法除了实现了方法本身的输出外,还额外输出了我们再 invoke 里的输出,这说明,我们并不是直接调用了 Impl 里 helloWorld() 方法这么简单。而在后面,我们会看到,我们可以在 invoke() 里面多做点文章,然后实现一些特殊的功能。
接着上面的话题,A 首先找到了 B,这个时候我们将 C 和 B 通过 Proxy.newProxyInstance(...) 方法绑定在了一起,然后 B 开始调用它的方法 HelloWorld,但是调用方法的逻辑已经被 invoke() 代理了,也就是说实际上我们并没有真的去执行 ProxyServiceImpl().HelloWorld 这个方法,而是去执行了 invoke() 方法,如果我们愿意,甚至可以让他连 Hello World 都不输出。
这表明,我们甚至可以完全不用去管 Impl 到底是怎么实现 Service 里的方法的,我们可以在 invoke() 里做我们想做的任何事。
换句话来,当 A 在调用 B 的某个方法是,我们可以把这个请求拦截下来,然后进行我们自己的操作。
三、拦截器
既然我们可以将某个方法调用的请求拦截下来,我们不如就去创建一个拦截器。
首先是拦截器的接口:
1 package SSMTest.JProxyTest; 2 3 import java.lang.reflect.Method; 4 5 public interface Interceptor { 6 boolean brfore(Object proxy,Object target,Method method,Object[] args); 7 boolean around(Object proxy,Object target,Method method,Object[] args); 8 boolean after(Object proxy,Object target,Method method,Object[] args); 9 10 }
这个拦截器,在拦截下请求之后,会执行 3 个方法的操作。
然后是它的具体实现:
1 package SSMTest.JProxyTest; 2 3 import java.lang.reflect.Method; 4 import static MyTools.PrintTools.*; 5 6 public class MyInterceptor implements Interceptor { 7 8 @Override 9 public boolean brfore(Object proxy, Object target, Method method, Object[] args) { 10 println("=====================proxy before ============================"); 11 return false; 12 } 13 14 @Override 15 public void around(Object proxy, Object target, Method method, Object[] args) { 16 println("=====================proxy aroud ============================"); 17 } 18 19 @Override 20 public void after(Object proxy, Object target, Method method, Object[] args) { 21 println("=====================proxy after ============================"); 22 } 23 24 }
好,现在我们可以利用这个拦截器去拦截一次方法的请求。
1 package SSMTest.JProxyTest; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 public class InterceptorJdkProxy implements InvocationHandler { 8 9 private Object target; 10 private String interceptorClass = null; 11 12 public InterceptorJdkProxy(Object target,String interceptorClass) { 13 this.target = target; 14 this.interceptorClass = interceptorClass; 15 } 16 17 public static Object bind(Object target,String interceptorClass) { 18 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 19 target.getClass().getInterfaces(), 20 new InterceptorJdkProxy(target,interceptorClass)); 21 } 22 @Override 23 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 24 if(interceptorClass == null) { 25 return method.invoke(target, args); 26 } 27 Object result = null; 28 Interceptor interceptor = (Interceptor)Class.forName(interceptorClass).newInstance(); 29 if(interceptor.before(proxy,target,method,args)) { 30 result = method.invoke(target, args); 31 }else { 32 interceptor.around(proxy, args, method, args); 33 } 34 interceptor.after(proxy, args, method, args); 35 return result; 36 } 37 38 }
InterceptorJdkProxy 和 ProxyJdkExample 的区别在于,我们会做一次判断来决定是继续执行原有的 Impl 的方法,还是执行拦截器自己的方法。另外,在执行方法前后都会有一个操作。
现在我们将原来 ProxyJdkTest 代码的 ProxyJdkExample 换成 InterceptorJdkProxy:
1 package SSMTest.JProxyTest; 2 3 public class ProxyJdkTest { 4 5 public static void main(String[] args) { 6 ProxyService proxy = (ProxyService) InterceptorJdkProxy.bind(new ProxyServiceImpl(),"SSMTest.JProxyTest.MyInterceptor"); 7 proxy.HelloWorld(); 8 } 9 10 } 11 /*output 12 =====================proxy before ============================ 13 =====================proxy aroud ============================ 14 =====================proxy after ============================ 15 */
通过代理的方式,我们将原来的简单的 HelloWorld() 方法的调用拦截,并且按照我们的方式进行的新的操作。