这里的服务指的是接口API,在代码解耦中,有一种非常重要的方法就是“面向接口编程”,面向接口编程使得协作的板块之间只要要关注接口API,而无需关注API的具体实现。一套好的面向接口编程架构应该至少包含两个方面:简洁通用的接口定义,以及无迹可寻的接口实现。本文详情的是基于动态代理商实现的服务框架,作用场景可以是APP板块化开发或者者SDK开发。
Java的代理商模式可以分成静态代理商和动态代理商。
静态代理商模式很简单,它有三部分组成:接口、委托类、代理商类。代理商类直接持有委托类的实例,代理商类实现了接口里面的方法,没有方法的执行内部直接通过调用委托类实例对应的方法执行。
动态代理商比静态代理商来的更加方便些,当然其本质也是一样的。看过动态代理商源码之后可以简单的总结一下:动态代理商在运行时生成代理商类的字节码,从字节码中创立出代理商类的实例,对其所有的方法调用都转发到 invocation handler 的 invoke 方法,在 invoke 方法中执行额外的逻辑。
下面,我们简单从代码层面来回顾一下动态代理商的原理。
接口定义
public interface Interface { void doSomething();}
动态代理商调用
Interface anInterface = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class[]{Interface.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; }});
动态代理商生成的字节码
跟踪动态代理商源码,可以很清晰的看到其动态创立 proxy 类的过程。这里我简单做了一个试验,我将动态生成的字节码保存到文件中,再反编译读取出来。
// 获取字节码再保存到文件中String proxyName = "$Proxy0";byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, new Class[]{Interface.class});saveToFile(proxyClassFile);// 通过 JD_GUI 工具读取 $Proxy0.class public final class $Proxy0 extends Proxy implements Interface{ private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final boolean equals(Object paramObject) { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void doSomething() { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.xud.proxy.principle.Interface").getMethod("doSomething", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } }}
从生成的代理商类可以非常清晰的看到,对代理商类中 doSomeThing() 方法的调用最终会回调 InvocationHandler.invoke() 方法。
阅读过 Retrofit 源码的同学肯定对它面向接口的服务使用方式赞赏不已,Retrofit 也是基于动态代理商来实现如此优雅的方式的,本文详情的方式跟 Retrofit 的方式也有相似之处。不过, Retrofit 的动态代理商是没有委托类的,我们只负责定义接口。
假设目前有个接口叫做 AuthService, 我们先来看看这个接口的使用方式,从而对这种方式有个初步的认识。
接口定义:
public interface AuthService extends Service { AbortableFuture<String> login(LoginInfo var1); void logout();}
接口的使用:
AuthService authService = ServiceClient.getService(AuthService.class);authService.login(loginInfo).setCallback(new RequestCallback() { @Override public void onSuccess(Object var1) { } @Override public void onFailed(int var1) { } @Override public void onException(Throwable var1) { }});
接口通信需要支持三种模式:
针对这三种情况,定义了以下几个接口,用以解决接口数据返回:
RequestCallback
数据异步回调接口
public interface RequestCallback<T> { void onSuccess(T var1); void onFailed(int var1); void onException(Throwable var1);}
RequestCallbackWrapper
简化后的数据回调接口
public abstract class RequestCallbackWrapper<T> implements RequestCallback<T> { public RequestCallbackWrapper() { } public abstract void onResult(int var1, T var2, Throwable var3); public void onSuccess(T var1) { this.onResult(200, var1, (Throwable)null); } public void onFailed(int var1) { this.onResult(var1, (Object)null, (Throwable)null); } public void onException(Throwable var1) { this.onResult(1000, (Object)null, var1); }}
InvocationFuture
正常情况下的回调接口封装
public interface InvocationFuture<T> { void setCallback(RequestCallback<T> var1);}
AbortableFuture
可中断的回调接口封装
public interface AbortableFuture<T> extends InvocationFuture { boolean abort();}
有了这几个通用接口时,我们在定义具体的服务API接口就非常方便了,比方上面已经写过的 AuthService 接口。
ServiceClient
在获取服务的时候,我希望有个统一的口子,这个口子就是类 ServiceClient. 这个类中有个核心的方法:getService(), 这是获取服务接口的唯一入口。
public class ServiceClient { private static ServiceCache serviceCache = new ServiceCache(); private static ServiceMethodExcuter excuter = new ServiceMethodExcuter(); public static <T> T getService(Class<T> cls) { return serviceCache.getService(cls); } public static Object excute(ServiceMethodContainer container) { return excuter.invoke(container); }}
ServiceCache
ServiceCache主要是对服务进行缓存,避免每次获取时候重复性创立。
public class ServiceCache { private final Map<Class<?>, Object> caches = new HashMap(); public ServiceCache() { } public final <T> T getService(Class<T> cls) { if (!cls.isInterface()) { throw new IllegalArgumentException("only accept interface: " + cls); } else { synchronized (this.caches) { T hitProxy; if ((hitProxy = (T) this.caches.get(cls)) == null) { hitProxy = (T) Proxy.newProxyInstance(cls.getClassLoader(), new Class[]{cls}, new ServiceInvovationHandler()); this.caches.put(cls, hitProxy); } return hitProxy; } } }}
在这个类中,我们看到了动态代理商的影子,是的,关键就在于它。所以,这个 cache 缓存的是接口服务的代理商类。
ServiceInvovationHandler
接口API方法的调用最终会回调 ServiceInvovationHandler.invoke() 方法,我在这个方法中去具体执行接口方法调用,这个类中的 ServiceMethodContainer 是对 method 和 args 的封装。
public class ServiceInvovationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy: " + proxy.getClass() + ", method:" + method + ", args: " + args); ServiceMethodContainer methodContainer = new ServiceMethodContainer(method, args); return ServiceClient.excute(methodContainer); }}public class ServiceMethodContainer { public Method method; public Object[] args; public ServiceMethodContainer() { } public ServiceMethodContainer(Method method, Object[] args) { this.method = method; this.args = args; } public String getMethodDeclarClassName() { return method.getDeclaringClass().getSimpleName(); } public String getMethodName() { return method.getName(); }}
ServiceMethodExcuter
在 invoke 方法中,是通过调用 ServiceClient.excute(methodContainer) 来执行具体方法的。为此,有一个类 ServiceMethodExcuter 专门用来做这个事情了。这个类中,也可以看到服务接口的具体委托类的实现,即 代码中的 AuthServiceImpl。所以,本文这种动态代理商是一种标准的代理商,它有接口、代理商类、委托类,这个跟 Retrofit 的设计是不同的,下面来具体看代码:
public class ServiceMethodExcuter { private final Map<String, A> serviceMap = new HashMap<>(); ServiceMethodExcuter() { System.out.println("Register Service Start"); this.addService(AuthService.class, AuthServiceImpl.class); this.addService(UserService.class, UserServiceImpl.class); System.out.println("Register Service End"); } public final Object invoke(ServiceMethodContainer container) { ServiceMethodExcuter.A a; if ((a = this.serviceMap.get(container.getMethodDeclarClassName())) == null) { return null; } else { try { Object object = a.invoke(container); return object; } catch (Exception e) { e.printStackTrace(); } return null; } } private void addService(Class<?> interfaceCls, Class<? extends Service> implCls) { this.serviceMap.put(interfaceCls.getSimpleName(), new ServiceMethodExcuter.A(interfaceCls, implCls)); } private static class A { private final Map<String, Method> methodMap = new HashMap<>(); private Service realService; public A (Class<?> interfaceCls, Class<? extends Service> implCls) { Method[] methods; int length = (methods = interfaceCls.getDeclaredMethods()).length; for (int i = 0; i < length; i++) { Method method = methods[i]; this.methodMap.put(method.getName(), method); } try { this.realService = (Service) implCls.newInstance(); } catch (Exception e) { e.printStackTrace(); } } public final Object invoke(ServiceMethodContainer container) throws Exception { return this.methodMap.get(container.getMethodName()).invoke(this.realService, container.args); } }}
这个类也是这个架构中最关键的一个类,当然,它也很简洁。这个类核心的只有三点:
以上,就把基本的原理详情清楚了,使用时,就直接通过 ServiceClient.getService() 来获取服务。
因为篇幅问题,本文只对原理性的东西进行展现,并没有把更多细节的解决展现出来。所以以下这些问题是读者在实操过程中要去考虑的。