ダイナミックエージェントソース解析


  • jdk 1.8.0に基づく分析
  • 動的エージェントDemo
    /**
     *      
     * Created by blacktoast on 2018/1/3.
     */
    
    public interface ProxiedObject {
        public void doSomething1();
        public void doSomething2();
    }
    import android.util.Log;
    
    /**
     * Created by blacktoast on 2018/1/3.
     */
    
    public class ConcreteProxiedObject implements ProxiedObject {
        @Override
        public void doSomething1() {
            Log.e("TAG", "doSomething1");
        }
    
        @Override
        public void doSomething2() {
            Log.e("TAG", "doSomething2");
        }
    }
    import android.util.Log;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * Created by blacktoast on 2018/1/3.
     */
    
    public class ProxyHandler implements InvocationHandler {
        private Object proxied;
    
        public ProxyHandler(Object object) {
            this.proxied =object;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Log.e("TAG", "do something before proxied object invoked.");
            Object invoke = method.invoke(proxied, args);
            Log.e("TAG", "do something after proxied object invoked.");
            return invoke;
        }
    }
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    
    import com.example.yinhua.aoptest.dl.ConcreteProxiedObject;
    import com.example.yinhua.aoptest.dl.ProxiedObject;
    import com.example.yinhua.aoptest.dl.ProxyHandler;
    
    import java.lang.reflect.Proxy;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            testDyProxy();
        }
    
        public void testDyProxy() {
            ConcreteProxiedObject concreteProxiedObject = new ConcreteProxiedObject();
            ProxiedObject proxiedObject = (ProxiedObject) Proxy.newProxyInstance(ProxiedObject.class.getClassLoader(),
                    new Class[]{ProxiedObject.class},//   concreteProxiedObject.getClass().getInterfaces()
                    new ProxyHandler(concreteProxiedObject));
            proxiedObject.doSomething1();
            Log.e("TAG", "here:" + proxiedObject.getClass().getName());
        }
    
    }
    Log  :
    E/TAG: do something before proxied object invoked.
    E/TAG: doSomething1
    E/TAG: do something after proxied object invoked.
    E/TAG: here:$Proxy0

    注意事項
  • Interface修飾子はpublic修飾である必要があります.public修飾がない場合に生成されるclas.getName()は異なります.
  • で生成するエージェントクラスのバイトコードは、仮想マシンの実行時に動的に生成する
  • である.
  • 被エージェントオブジェクトはインタフェースを実現する必要があり、実質的にインタフェースのエージェント(私はそう理解している)
  • である.
    ダイナミックエージェントメカニズム
  • 動的プロキシプロセッサ/スケジューラInvocationHandler
  • を生成
  • Proxy静的手法によりプロキシオブジェクト
  • を生成する.
    ソース分析(==分析を注釈に書いた===)
  • Proxy.newProxyInstanceソース分析
  • ClassLoader、Interfaceでプロキシクラスタイプオブジェクトを生成します.生成エージェントインスタンスの反射(Reflection Generation Agent Instance)
    public static Object newProxyInstance(ClassLoader loader,
                                              Class>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
    
            //   Interface
            final Class>[] intfs = interfaces.clone();
            // Android-changed: sm is always null
            // final SecurityManager sm = System.getSecurityManager();
            // if (sm != null) {
            //     checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            // }
    
            /*
             * Look up or generate the designated proxy class.
             *   ClassLoader、Interface       
             * ------>           
             */
            Class> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                // Android-changed: sm is always null
                // if (sm != null) {
                //     checkNewProxyPermission(Reflection.getCallerClass(), cl);
                // }
    
                //           
                //             
                final Constructor> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                //        public        
                if (!Modifier.isPublic(cl.getModifiers())) {
                    // Android-changed: Removed AccessController.doPrivileged
                    cons.setAccessible(true);
                }
                //     
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }
  • getProxyClass 0ソース分析
  • Class
        private static Class> getProxyClass0(ClassLoader loader,
                                               Class>... interfaces) {
            //             65535    ,     ,   
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
    
            // If the proxy class defined by the given loader implementing
            // the given interfaces exists, this will simply return the cached copy;
            // otherwise, it will create the proxy class via the ProxyClassFactory
            return proxyClassCache.get(loader, interfaces);
        }
  • proxyClassCache分析
  • new KeyFactory()とnew ProxyClassFactory()の2つのオブジェクトは、すべてのエージェントクラスのマッピングを処理します.
        /**
         * a cache of proxy classes
         *           
         *   :new KeyFactory()、new ProxyClassFactory()     ,      
         */
        private static final WeakCache[], Class>>
            proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
  • WeakCache getメソッド解析
  • mapによってClassLoaderに対応するマッピング関係valuesMapを取得する.valuesMapにはKeyとFactory間のマッピングが格納されます.Factory.get()メソッドは、ValueFactoryによってエージェントオブジェクトを取得する論理をカプセル化します.
        /**
         * WeakCache.get(loader, interfaces)
         */
        public V get(K key, P parameter) {
            Objects.requireNonNull(parameter);
    
            expungeStaleEntries();
    
            Object cacheKey = CacheKey.valueOf(key, refQueue);
    
            //   key(ClassLoader)      , CacheKey Factory     
            // lazily install the 2nd level valuesMap for the particular cacheKey
            ConcurrentMap> valuesMap = map.get(cacheKey);
            if (valuesMap == null) {
                //        put map 
                ConcurrentMap> oldValuesMap
                    = map.putIfAbsent(cacheKey,
                                      valuesMap = new ConcurrentHashMap<>());
                if (oldValuesMap != null) {
                    //   map  valuesMap    oldValuesMap
                    valuesMap = oldValuesMap;
                }
            }
    
            //   KeyFactory  Key,Key     interface hashcode    
            // create subKey and retrieve the possible Supplier stored by that
            // subKey from valuesMap
            Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
            //   Supplier  ,       ValueFactory         
            Supplier supplier = valuesMap.get(subKey);
            Factory factory = null;
    
            while (true) {
                if (supplier != null) {
                    // supplier might be a Factory or a CacheValue instance
                    //          
                    V value = supplier.get();
                    if (value != null) {
                        return value;
                    }
                }
                // else no supplier in cache
                // or a supplier that returned null (could be a cleared CacheValue
                // or a Factory that wasn't successful in installing the CacheValue)
    
                // lazily construct a Factory
                if (factory == null) {
                    factory = new Factory(key, parameter, subKey, valuesMap);
                }
    
                //   Supplier   Factory  ,    Key Factory     
                if (supplier == null) {
                    supplier = valuesMap.putIfAbsent(subKey, factory);
                    if (supplier == null) {
                        // successfully installed Factory
                        supplier = factory;
                    }
                    // else retry with winning supplier
                } else {
                    if (valuesMap.replace(subKey, supplier, factory)) {
                        // successfully replaced
                        // cleared CacheEntry / unsuccessful Factory
                        // with our Factory
                        supplier = factory;
                    } else {
                        // retry with current supplier
                        supplier = valuesMap.get(subKey);
                    }
                }
            }
        }
  • Factoryソース分析
  • Factoryによるプロキシオブジェクトの生成
    private final class Factory implements Supplier<V> {
    
            private final K key;
            private final P parameter;
            private final Object subKey;
            private final ConcurrentMap> valuesMap;
    
            Factory(K key, P parameter, Object subKey,
                    ConcurrentMap> valuesMap) {
                this.key = key;
                this.parameter = parameter;
                this.subKey = subKey;
                this.valuesMap = valuesMap;
            }
    
            @Override
            public synchronized V get() { // serialize access
                // re-check
                Supplier supplier = valuesMap.get(subKey);
                if (supplier != this) {
                    // something changed while we were waiting:
                    // might be that we were replaced by a CacheValue
                    // or were removed because of failure ->
                    // return null to signal WeakCache.get() to retry
                    // the loop
                    return null;
                }
                // else still us (supplier == this)
    
                // create new value
                V value = null;
                try {
                    //   ValueFactory        
                    value = Objects.requireNonNull(valueFactory.apply(key, parameter));
                } finally {
                    if (value == null) { // remove us on failure
                        valuesMap.remove(subKey, this);
                    }
                }
                // the only path to reach here is with non-null value
                assert value != null;
    
                // wrap value with CacheValue (WeakReference)
                //            ,CacheValue  Supplier     hashcode equals  ,       ,    
                CacheValue cacheValue = new CacheValue<>(value);
    
                // try replacing us with CacheValue (this should always succeed)
                if (valuesMap.replace(subKey, this, cacheValue)) {
                    // put also in reverseMap
                    reverseMap.put(cacheValue, Boolean.TRUE);
                } else {
                    throw new AssertionError("Should not reach here");
                }
    
                // successfully replaced us with new CacheValue -> return the value
                // wrapped by it
                return value;
            }
        }
  • KeyFactoryソース分析
  •     private static final class KeyFactory
            implements BiFunction<ClassLoader, Class>[], Object>
        {
            @Override
            public Object apply(ClassLoader classLoader, Class>[] interfaces) {
                switch (interfaces.length) {
                    //   Key,Key      Object  
                    case 1: return new Key1(interfaces[0]); // the most frequent
                    case 2: return new Key2(interfaces[0], interfaces[1]);
                    case 0: return key0;
                    default: return new KeyX(interfaces);
                }
            }
        }
  • ProxyFactory(ValueFactory)ソースコード分析
  • private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class>[], Class>>
        {
            // prefix for all proxy class names
            private static final String proxyClassNamePrefix = "$Proxy";
    
            // next number to use for generation of unique proxy class names
            private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
            @Override
            public Class> apply(ClassLoader loader, Class>[] interfaces) {
    
                Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
                for (Class> intf : interfaces) {
                    /*
                     * Verify that the class loader resolves the name of this
                     * interface to the same Class object.
                     */
                    Class> interfaceClass = null;
                    try {
                        interfaceClass = Class.forName(intf.getName(), false, loader);
                    } catch (ClassNotFoundException e) {
                    }
    
                    if (interfaceClass != intf) {
                        throw new IllegalArgumentException(
                            intf + " is not visible from class loader");
                    }
                    /*
                     * Verify that the Class object actually represents an
                     * interface.
                     */
                    //        ,      
                    //             java      
                    if (!interfaceClass.isInterface()) {
                        throw new IllegalArgumentException(
                            interfaceClass.getName() + " is not an interface");
                    }
                    /*
                     * Verify that this interface is not a duplicate.
                     */
                    if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                        throw new IllegalArgumentException(
                            "repeated interface: " + interfaceClass.getName());
                    }
                }
    
                String proxyPkg = null;     // package to define proxy class in
                int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
                /*
                 * Record the package of a non-public proxy interface so that the
                 * proxy class will be defined in the same package.  Verify that
                 * all non-public proxy interfaces are in the same package.
                 */
                for (Class> intf : interfaces) {
                    int flags = intf.getModifiers();
                    //   interface  public
                    //   public   ,         +  $Proxy1
                    if (!Modifier.isPublic(flags)) {
                        accessFlags = Modifier.FINAL;
                        String name = intf.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) {
                    // if no non-public proxy interfaces, use the default package.
                    proxyPkg = "";
                }
    
                //       java    ,java       com.sun.proxy$Proxy1, Android       $Proxy1
                {
                    // Android-changed: Generate the proxy directly instead of calling
                    // through to ProxyGenerator.
                    List methods = getMethods(interfaces);
                    Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
                    validateReturnTypes(methods);
                    List[]> exceptions = deduplicateAndGetExceptions(methods);
    
                    Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                    Class>[][] exceptionsArray = exceptions.toArray(new Class>[exceptions.size()][]);
    
                    /*
                     * Choose a name for the proxy class to generate.
                     */
                    // $Proxy       ,           1
                    //  nextUniqueNumber     1,          
                    long num = nextUniqueNumber.getAndIncrement();
                    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
                    //           , native   
                    return generateProxy(proxyName, interfaces, loader, methodsArray,
                                         exceptionsArray);
                }
            }
        }