zoukankan      html  css  js  c++  java
  • 浅析代理模式

    概述

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

    角色划分
    - 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源代码设计模式解析与实战》已经出版,购买链接

  • 相关阅读:
    Deepin v20 beta更新后没有无线网卡
    Java操作Poi--word图表中颜色的设置
    IDEA配置之CopyRight
    Oracle——日期函数,时间操作(汇总)
    IDEA插件之alibaba编程规范
    IDEA插件之PMD
    IDEA插件之CheckStyle
    IDEA插件之FindBugs
    小计--------flume 自定义kafka source 案例
    记一次--------phoenix创建表映射到hbase报错
  • 原文地址:https://www.cnblogs.com/wzzkaifa/p/7203985.html
Copyright © 2011-2022 走看看