Java代理模式与Android的情结

版权声明:本文为博主原创文章,未经博主允许不得转载

前言

Java 代理模式在 Android 中有很多的应用。比如 Android 中 Binder 的 Client 部分就是通过代理模式来访问 Server 端的、代理 Activity 或 Fragment 模式、还有很多开源框架也都使用了代理模式 (主要是动态代理)。

概念

简单地说,代理模式就是代理对象为其他真实对象 (也叫被代理对象) 提供代理机制,以便控制对真实对象的访问。此时,如果真实对象不想直接和客户端接触,则可让代理对象充当真实对象与客户端之间的中介来联系二者,完成事务联系。
抽象地说,代理对象和被代理对象一般实现相同的接口,调用者与代理对象进行交互。代理的存在对于调用者来说是透明的,调用者看到的只是接口。代理对象则可以封装一些内部的处理逻辑,如访问控制、远程通信、日志、缓存等。比如一个对象访问代理就可以在普通的访问机制之上添加缓存的支持

举个简单的例子,比如中国移动 (真实对象) 和用户 (客户端) 之间的这种业务关系。首先,我们老百姓不可能直接和中国移动总部接触,因而中国移动会将业务的操作权下放到各个省市乡的代理点 ( 代理对象),而用户到代理店办的业务其实就是总部的业务,这样一来便可大大地扩大业务,也能更方便服务用户。

代理模式的分类

静态代理

UML类图


UML解析:首先,定义了一个 Subject 类型的接口,并声明了接口方法 DoAction();其次,分别定义了两个实现了 Subject 接口的代理类 Proxy 以及 真实对象类 RealSubject ,其中代理类代表了(delegate)真实对象类;最后,客户端 Cient 依赖关联 Subject,再由 Subject 操作 DoAction()。

代码实现

public interface Subject {
//做操作
void doAction();
}

public class RealSubject implements Subject{

@Override
public void doAction() {

}
}  

public class Proxy implements Subject {
private RealSubject mRealSubject=null;
private void preDoAction(){
    //do something before doAction
}
@Override
public void doAction() {
   if (mRealSubject==null)
       mRealSubject=new RealSubject();
    preDoAction();//代理类准备事情
    //代理类与真实对象类搞事情了,等于代理类执行了真实类中对应的那个方法了,
    //即代理类隐藏了真实类中方法的实现细节。
    mRealSubject.doAction();
    postDoAction();//代理类善后
}
private void postDoAction(){
    //do something after doAction
}
 }  


public class Client {
public static void main(String[] args) throws Throwable {
    Subject subject=new Proxy();
    subject.doAction();//代理类代替真实类做事情
}
}

以上就是静态代理的代码实现。你可以看见,每个代理类都需要一个真实类来对应,即代理类依赖于真实类的对象。

静态代理如何用

我先举个场景。我们需要拿到数据,而数据分别来源于本地、内存及网络,定义一个方法 getData() 用于获取数据。由于我们数据有三种,我们必然需要分别实现 getData(),此时,我们将此方法抽取到接口中,三个实现类各自实现接口即可。然而,现在我们需要在每次获取数据前后执行一些公共操作,比如缓存,日志处理等。那么,最差的做法就是每个实现类都去实现一样的逻辑后再去调用。而如果我们利用静态代理,就可以将此方法代理出去。在代理方法中完整的执行获取数据及其公共操作部分,而隐藏 getData()
的实现细节,实现细节交由各个实现类处理。这样一来,调用者只需调用这个代理方法,而无需和其他实现类交互。在代码层面,客户端调用获取数据的方法时代码调用趋于固定,而如果以后需要修改 getData() 实现细节,也只需要改动实现类中对应的方法即可,这样便提高了可扩展性。

注:getData()类似上述Subject中的doAction(),代理方法类似Proxy中的doAction()

然而,当每个代理类都对应一个真实类,那么大量使用的话会引起代码量的急剧膨胀,因而我们就需要一种机制—不需要提前知道真实类,而是在动态运行中才指定这样一个类。那么这样就引入了代理模式的另一种方式—动态代理。

动态代理

涉及到的几个类

  1. 接口 InvocationHandler:该接口中仅定义了一个方法 Object:invoke(Object obj,Method method, Object[] args)。在实际使用时,第一个参数 obj 一般是指代理类,method 是被代理的方法,如上例中的 doAction(),args 为该方法的参数对象数组。这个抽象方法在代理类中动态实现(反射调用)。
  2. Proxy:该类即为生成的动态代理类的父类,作用类似于静态代理中的 Proxy,其中主要包含以下内容:
    • 构造函数 Protected Proxy(InvocationHandler h),其中子类 (生成的动态代理类)继承父类构造方法可获得 h 实例 。
    • Static Class getProxyClass (ClassLoaderloader, Class[] interfaces):获得一个代理类,其中 loader 是类装载器引用,interfaces 是被代理类所拥有的全部接口的数组。
    • Static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在 Subject 接口中声明过的方法),也就是此时代理类已获得被代理类的类似功能,故你只需操作代理类完成所需请求即可。

机制分析

动态代理允许客户端在运行时刻动态地创建出实现了多个接口的代理类及其对象。其中每一个代理类的对象都会关联一个表示内部处理逻辑的接口类 InvocationHandler (继承父类含参构造方法可得到接口引用)实现。当客户端调用了代理对象所代理的接口方法时,信息会被 InvocationHandler 中的 invoke() 方法拦截处理。在 invoke() 方法里可以截取到代理类的对象,代理方法对应的 Method 对象以及实际调用的参数,然后再经过处理而后返回新的对象。

接下来我们来看看代码实现。

代码实现

Subject 以及 RealSubject 同上

  1. 实现了调用处理器监听的动态代理类

     public class DynamicProxyObject implements InvocationHandler {
     private Object mObject;//被代理对象的引用
    
     public DynamicProxyObject(Object object) {
         mObject = object;
     }
    
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         System.out.println("before calling " + method);
         Object object=method.invoke(mObject,args);
         System.out.println("after calling " + method);
         return object;
     }
     }
    
  2. 客户端类

     public class Client {
     public static void main(String[] args) throws Throwable {
         RealSubject rs = new RealSubject();
         InvocationHandler handler = new DynamicProxyObject(rs);
         Class cl = rs.getClass();
         //第一种.分解步骤得到代理类对象
         //Class c = Proxy.getProxyClass(cl.getClassLoader(), cl.getInterfaces());
         //Constructor ct = c.getConstructor(new Class[]{InvocationHandler.class});
         //Subject subject1 = (Subject) ct.newInstance(new Object[]{handler});
        //第二种.一次性得到代理类对象
         Subject subject=
         (Subject) Proxy.newProxyInstance(cl.getClassLoader(),cl.getInterfaces(),handler);
          subject.doAction();
     }
     }
    
  3. 总结

    通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject 接口)可以在运行时改变,控制的方式(DynamicProxyObject 类)也可以动态改变,从而实现了非常灵活的动态代理机制。

    源码分析

    主要以 Proxy 类为主 (该类位于java.lang.reflect 包下)

    几个重要的静态变量

     private final static String proxyClassNamePrefix = "$Proxy";
    
     //动态代理类的构造函数参数类数组
     private final static Class[] constructorParams ={ InvocationHandler.class };
    
     //映射表:用于维护类加载器对象到其对应的代理类缓存
     private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache
             = new WeakHashMap<>();
    
     //标记动态代理类正在创建中
     private static Object pendingGenerationMarker = new Object();
    
     //用于创建唯一的动态代理类名而定义的 number,可递增
     private static long nextUniqueNumber = 0;
     private static Object nextUniqueNumberLock = new Object();
    
     //同步表:记录所有已创建的动态代理类实例类型,可供 isProxyClass() 使用
     private static Map<Class<?>, Void> proxyClasses =
             Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());
    
     //动态代理类关联的 InvocationHandler
     protected InvocationHandler h;  
    

    获取动态代理类实例的方法1

     private static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 
     throws IllegalArgumentException
     { 
      Class<?> proxyClass = null;
    
     //收集接口名用来作为代理类缓存的key
     String[] interfaceNames = new String[interfaces.length];
    
     //用于检测重复接口名
     Set<Class<?>> interfaceSet = new HashSet<>();
    
     for (int i = 0; i < interfaces.length; i++) {
         //验证class loader 解析出来的接口是否与反射出来的类对象相同
         String interfaceName = interfaces[i].getName();
         Class<?> interfaceClass = null;
         try {
             //Java反射得到一个类对象
             interfaceClass = Class.forName(interfaceName, false, loader);
         } catch (ClassNotFoundException e) {
         }
         //当前反射得到的类对象不等于接口数组中的接口类对象
         if (interfaceClass != interfaces[i]) {
             throw new IllegalArgumentException(
                     interfaces[i] + " is not visible from class loader");
         }
    
         //验证反射出来的类对象是否是接口
         if (!interfaceClass.isInterface()) {
             throw new IllegalArgumentException(
                     interfaceClass.getName() + " is not an interface");
         }
    
         //验证接口类对象不重复
         if (interfaceSet.contains(interfaceClass)) {
             throw new IllegalArgumentException(
                     "repeated interface: " + interfaceClass.getName());
         }
         //每次反射得到的类对象即接口对象存入 Set 集合
         interfaceSet.add(interfaceClass);
         //数组记录接口类名
         interfaceNames[i] = interfaceName;
     }
    
     //接口名数组转接口名集合,以作为代理类缓存的 key
     List<String> key = Arrays.asList(interfaceNames);
    
     //为类加载实例查找或者创建代理类缓存
     Map<List<String>, Object> cache;
     synchronized (loaderToCache) {
         cache = loaderToCache.get(loader);
         if (cache == null) {//无则创建加入缓存映射表
             cache = new HashMap<>();
             loaderToCache.put(loader, cache);
         }
     }
     //接下来就是使用 key 检索代理类缓存。而这次检索将会产生以下三张情况
     //1.空值 null. 意味着当前类加载器中没有该代理类
     //2.正在创建的对象.意味着一个代理类正在创建中.
     //3.类对象的一个弱引用,意味着代理类此时已经创建.
    
     synchronized (cache) {
         do {
             Object value = cache.get(key);
             if (value instanceof Reference) {
                 proxyClass = (Class<?>) ((Reference) value).get();
             }
             if (proxyClass != null) {
                 //上述情况3,直接返回代理类实例
                 return proxyClass;
             } else if (value == pendingGenerationMarker) {
                 // 上述情况2,需要等待创建成功
                 try {
                     cache.wait();
                 } catch (InterruptedException e) {
    
                 }
                 continue;
             } else {
    
                 //上述情况1,标记正在创建.
                 cache.put(key, pendingGenerationMarker);
                 break;
             }
         } while (true);
     }
    
     try {
         String proxyPkg = null;     //定义代理类所在的包名
    
         //记录非公有代理类接口的包名以便代理类都能被定义在相同包名下,
         // 并验证所有的非公有代理类接口都在相同包名下.
         for (int i = 0; i < interfaces.length; i++) {
             int flags = interfaces[i].getModifiers();
             if (!Modifier.isPublic(flags)) {//是否公有
                 String name = interfaces[i].getName();
                 int n = name.lastIndexOf('.');
                 String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                 if (proxyPkg == null) {//接口名作为包名
                     proxyPkg = pkg;
                 } else if (!pkg.equals(proxyPkg)) {//包名不同
                     throw new IllegalArgumentException(
                             "non-public interfaces from different packages");
                 }
             }
         }
    
         if (proxyPkg == null) {
             proxyPkg = "";
         }
          //直接生成代理类引用
         {
             //得到所有接口的方法对象集合
             List<Method> methods = getMethods(interfaces);
             //方法对象集合排序
             Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
             //验证每个方法的返回类型
             validateReturnTypes(methods);
             //去掉重复的方法,并收集异常
             List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
    
             Method[] methodsArray = methods.toArray(new Method[methods.size()]);
             Class<?>[][] exceptionsArray =
             exceptions.toArray(new Class<?>[exceptions.size()][]);
    
             //定义动态代理类的名字 proxyName
             final long num;
             synchronized (nextUniqueNumberLock) {
                 num = nextUniqueNumber++;
             }
             String proxyName = proxyPkg + proxyClassNamePrefix + num;
             //利用 generateProxy() 方法传入所需参数得到动态代理类的引用
             proxyClass = generateProxy(proxyName, interfaces, loader, methodsArray,
                     exceptionsArray);
         }
         //保存数据
         proxyClasses.put(proxyClass, null);
    
     } finally {
    
         //异常进入处理,如果代理类创建成功则用弱引用处理后加入代理类缓存中,
         // 否则去除该key所对应的正在创键的代理类
         synchronized (cache) {
             if (proxyClass != null) {
                 cache.put(key, new WeakReference<Class<?>>(proxyClass));
             } else {
                 cache.remove(key);
             }
             //刷新缓存集合
             cache.notifyAll();
         }
     }
     return proxyClass;
     }
    

    动态创建代理类实例的方法2

      public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
     InvocationHandler h) throws IllegalArgumentException
     {
      if (h == null) {
         throw new NullPointerException();
     }
     Class<?> cl = getProxyClass(loader, interfaces);
    
     /*
      * @param constructorParams 构造器参数类数组
      * 利用构造器反射得到关联了 InvocationHandler 的代理类实例
      *
      */
     try {
         final Constructor<?> cons = cl.getConstructor(constructorParams);
         return newInstance(cons, h);//实际调用了  cons.newInstance(new Object[] {h} );
     } catch (NoSuchMethodException e) {
         throw new InternalError(e.toString());
     }
        }  
    

    动态创建出实现了接口方法的代理类代码例子

     import com.example.hrx.mvpdemo.bean.Subject;
     import java.lang.reflect.InvocationHandler;
     import java.lang.reflect.Method;
     import java.lang.reflect.Proxy;
     import java.lang.reflect.UndeclaredThrowableException;
    
     public final class DynamicProxyType extends Proxy implements Subject {
     private static Method m1;
     private static Method m0;
     private static Method m3;
     private static Method m2;
    
     //构造方法,参数就是一开始实例化的InvocationHandler接口的实例 继承自 Proxy
     public $Proxy1(InvocationHandler var1){
         super(var1);
     }
    
     public final boolean equals(Object var1){
         try {
             return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
         } catch (RuntimeException | Error var3) {
             throw var3;
         } catch (Throwable var4) {
             throw new UndeclaredThrowableException(var4);
         }
     }
    
     public final int hashCode() {
         try {
             return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
         } catch (RuntimeException | Error var2) {
             throw var2;
         } catch (Throwable var3) {
             throw new UndeclaredThrowableException(var3);
         }
     }
     //动态代理类代理的方法实现
     public final void doAction()  {
         try {
             super.h.invoke(this, m3, (Object[])null);
         } catch (RuntimeException | Error var2) {
             throw var2;
         } catch (Throwable var3) {
             throw new UndeclaredThrowableException(var3);
         }
     }
    
     public final String toString()  {
         try {
             return (String)super.h.invoke(this, m2, (Object[])null);
         } catch (RuntimeException | Error var2) {
             throw var2;
         } catch (Throwable var3) {
             throw new UndeclaredThrowableException(var3);
         }
     }
    
     //static代码块加载Method对象
     static {
         try {
             m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
             m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
             m3 = Class.forName("com.example.hrx.Subject").getMethod("doAction", new Class[0]);
             m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
         } catch (NoSuchMethodException var2) {
             throw new NoSuchMethodError(var2.getMessage());
         } catch (ClassNotFoundException var3) {
             throw new NoClassDefFoundError(var3.getMessage());
         }
     }
     }
    

    动态代理怎么用

    我们仍以静态代理怎么用中的场景为例。当实现类越来越多时,利用动态代理,能够大大简化代码量。而我们在静态代理的代理方法实现类似地被放在了动态代理中invoke()中,但是实现形式有差异。后者主要利用反射来拿到所需要的实现类的被代理方法,其他则和静态代理无异。

    与 Android 的情结

    Retrofit 中自定义的网络请求到 OkHttp.Call 的适配

    我们都知道可以使用类似 GitHub github = retrofit.create(GitHub.class) 来获得接口的引用;其实在内部是创建了一个实现了我们自定义的接口 GitHub 的动态代理类,当我们开始执行网络请求时,代理类会按照 Retrofit 先前配置的逻辑来处理我们发出的网络请求:比如交由 okhttp3.Call 来进行网络请求。Retrofit 完成的是封装客户网络请求的高效工作,而真正的网络请求的工作是委托给了 OkHttp 来完成。

关键代码:

    public final class Retrofit {

      public <T> T create(final Class<T> service) {
        //此处同样应用上述创建动态代理类实例方法
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] 
        { service },new InvocationHandler() {
              private final Platform platform = Platform.get();
               @Override
               public Object invoke(Object proxy, Method method, Object... args)
                  throws Throwable {
                // method来自Object.class则不做自定义处理
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                //默认处理,即根据平台类型(Android、Java8、IOS)做处理
                if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                ServiceMethod serviceMethod = loadServiceMethod(method);
                OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                return serviceMethod.callAdapter.adapt(okHttpCall);
              }
            });
      }
    }  

binder机制

在跨进程通信中,binder 作为通信的媒介,联系了客户端 Client 与服务端 Server 的信息交流。
接下来,我们来看一张图:

注:图中 SM 表示 ServerManager,其在 Android 中是联系这四大组件的关键。

首先,Server 必须先注册到 SM 中,才能够有机会和 Client 通信,然后 Client 发起请求想要调用 Server 中的方法 add(),然而 Client 与 Server 处在不同的进程当中。如果没有媒介binder 驱动的帮助,进程间就无法完成通信。因此,透过 binder 的作用,Server 可以给 Client 下发一个代理对象,以便能够调用 Server中的方法。这样以来,Client 就无法知道 Server 的逻辑,而只能调用被 Server 代理出去的方法 add()。这一通信过程,便是跨进程通信。

总结

Java 代理模式的应用还是相当广泛的。对于静态代理模式,我们可以用它来代理 Activity 或者 Fragment 的生命周期方法,定义一个不注册的 ActivityDelegate 或者 FragmentDelegate,这个可能会是比较特别的高级技巧。对于动态代理,涉及到的大多是第三方框架,其中思想很多包含有动态代理。

感谢

Java设计模式之代理模式(Proxy)
JAVA 代理模式(Proxy)
代理模式和Java中的动态代理机制
写给Android App开发人员看的Android底层知识

-------------本文结束感谢您的阅读-------------