浅析代理模式

概述

在面向对象系统中,有些对象由于某些原因(比如对象的创建开销很大,或者某些操作需要安全控制),直接访问会给使用者或者系统结构带来很多麻烦,我们在访问此对象时加上一个对此对象的访问层,这种方式被我们称做代理模式或者委托模式;而根据程序运行前代理类是否已经存在,我们又将代理分为静态代理和动态代理。

角色划分
- Subject抽象主题角色:抽象主题类可以是抽象类也可以是接口,它负责定义对外暴露的接口信息。
- RealSubject具体主题角色:也叫做被委托角色或者被代理角色,不折不扣的anonymous。
- Proxy代理主题角色:也叫做委托类或者代理类,它持有真实角色的引用,把所有抽象主题类定义的方法委托给真实主题角色。

模式类图

静态代理

静态代理相对其他模式还是比较容易理解的,这里给出一个简单的demo帮助理解该模式概念。

抽象主题

public interface Subject {

    void request();
}

真实主题

class RealSubject implements Subject {

    private final static String TAG = RealSubject.class.getSimpleName();

    @Override
    public void request() {
        Log.d(TAG, "Real processing");
    }
}

主题代理


public class ProxySubject implements Subject {

    private final static String TAG = ProxySubject.class.getSimpleName();

    private Subject realSubject;

    public ProxySubject() {
        realSubject = new RealSubject();
    }

    @Override
    public void request() {
        Log.d(TAG, "other operation");
        realSubject.request();
        Log.d(TAG, "other operation");
    }
}

场景类

public class MainActivity extends AppCompatActivity {

    Button requestBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        requestBtn = (Button) findViewById(R.id.request);
        requestBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                request();
            }
        });
    }

    private void request(){
        ProxySubject proxySubject = new ProxySubject();
        proxySubject.request();
    }
}

细想一下,每个代理方法中都要重复真实主题代码,如果要想为多个类进行代理,则需要建立多个代理类,维护成本增加;倘若事先并不知道真实角色呢?这些问题可以通过动态代理解决。

动态代理

public class CCInvocationHandler implements InvocationHandler {

    private Object target;

    public CCInvocationHandler() {}

    public CCInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.d(TAG, "other operation");
        Object obj = method.invoke(target, args);
        Log.d(TAG, "other operation");
        return obj;
    }
}
  • target 委托类对象。
  • InvocationHandler 该接口的实现负责连接代理类和委托类。
  • proxy 代理类对象。
  • method 代理对象被调用的函数。
  • args 代理对象被调用的函数的参数。
  • invoke函数中我们也可以通过对method做一些判断,从而对某些函数特殊处理。
public class MainActivity extends AppCompatActivity {

    private final static String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CCInvocationHandler ccInvocationHandler = new CCInvocationHandler(new RealSubject());
        Subject operate = (Subject) (Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class},
                ccInvocationHandler));

        operate.request();

    }
}
  • loader 当前类的类加载器。
  • interfaces 委托类所实现的接口。
  • ccInvocationHandler InvocationHandler实现类对象,连接代理类和委托类的中间类对象。

我想你应该和我一样对此很好奇,动态代理机制是怎么运作的,那就一探究竟吧!

从Proxy.newProxyInstance()切入

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
                                          InvocationHandler invocationHandler)
            throws IllegalArgumentException {

        if (invocationHandler == null) {
            throw new NullPointerException("invocationHandler == null");
        }
        Exception cause;
        try {
            return getProxyClass(loader, interfaces)
                    .getConstructor(InvocationHandler.class)
                    .newInstance(invocationHandler);
        } catch (NoSuchMethodException e) {
            cause = e;
        } catch (IllegalAccessException e) {
            cause = e;
        } catch (InstantiationException e) {
            cause = e;
        } catch (InvocationTargetException e) {
            cause = e;
        }
        AssertionError error = new AssertionError();
        error.initCause(cause);
        throw error;
    }

newProxyInstance代码段还是比较直观的,首先对invocationHandler做非空判断,之后把loader和interfaces传入getProxyClass()后获得代理类,然后拿到代理类的构造函数,最后将invocationHandler作为newInstance参数传入生成代理类对象。关于如何得到代理类的呢?继续跟进getProxyClass()。

    public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
            throws IllegalArgumentException {
        if (loader == null) {
            loader = ClassLoader.getSystemClassLoader();
        }

        if (interfaces == null) {
            throw new NullPointerException("interfaces == null");
        }

        // Make a copy of the list early on because we're using the list as a
        // cache key and we don't want it changing under us.
        final List<Class<?>> interfaceList = new ArrayList<Class<?>>(interfaces.length);
        Collections.addAll(interfaceList, interfaces);

        // We use a HashSet *only* for detecting duplicates and null entries. We
        // can't use it as our cache key because we need to preserve the order in
        // which these interfaces were specified. (Different orders should define
        // different proxies.)
        final Set<Class<?>> interfaceSet = new HashSet<Class<?>>(interfaceList);
        if (interfaceSet.contains(null)) {
            throw new NullPointerException("interface list contains null: " + interfaceList);
        }

        if (interfaceSet.size() != interfaces.length) {
            throw new IllegalArgumentException("duplicate interface in list: " + interfaceList);
        }

        synchronized (loader.proxyCache) {
            Class<?> proxy = loader.proxyCache.get(interfaceList);
            if (proxy != null) {
                return proxy;
            }
        }

        String commonPackageName = null;
        for (Class<?> c : interfaces) {
            if (!c.isInterface()) {
                throw new IllegalArgumentException(c + " is not an interface");
            }
            if (!isVisibleToClassLoader(loader, c)) {
                throw new IllegalArgumentException(c + " is not visible from class loader");
            }
            if (!Modifier.isPublic(c.getModifiers())) {
                String packageName = c.getPackageName$();
                if (packageName == null) {
                    packageName = "";
                }
                if (commonPackageName != null && !commonPackageName.equals(packageName)) {
                    throw new IllegalArgumentException(
                            "non-public interfaces must be in the same package");
                }
                commonPackageName = packageName;
            }
        }

        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()][]);

        String baseName = commonPackageName != null && !commonPackageName.isEmpty()
                ? commonPackageName + ".$Proxy"
                : "$Proxy";

        Class<?> result;
        synchronized (loader.proxyCache) {
            result = loader.proxyCache.get(interfaceList);
            if (result == null) {
                String name = baseName + nextClassNameIndex++;
                result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);
                loader.proxyCache.put(interfaceList, result);
            }
        }

        return result;
    }

代码段稍微有些长,那就一点点分析吧……

    if (loader == null) {
        loader = ClassLoader.getSystemClassLoader();
    }

如果传入的加载抽象主题的类加载器对象为null,就获取系统类加载器,关于类加载,我建议同学们有必要去了解一下,比如类的双亲委派机制,这些概念有助于理解动态加载apk文件。

      synchronized (loader.proxyCache) {
            Class<?> proxy = loader.proxyCache.get(interfaceList);
            if (proxy != null) {
                return proxy;
            }
      }

尝试从缓存中去代理类Class对象,如果存在需要的代理类Class对象则直接返回,否则继续执行。继续往下分析…

        String commonPackageName = null;
        for (Class<?> c : interfaces) {
            if (!c.isInterface()) {
                throw new IllegalArgumentException(c + " is not an interface");
            }
            if (!isVisibleToClassLoader(loader, c)) {
                throw new IllegalArgumentException(c + " is not visible from class loader");
            }
            if (!Modifier.isPublic(c.getModifiers())) {
                String packageName = c.getPackageName$();
                if (packageName == null) {
                    packageName = "";
                }
                if (commonPackageName != null && !commonPackageName.equals(packageName)) {
                    throw new IllegalArgumentException(
                            "non-public interfaces must be in the same package");
                }
                commonPackageName = packageName;
            }
        }

如果interfaces中存在非public的接口,则所有非public接口必须在同一包下面,后续生成的代理类也会在该包下面。

  String baseName = commonPackageName != null && !commonPackageName.isEmpty()
                ? commonPackageName + ".$Proxy"
                : "$Proxy";

得到代理类的类名

     Class<?> result;
     synchronized (loader.proxyCache) {
     result = loader.proxyCache.get(interfaceList);
     if (result == null) {
        String name = baseName + nextClassNameIndex++;
        result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);
        loader.proxyCache.put(interfaceList, result);
     }
   }

generateProxy() native层实现,是JVM加载代理类并返回其Class对象,得到Class对象之后存入缓存。

  • 获取RealSubject上的所有接口列表。
  • 确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX。
  • 根据需要实现的接口信息,动态创建该Proxy class文件。
  • 将字节码信息转换为对应的class对象。
  • 创建InvocationHandler实例handler,用来处理Proxy所有方法调用。
  • Proxy的class对象以创建的handler对象为参数,实例化一个proxy对象。

结语

  • 静态代理模式的使用场景比较常见,比如android appcompat体系或者说context体系。
  • 动态代理可以对代理类的函数做统一或特殊处理,比如所有函数执行前添加验证判断、对某个特殊函数进行特殊操作。

朋友的新书《Android源码设计模式解析与实战》已经出版,购买链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值