我们可以很轻松的实现一个简单的代理。
静态代理
实现静态代理是个很简单的事情,最基础的代理只需要定义一个接口(虽然不是必要,但这显然才是标准的设计)、一个被代理类和一个代理类,例如:
定义一个接口:
public interface HelloWorldInterface{ public void hello(); }
一个实现类:
public interface HelloWorld extends HelloWorldInterface{ public void hello(){ System.out.println("Do something here!"); } }
和一个代理类:
public interface HelloWorldProxy extends HelloWorldInterface{ HelloWorld helloWorld; public HelloWorldProxy (HelloWorld helloWorld) { super(); this.helloWorld= helloWorld; } public void hello(){ System.out.println("Before hello!"); helloWorld.hello(); System.out.println("After hello!"); } }
实际使用时,我们是去调用HelloWorldProxy的方法,其将作为HelloWorld的代理实现。
此种方式直接实现的代理太过于死板,因为每一种代理行为都要制定一个代理类,我们熟知的很多基于代理的实现(譬如AOP、事务)显然不可能用静态代理的方式针对每一处类切点都覆写一个代理类,这种时候就需要动态代理。
动态代理
我们所熟知的相当多的框架均基于动态代理开发,JDK本身基于反射(java.lang.reflect)提供了动态代理,我们只需定义代理的行为,而对于代理类的范围并不是固定值。核心接口是InvocationHandler,我们通过对其的实现来定义实际的代理行为,调用被代理类只需要invoke方法。并且可以选择调用0~n个被代理的方法。依旧以上面的代理为例,我们假设已经定义了相应的接口和实现类(实现类可以没有也可以是多个),对于代理行为的定义时这样的。
定义代理行为:
public class HelloWorldProxy extends InvocationHandler{ HelloWorld helloWorld; public HelloWorldProxy (HelloWorld helloWorld) { super(); this.helloWorld= helloWorld; } @Override public void invoke(Object proxy, Method method, Object[] args){ System.out.println("Before "+ method.getName()); method.invoke(helloWorld,args); System.out.println("After "+ method.getName()); return null; //非void直接按method.invoke的调用结果返回 } }
由于InvocationHandler其实是函数式接口,可以用lamdba表达式实现。
当我们想要调用代理时,需要指定其
HelloWorld helloWorld= new HelloWorld (); HelloWorldProxy helloWorldProxy = new HelloProxy(helloWorld); Class[] clazzList = {HelloWorldInterface.class}; //没有自定义的类加载器的话直接使用线程上下文的就好,总之都是一个系统 类加载器 HelloWorldInterface proxyHello = (HelloWorldInterface)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList , helloWorldProxy); //调用代理方法,从执行结果看等同于调用了HelloWorldProxy的invoke proxyHello.hello();
如果你懒于定义InvocationHandler的实现,直接使用lamdba即可,当然这种时候我们限定住了可传的被代理实例:
HelloWorldInterface proxyHello = (HelloWorldInterface)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList , (Object proxy, Method method, Object[] args)->{ System.out.println("Before "+ method.getName()); //这一行可以没有,也可以是继承于clazzList的任一实现,本质上 只要能反射执行,就不会报错 method.invoke(helloWorld,args); System.out.println("After "+ method.getName()); return null; //非void直接按method.invoke的调用结果返回 }); proxyHello.hello();
可以预见到基于此,AOP的思路大概是这样的:
Class[] clazza = new Class[0]; try { clazza = new Class[]{Class.forName("com.xhl.autosite.common.Hello")}; } catch (ClassNotFoundException e) { e.printStackTrace(); } Xxx bean = (Xxx)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList , (Object proxy, Method method, Object[] args)->{ System.out.println("Before "+ method.getName()); //method.invoke(xxx); 这一行可能是没有的,有的话是从bean中获取的 System.out.println("After "+ method.getName()); return null; }); //实际执行的是动态代理类的方法了 bean.xxx();