浅析代理模式

概述

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

角色划分

- Subject抽象主题角色:抽象主题类能够是抽象类也能够是接口。它负责定义对外暴露的接口信息。

- RealSubject详细主题角色:也叫做被托付角色或者被代理角色,不折不扣的anonymous。

- Proxy代理主题角色:也叫做托付类或者代理类,它持有真实角色的引用,把全部抽象主题类定义的方法托付给真实主题角色。

模式类图

静态代理

静态代理相对其它模式还是比較easy理解的,这里给出一个简单的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源代码设计模式解析与实战》已经出版,购买链接

时间: 2024-07-28 17:50:55

浅析代理模式的相关文章

浅析代理模式(转)

前言: 代理模式作为常见的设计模式之一,在项目开发中不可或缺.本文就尝试着揭开代理的神秘面纱,也欢迎各路人批评指正! 1.如何实现代理: [假设有个关于汽车移动(move)的计时需求] 设计:Moveable接口,一个Car的实现类:两个代理CarTimer,TimeHandler.UML图如下: 1)继承 1 package com.gdufe.proxy; 2 3 import java.util.Random; 4 5 public class CarTimer extends Car {

Spring框架_代理模式(静态代理,动态代理,cglib代理)

共性问题: 1. 服务器启动报错,什么原因? * jar包缺少.jar包冲突 1) 先检查项目中是否缺少jar包引用 2) 服务器: 检查jar包有没有发布到服务器下:                                      用户库jar包,需要手动发布到tomcat. (每次新建项目) 3) 重新发布项目 * 配置文件错误 (web.xml / struts.xml /bean.xml /hibernate.xml / *.hbm.xml) 明确的提示 * 端口占用 * we

拦截器的设计思想——代理模式

代理模式:静态代理和动态代理 1.代理类和被代理类必须实现同一个借口 2.代理类和别代理类有一种关联关系 静态代理: 例如实现一个日志: //定义一个接口 public interface UserDao{ public void delete(); } //定义一个实现类 public class UserDaoImpl implements UserDao{ public void delete(){ System.out.pritln("成功删除"); } } //定义一个代理类

Java设计模式——代理模式

前言: 上一篇说到了策略模式.单类图上来说,它和本篇要说的代理模式还真是有些像似.都需要一个公共的接口,还有一些实现类.代理类(包装类)封装了一个接口对象,提供客户端调用.这些都很类似.不过,有一个细节需要我们注意一下,那就是这里的代理类也需要去继承这里的公共接口.而在策略模式中,包装类则不需要这么做. 概述: 代理模式就是定义一个原对象的代理对象,来帮助原对象和系统之外的业务作沟通.也就是说,如果我们不能直接或是不愿直接去使用原对象,那么我们就可以使用创建一个原对象的代理来进行操作. 本文链接

代理模式(Proxy)

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问. 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 代理模式一般涉及到的角色有 抽象角色:声明真实对象和代理对象的共工接口 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象.同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装. 真实角色:代理角色所代表的真实对

深刻理解代理模式在java中如何优化系统性能

最近参与实习公司的项目迭代,项目中需要实现系统的快速响应和大量数据处理.在不断的学习中获得一点儿心得,先记录下来.慢慢的修改! 关于代理模式的知识和简单应用之前的学习笔记中已经有记录了,可以复习一下.这里主要记录如何使用代理模式来实现延迟加载,进而提升系统系能和反应速度. 使用代理模式实现延迟加载的一个简单实例: 需求:项目中对系统的启动速度做了一定的要求 我们在系统首次加载时,因为不需要实际的数据来构造显示界面,这就为我们实现系统首次加载的快速响应提供了可能.在常规模式下,我们一般会在系统启动

使用反射机制调用属性和私有成员与代理模式的介绍

使用反射机制调用属性: 通过反射机制可以获得类的属性,获得到的属性同样的可以进行赋值.得值操作,调用getField方法并传递属性的名称可以获得[学Java,到凯哥学堂kaige123.com]指定的属性,调用getFields方法则可以获得全部属性,但是这种方式不能获得私有属性: 代码示例: Student类示例: 运行结果: 从运行结果可以看出只拿出了公开的属性,私有的属性拿不到. 使用反射机制调用私有成员: 1.调用私有属性 在反射机制里调用私有属性需要通过getDeclaredField

动态代理模式

动态代理模式简介: 动态代理能够自动监听代理对象的方法,并且能够自动生成代理类的代码,这样就不需要我们自己去重写代理对象里的方法了,这样解决了代理类代码因业务庞大而庞大的问题,因为动态代理模式会在代码运行时根据代码来在内存中动态生成一个代理类自动重写代理对象[学Java,到凯哥学堂kaige123.com]的方法,然后这个动态代理类再调用处理类里的代码,处理类再调用到实际方法上去.而且我们可以选择性的监听代理对象的方法,有些不需监听的方法就可以在处理类中过滤掉.所以动态代理的优势就在于可以自动的

设计模式(十四):代理模式

一.概述 代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问.其实就是代理就是做到类似转发的功能,针对不同代理,转发的具体实现不同. 二.解决问题 从原理知道他就是控制客户对一个对象的访问,它跟现实中的中介代理类似,只是作为代表做一些受理工作,真正执行的并不是它自己.比如买火车票,外面有很多火车票代理点,我们直接去代理点买票就好而不用跑到火车票买了(暂时不考虑网购哈). 三.结构类图 四.应用实例 在这个例子中,主要讲解远程代理,它可以作为另一个JVM上的本地代表.客户端调用代理的方