zoukankan      html  css  js  c++  java
  • JDK动态代理

    在《springAOP之代理模式》中说了代理模式,包含静态代理和动态代理,在动态代理模式中又分为JDK动态代理和CGlib动态代理,今天重点来看JDK动态代理。

    一、概述

    说到JDK动态代理就必须想到JDK动态代理要求有一个统一的接口,那为什么要有接口,下面会说到,下面看我的接口类,

    package cn.com.jdk.proxy;
    
    public interface Subject {
    
        void sayHello(String a);
    }

    接口类很简单就是一个简单的方法定义。下面看实际的接口的实现类SubjectImpl,

    package cn.com.jdk.proxy;
    
    public class SubjectImpl implements Subject {
    
        @Override
        public void sayHello(String a) {
            // TODO Auto-generated method stub
    
            System.out.println("hello:"+a);
        }
    
    }

    实现类简单的事项了Subject接口,进行了打印操作。下面看代理类

    package cn.com.jdk.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class JDKProxy implements InvocationHandler {
        private SubjectImpl si;
        //此属性不用管
        private String a;
    /**
     * proxy  JDK动态生成的代理类的实例
     * method 目标方法的Method对象     Class.forName("cn.com.jdk.proxy.Subject").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
     * args   目标方法的参数                       new Object[] { paramString }
     */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // TODO Auto-generated method stub
            System.out.println("before");
            //使用反射的放式调用si(被代理类)目标方法
            Object o=method.invoke(si, args);
            System.out.println("after");
            return o;
        }
        public JDKProxy(SubjectImpl si,String a) {
            this.si=si;
            this.a=a;
        }
    
    }

    上面是代理类的实现,在代理类中含义被代理类的一个引用,且提供了响应的构造方法。下面具体的使用,

    package cn.com.jdk.proxy;
    
    import java.lang.reflect.Proxy;
    
    public class ProxyTest {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            //进行此项设置,可以在项目的com/sun/proxy目录下找到JDK动态生成的代理类的字节码文件
            System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
            SubjectImpl si=new SubjectImpl();
            
            Subject subject=(Subject)Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), new JDKProxy(si,"111"));
            subject.sayHello("tom");
    
        }
    
    }

    上面是使用的代码,通过Proxy类的newProxyInstance方法获得一个Subject的实例,调用sayHello方法,下面看执行结果

    before
    hello:tom
    after

    可以看到执行了sayHello方法,且打印了before和after,这不正是代理类中invoke方法的执行吗,看下面

    很神奇的一件事,我们不光调用了sayHello方法,实现了打印,而且在加入了自己的打印方法,这不正是AOP的增强功能吗。这一切是怎么发生的那,下面细细说来。

    二、详述

    上面,我们又复习了JDK动态代理的内容,以及演示了如何使用JDK动态代理,下面我们要看这是怎么实现的,先从测试的下面这段代码说起,也是最重要的代码,JDK动态代理的精华都在这句代码里,

    Subject subject=(Subject)Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), new JDKProxy(si,"111"));

    这句代码是调用了Proxy类的newProxyInstance方法,此方法的入参如下,

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)

     一共三个参数,一个是ClassLoader,这里传入的是被代理对象的类加载器;一个是Class,这里传入的是被代理对象所实现的接口;一个是InvocationHandler,这里传入的是代理类,代理类实现了InvocationHandler接口。

     1、newProxyInstance方法

    下面看newProxyInstance方法的定义,

    @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            /*
             * Look up or generate the designated proxy class.
             */
              //1、使用代理类的类加载器和其所实现的接口,动态生成代理类
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
                //2、返回JDK生成的代理类的构造方法,该构造方法的参数为
                //  InvocationHandler
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
    //3、返回该构造方法的一个实例,也就是使用InvocationHandler为参数的构造方法利用反射的机制返回一个实例。
    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); } }

    该方法中有三步比较重要,上面的注释已经标出。

    1.1、getProxyClass0(loader, intfs)方法

    该方法便是上面的第一步,这一步的作用是JDK返回一个代理类的实例,方法上的注释如下,

    /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, intfs);

    注释直译过来是查找或者生成指定的代理类,这里有两层意思,一个是查找,第二个是生成,由此可以想到这个方法中应该有缓存,下面看方法的具体定义,

    /**
         * Generate a proxy class.  Must call the checkProxyAccess method
         * to perform permission checks before calling this.
         */
        private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            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);
        }

    这个方法很简单,判断了接口的数量,大于65535便抛异常,接口的数量大于65535的可能性不大。最后调用了proxyClassCache的get方法,首先看proxyClassCache,从字面上理解是代理类的缓存,看其定义,

    /**
         * a cache of proxy classes
         */
        private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
            proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    是一个WeakCache对象实例,看下该构造方法,

    /**
         * Construct an instance of {@code WeakCache}
         *
         * @param subKeyFactory a function mapping a pair of
         *                      {@code (key, parameter) -> sub-key}
         * @param valueFactory  a function mapping a pair of
         *                      {@code (key, parameter) -> value}
         * @throws NullPointerException if {@code subKeyFactory} or
         *                              {@code valueFactory} is null.
         */
        public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                         BiFunction<K, P, V> valueFactory) {
            this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
            this.valueFactory = Objects.requireNonNull(valueFactory);
        }

    看了该类的构造方法后,回到proxyClassCache.get(loader, interfaces)方法的调用,我们已经知道proxyClassCache是WeakCache的一个实例,那么get方法如下,

     /**
         * Look-up the value through the cache. This always evaluates the
         * {@code subKeyFactory} function and optionally evaluates
         * {@code valueFactory} function if there is no entry in the cache for given
         * pair of (key, subKey) or the entry has already been cleared.
         *
         * @param key       possibly null key
         * @param parameter parameter used together with key to create sub-key and
         *                  value (should not be null)
         * @return the cached value (never null)
         * @throws NullPointerException if {@code parameter} passed in or
         *                              {@code sub-key} calculated by
         *                              {@code subKeyFactory} or {@code value}
         *                              calculated by {@code valueFactory} is null.
         */
        public V get(K key, P parameter) {
            Objects.requireNonNull(parameter);
    
            expungeStaleEntries();
    
            Object cacheKey = CacheKey.valueOf(key, refQueue);
    
            // lazily install the 2nd level valuesMap for the particular cacheKey
            ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
            if (valuesMap == null) {
                ConcurrentMap<Object, Supplier<V>> oldValuesMap
                    = map.putIfAbsent(cacheKey,
                                      valuesMap = new ConcurrentHashMap<>());
                if (oldValuesMap != null) {
                    valuesMap = oldValuesMap;
                }
            }
    
            // create subKey and retrieve the possible Supplier<V> stored by that
            // subKey from valuesMap
            Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
            Supplier<V> supplier = valuesMap.get(subKey);
            Factory factory = null;
    
            while (true) {
                if (supplier != null) {
                    // supplier might be a Factory or a CacheValue<V> 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);
                }
    
                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);
                    }
                }
            }
        }

     上面是WeakCache的get方法,这个方法暂时不作说明,后面会详细介绍WeakCache类,请参见《JDK动态代理之WeakCache 》。这里只需记住该get方法会返回一个代理类的实例即可。那么此代理类是如何定义的那?

    1.1.1、$Proxy0.class代理类

    这个代理类是JDK动态生成的,其命名规则为以“$”开头+Proxy+“从0开始的序列”。上面在测试的时候,我们加入了下面这行代码,

    //进行此项设置,可以在项目的com/sun/proxy目录下找到JDK动态生成的代理类的字节码文件
            System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

    注释中写到可以生成代理类的字节码文件,下面是使用反编译工具过来的java代码,

    package com.sun.proxy;
    
    import cn.com.jdk.proxy.Subject;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy
      implements Subject
    {
      private static Method m1;
      private static Method m3;
      private static Method m2;
      private static Method m0;
     //参数为InvocationHandler的构造方法
      public $Proxy0(InvocationHandler paramInvocationHandler)
        throws 
      {
       //调用父类Proxy的构造方法,在父类的构造方法中会初始化h属性
        super(paramInvocationHandler);
      }
    
      public final boolean equals(Object paramObject)
        throws 
      {
        try
        {
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    //实现的Subject的sayHello方法
      public final void sayHello(String paramString)
        throws 
      {
        try
        {
          //调用h的invoke方法,这里的h指的是实现了InvocationHandler的类
          //调用其中的invoke方法,在本例中是调用JDKProxy类中的invoke方
          //
          this.h.invoke(this, m3, new Object[] { paramString });
          return;
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final String toString()
        throws 
      {
        try
        {
          return (String)this.h.invoke(this, m2, null);
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final int hashCode()
        throws 
      {
        try
        {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("cn.com.jdk.proxy.Subject").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
        }
        throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
      }
    }

    上面是反编译过来的JDK生成的代理类的代码,包含了一个使用InvocationHandler作为参数的构造方法,以及实现了Subject接口的sayHello方法。上面注释中写到该构造方法调用了其父类Proxy的构造方法,下面看其父类Proxy的构造方法,

    protected Proxy(InvocationHandler h) {
            Objects.requireNonNull(h);
            this.h = h;
        }

    把InvocationHandler的值赋给了h,h的定义如下,

    protected InvocationHandler h;

    那么在生成的代理类中自然会继承该属性,所以在代理类中的sayHello中使用下面的方法调用,

    public final void sayHello(String paramString)
        throws 
      {
        try
        {
          this.h.invoke(this, m3, new Object[] { paramString });
          return;
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }

    上面的this.h便是其父类的h属性。在上面的this.h.invoke中的m3是怎么来的那,看下面,

     static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("cn.com.jdk.proxy.Subject").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
        }
        throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
      }

    在该类的静态代码块中给出了4个属性。

    1.2、getConstructor(constructorParams)方法

    在上面的getProxyClass0方法中我们知道该方法会返回一个JDK生成代理类的Class对象,此类的定义便是上面的$Proxy0.class类。其定义在上面已经分析过。getConstructor方法要返回一个以constructorParams为参数的构造方法,

    @CallerSensitive
        public Constructor<T> getConstructor(Class<?>... parameterTypes)
            throws NoSuchMethodException, SecurityException {
            checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
            return getConstructor0(parameterTypes, Member.PUBLIC);
        }

    调用了getConstuctor0方法返回一个public的构造方法,

    private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                            int which) throws NoSuchMethodException
        {
            Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
            for (Constructor<T> constructor : constructors) {
                if (arrayContentsEq(parameterTypes,
                                    constructor.getParameterTypes())) {
                    return getReflectionFactory().copyConstructor(constructor);
                }
            }
            throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
        }

    上面的方法会返回一个public的构造方法。

    回到最初的调用,我们看getConstructor方法的参数是constructorParams,此属性定义如下,

    /** parameter types of a proxy class constructor */
        private static final Class<?>[] constructorParams =
            { InvocationHandler.class };

    是一个Class数组,其类型为InvocationHandler。这样便可以知道是通过代理类的Class对象返回其构造方法cons。有了构造方法下面便是通过构造方法生成实例。

    1.3、cons.newInstance(new Object[]{h})方法

    此方法便是通过构造方法返回一个代理类的实例。

    上面分析了Proxy的newProxyInstance方法,此方法最终会返回一个代理类的实例,会经过下面几个步骤,

    从上面的步骤,我们知道在获得代理类的构造方法时,是获得其参数为InvocationHandler的构造方法,所以肯定要实现InvocationHandler接口,在本例中便是JDKProxy类,这个类实现了这个接口。值开篇我们讲到JDK动态代理必须要有统一的接口,从上面的步骤中我们知道在生成代理类的Class对象时使用了两个参数,一个ClassLoader,另一个是接口,这里就是为什么要有统一的接口,因为在生成代理类的Class对象中需要接口,所以被代理类必须要有一个接口。

    2、方法调用

    这里的方法调用,便是对应使用方法中的下面这行代码,

    subject.sayHello("tom");

    在上面的分析中获得了一个代理类的实例,即下面这行代码,

    Subject subject=(Subject)Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), new JDKProxy(si,"111"));

    通过使用被代理类的类加载器、被代理类所实现的接口、实现了InvocationHandler接口的类的实例三个参数,返回了一个代理类的实例。上面已经详细分析过。此代理类的实例继承了Proxy,实现了Subject接口。其sayHello方法如下,

    public final void sayHello(String paramString)
        throws 
      {
        try
        {
          this.h.invoke(this, m3, new Object[] { paramString });
          return;
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }

    上面已经分析过,this.h是InvocationHandler的实例,这里便是new JDKProxy(si,"111"),m3是m3 = Class.forName("cn.com.jdk.proxy.Subject").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });下面看JDKProxy中的invoke方法,

    @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // TODO Auto-generated method stub
            System.out.println("before");
            //使用反射的放式调用目标方法
            Object o=method.invoke(si, args);
            System.out.println("after");
            return o;
        }

    此方法的三个参数分别为代理类的实例、Method对象(sayHello),调用sayHello时的参数,所以要调用被代理类的sayHello方法,需要这样写:method.invoke(si,args),即调用被代理类(SubjectImpl)的sayHello方法,参数为args(tom)。下面是一个简单的方法调用过程,

    三、总结

    本文分析了JDK动态代理的简单使用方法及背后的原理,有不当之处欢迎指正,感谢!

  • 相关阅读:
    Codeforces467C George and Job
    Codeforces205E Little Elephant and Furik and RubikLittle Elephant and Furik and Rubik
    Codeforce205C Little Elephant and Interval
    51nod1829 函数
    51nod1574 排列转换
    nowcoder35B 小AA的数列
    Codeforce893E Counting Arrays
    gym101612 Consonant Fencity
    CodeForces559C Gerald and Giant Chess
    CodeForces456D A Lot of Games
  • 原文地址:https://www.cnblogs.com/teach/p/13143430.html
Copyright © 2011-2022 走看看