深入剖析JDK动态代理源码实现

动态代理、静态代理优缺点

优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。动态代理只有在用到被代理对象的时候才会对被代理类进行类加载。 而静态代理在编译器就已经开始占内存了。。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

弱引用

被弱引用关联的对象只能生存到下一次垃圾收集发生之前。 当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。 在JDK 1.2之后,提供了WeakReference类来实现弱引用。jdk动态代理使用WeakCache 类 对代理类对象进行缓存, 代理类的引用就是采用的虚引用key 继承自WeakReference

借助JDK实现一个动态代理

JDK动态代理步骤

步骤一、编写代理接口

package com.cbam.demo.dynamicProxy;

/**
 * CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd
 * Project: demo
 * Comments:
 * Author:cbam
 * Create Date:2017/3/29
 * Modified By:
 * Modified Date:
 * Modified Reason:
 */
public interface HelloService {
    void sayHello(String name);
}

步骤二、编写接口实现类

package com.cbam.demo.dynamicProxy;

/**
 * CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd
 * Project: demo
 * Comments:
 * Author:cbam
 * Create Date:2017/3/29
 * Modified By:
 * Modified Date:
 * Modified Reason:
 */
public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello " + name);
    }
}

步骤二、编写接口实现类

package com.cbam.demo.dynamicProxy;

/**
 * CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd
 * Project: demo
 * Comments:
 * Author:cbam
 * Create Date:2017/3/29
 * Modified By:
 * Modified Date:
 * Modified Reason:
 */
public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello " + name);
    }
}

步骤三、编写InvocationHandler 实现类

package com.cbam.demo.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd
 * Project: demo
 * Comments:
 * Author:cbam
 * Create Date:2017/3/29
 * Modified By:
 * Modified Date:
 * Modified Reason:
 */
public class ProxyInvocationHandler implements InvocationHandler {
   //要代理的对象
    private Object target;

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

    public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread()
                .getContextClassLoader(), target.getClass().getInterfaces(),
                this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----- before -----");
        Object rtn = method.invoke(target, args);
        System.out.println("----- after -----");
        return rtn;
    }
}

步骤四、编写InvocationHandler 实现类

package com.cbam.demo.dynamicProxy;

import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;

/**
 * CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd
 * Project: demo
 * Comments:
 * Author:cbam
 * Create Date:2017/3/29
 * Modified By:
 * Modified Date:
 * Modified Reason:
 */
public class Main {

    public static void main(String[] args) {
        HelloService helloService = new HelloServiceImpl();
        ProxyInvocationHandler proxyInovationHandler = new ProxyInvocationHandler(helloService);
        HelloService proxy = (HelloService) proxyInovationHandler.getProxy();
        proxy.sayHello("梁舒");

    }
}

输出:

----- before -----
Hello 梁舒
----- after -----

前面代码示例展示了jdk动态代理怎么玩儿。 下面我们来分析一下, jdk底层到底搞了啥就生成了代理类:
首先我们从获取代理类的方法中的Proxy.newProxyInstance

 public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread()
                .getContextClassLoader(), target.getClass().getInterfaces(),
                this);
    }

点进去, 可以在Proxy 中看到该静态方法的签名:

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

该方法的注释:

Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.返回一个特定接口的代理类的实例, 代理类对象可以分发method调用到专门的调用处理器。

待会儿就能理解这句话的含义了。根据javaDoc 得出:
第一个参数为一个类加载器, 就是指定一个类加载器来加载所生成的代理类的字节码而已。
第二个参数为代理类要去实现的接口
第三个参数就是我们所定义的invocation handler 我们毕竟刚才传递的是this
再看整个方法里面的核心逻辑(删除部分校验逻辑):

  @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
    //接口克隆了一份
        final Class<?>[] intfs = interfaces.clone();

        /*
         * Look up or generate the designated proxy class.
         * 查询或着生成指定的代理类, 之所以查询是因为, 之前曾经生成
         * 的代理类可能被缓存了。 在这里只是生成了代理类class对象
         * 而已
         */
        Class<?> cl = getProxyClass0(loader, intfs);
        //在这里根据class对象拿到代理类构造方法
            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;
                    }
                });
            }
    //这里真正的创建了 一个代理对象的实例, 
    //我们留一个疑问, 为什么传过去一个包含h的数组???
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
          。。。。。
    }

我们接下来把getProxyClass0() 方法逻辑拉出来

  private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }


        return proxyClassCache.get(loader, interfaces);
    }

可以看到Cache字眼了, 如果缓存中存在代理类, 直接从Cache取出, 否则生成并缓存。这里有意思的是如何进行的缓存? 如何判断的代理类是否是同一个?

缓存逻辑

继续看下proxyClassCache.get() 方法逻辑, 这个方法在WeakCache 类中实现。而proxyClassCache 只是Proxy 类的一个WeakCache 类组合对象, 在Proxy 类是这样定义的:

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

观察WeakCache 构造参数, 里面传入了两个工厂实例, 两个工厂均实现了BiFunction 函数接口, KeyFactory 用来生产 缓存 keyProxyClassFactory 用来生产字节码 。
这里写图片描述
看看WeakCache 类用什么来进行的缓存:
这里写图片描述

可以看到使用两层ConcurrentHashMap来存的:(key, sub-key) -> value} 其中keyvalue是弱引用, sub-key 是强引用。其中的key 就是我们指定的类加载器。 sub-key 是通过subKeyFactory 工厂方法产生, value 是通过valueFactory 工厂产生, 在上图WeakCache 中都能找到。
怎么获取到key

Object cacheKey = CacheKey.valueOf(key, refQueue);

点开valueOf, 可以看到CacheKeyWeakCache 的一个实现了WeakReference 的静态内部类, 其中有个静态valueOf 方法来产生 我们上面所说的第一个参数keysuper(key, refQueue); 这句话意味着, 将类加载器作为了弱引用的keyrefQueue 表示弱引用队列(会被清理)。

 private static final class CacheKey<K> extends WeakReference<K> {

        private static final Object NULL_KEY = new Object();

        static <K> Object valueOf(K key, ReferenceQueue<K> refQueue) {
            return key == null

                   ? NULL_KEY

                   : new CacheKey<>(key, refQueue);
        }

        private final int hash;

        private CacheKey(K key, ReferenceQueue<K> refQueue) {
            super(key, refQueue);
            this.hash = System.identityHashCode(key);  // compare by identity
        }

怎么拿到的subKey

Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

看下subKeyFactory.apply() 方法代码, 方法实现还是在Proxy 类的KeyFactory 静态内部工厂中。Key1 类并没有对实例的弱引用

 private static final class KeyFactory
        implements BiFunction<ClassLoader, Class<?>[], Object>
    {
        @Override
        public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
            switch (interfaces.length) {
                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);
            }
        }
    }

怎么拿到的Supplier ? 我们缓存Map 的第二层Map 中的也就是我们的代理类, 存的就是我们下面这个工厂实例, 实现了Supplier , 其实Factroy类 就是对我们缓存Map 第二层Map的一个封装。 只有当我们拿到Supplier调用里面的 get 方法时才会返回真正的代理类。注意当调用 get 方法首先从缓存的第二层Map中取, 如果没有
那么调用

 value = Objects.requireNonNull(valueFactory.apply(key, parameter));

其中valueFactory 是我们上面说过的java.lang.reflect.Proxy.ProxyClassFactory 这个工厂类来生产字节码的, 这个类的核心逻辑代码是:

   /*
             * Generate the specified proxy class.
             */
             //生成字节码, 并返回在堆中实例完的对象
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);

待会我们利用这段代码, 来生成一下我们的字节码文件。
下面就是包装代理类的工厂的代码:

private final class Factory implements Supplier<V> {

        private final K key;
        private final P parameter;
        private final Object subKey;
        // 工厂封装的缓存Map的第二个Map
        private final ConcurrentMap<Object, Supplier<V>> valuesMap;

        Factory(K key, P parameter, Object subKey,
                ConcurrentMap<Object, Supplier<V>> valuesMap) {
            this.key = key;
            this.parameter = parameter;
            this.subKey = subKey;
            this.valuesMap = valuesMap;
        }

        @Override
        public synchronized V get() { // serialize access
            // re-check
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {

                return null;
            }

            V value = null;
            try {
            // 这里是缓存中没有找到代理类后所进行的逻辑,上面已经解释
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }

            assert value != null;

            CacheValue<V> cacheValue = new CacheValue<>(value);

            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;
        }
    }

有了上面的准备, 下面看:proxyClassCache.get()


 public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();
// 由上面所说的生成缓存Map的第一个参数key
        Object cacheKey = CacheKey.valueOf(key, refQueue);
//懒惰实例化缓存Map的第二个参数, 其中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;
            }
        }

        //这里拿到subKey(缓存Map中那个subKey) , 来获取可能的  //存储在valuesMap中的Supplier
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        //supplier所指向的实例就是Factroy实例, 包装了第二层Map
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                //有代理类缓存就直接返回
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }

            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }
    //这里的扔进去的supplier 实质上是一个实现了supplier的工厂实例
            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);
                }
            }
        }
    }

到了这里我们捋一捋调用过程:
首先Proxy.newProxyInstance() -> Class<?> cl = getProxyClass0(loader, intfs); -> proxyClassCache.get(loader, interfaces) ->
1、 Object cacheKey = CacheKey.valueOf(key, refQueue); //拿到第一层key
2、Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));// 拿到第二层subKey
3、Supplier<V> supplier = valuesMap.get(subKey); 拿到代理类的包装类。
缓存总结: 缓存逻辑的keyvalue 都是工厂方法产生, 能够学习到怎么利用弱引用来写一个缓存管理器。

回过头,再来看我们生成的代理类

利用上面那段生成字节码的代码, 我们也来生成一下, 看看这个代理类长什么样子:

package com.cbam.demo.dynamicProxy;

import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;

/**
 * CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd
 * Project: demo
 * Comments:
 * Author:cbam
 * Create Date:2017/3/29
 * Modified By:
 * Modified Date:
 * Modified Reason:
 */
public class Main {

    public static void main(String[] args) {
        HelloService helloService = new HelloServiceImpl();
        ProxyInvocationHandler proxyInovationHandler = new ProxyInvocationHandler(helloService);
        HelloService proxy = (HelloService) proxyInovationHandler.getProxy();
        proxy.sayHello("梁舒");

        String path = "D:/$Proxy0.class";
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",
                HelloServiceImpl.class.getInterfaces());
        FileOutputStream out = null;

        try {
            out = new FileOutputStream(path);
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

下面给出了反编译$Proxy0.class 之后的代码。代理类继承了Proxy 实现了HelloService 我们的代理接口
我们看看$Proxy0 构造函数? 是不是有感觉? 还记得当初留得疑问么

return cons.newInstance(new Object[]{h});

其中的m1 m2…..都是我们要代理的那些方法, 可以说我们用代理类调用的方法, 其实都是委托给我们自定义的InvocationHandler 来调用其invoke 方法实现的!而我们每次动态获取代理类其实很可能从缓存中取出来的!

import com.cbam.demo.dynamicProxy.HelloService;
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 HelloService
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;

  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

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

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    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("com.cbam.demo.dynamicProxy.HelloService").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());
    }
  }
}

好了, 上面的疑问,都解释差不多了。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值