java中的动态代理(二)
上一节我介绍了什么是静态代理。在静态代理中的代理对象是直接定义在代码中的,这样会导致代码不能复用并且工作量也会成倍的增加所以在日常的开发中我们更多使用的是动态代理模式。在动态代理中,代理类在是程序运行中动态生成的,在java中一般有两种方式来实现动态代理模式,它们分别是javaSDK动态代理和第三方类库cglib动态代理。
今天我介绍的是java SDK动态代理。首先我们先来看一下如何使用java SDK实现动态代理模式:
public class JavaSDKProxyTest { static interface BaseService { void say(); } static class RealService implements BaseService { @Override public void say() { System.out.println("i am realService!"); } } static class ProxyInvocationHandler implements InvocationHandler{ private Object service; public ProxyInvocationHandler(Object service){ this.service = service; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("say before"); Object object = method.invoke(this.service, args); System.out.println("say after"); return object; } } public static void main(String[] args) { BaseService realService = new RealService(); BaseService proxyService = (BaseService) Proxy.newProxyInstance(BaseService.class.getClassLoader(), new Class<?>[]{BaseService.class},new ProxyInvocationHandler(realService)); proxyService.say(); } }
感觉代码看起来更加的复杂了,没事其实还是比较的好理解的。BaseService和RealService是公共的接口和真正的对象,它们两个的定义方式没有变化。但是代理对象的创建方式变了,使用的是Proxy这个类里面的一个静态方法newProxyInstance。这个方法需要传三个参数,第一个参数是当前类的类加载器,这里只需要保证和公共接口使用的是同一个类加载器即可,关于ClassLoader我会在下面的文章中单独介绍。第二个参数是一个字节码的对象数组表示的是要实现的接口列表,这里需要注意的是数组中元素的类型只能是接口,不能是普通的类,在该例中只有一个公共的接口就是BaseService。第三个参数是InvocationHandler,它是一个接口只定义了一个方法就是invoke,对代理接口中的所有的方法的调用都会转给该方法。方法newProxyInstance返回的是Object类型但是可以强制转换为字节码数组中的任意接口类型,但是需要注意的是不可以强制转换为接口对应的实现类的类型。
ProxyInvocationHandler实现了InvocationHandler接口,这个接口中的invoke方法中有三个参数,其中proxy表示的代理对象,method表示的是正在被调用的方法,args表示正在被调用的方法的参数。所以当我们需要执行被代理对象的方法时可以利用反射来调用:Object result = method.invoke(realObject,args),这里需要注意的是不要把proxy当成被代理的对象传到invoke方法中,如果把proxy传到invok方法中会导致死循环。
与静态代理相比动态代理在实现上更加的复杂但是设计的思想是一样的。今天我介绍的java SDK的动态代理的局限在于它只能为接口创建代理,返回的代理对象也只能强转成接口类型,如果一个类没有实现接口就不适用了。