• 深入剖析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);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    步骤二、编写接口实现类
    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);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    步骤二、编写接口实现类
    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);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    步骤三、编写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;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    步骤四、编写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("梁舒");

    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    输出:

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

    public Object getProxy() {
    return Proxy.newProxyInstance(Thread.currentThread()
    .getContextClassLoader(), target.getClass().getInterfaces(),
    this);
    }
    1
    2
    3
    4
    5
    点进去, 可以在Proxy 中看到该静态方法的签名:

    public static Object newProxyInstance(ClassLoader loader,
    Class<?>[] interfaces,
    InvocationHandler h)
    throws IllegalArgumentException
    1
    2
    3
    4
    该方法的注释:

    Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.返回一个特定接口的代理类的实例, 代理类对象可以分发method调用到专门的调用处理器。
    1
    待会儿就能理解这句话的含义了。根据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) {
    。。。。。
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    我们接下来把getProxyClass0() 方法逻辑拉出来

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


    return proxyClassCache.get(loader, interfaces);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    可以看到Cache字眼了, 如果缓存中存在代理类, 直接从Cache取出, 否则生成并缓存。这里有意思的是如何进行的缓存? 如何判断的代理类是否是同一个?

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

    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
    1
    2
    3
    观察WeakCache 构造参数, 里面传入了两个工厂实例, 两个工厂均实现了BiFunction 函数接口, KeyFactory 用来生产 缓存 key ,ProxyClassFactory 用来生产字节码 。

    看看WeakCache 类用什么来进行的缓存:


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

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

    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
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    怎么拿到的subKey ?

    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    1
    看下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);
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    怎么拿到的Supplier ? 我们缓存Map 的第二层Map 中的也就是我们的代理类, 存的就是我们下面这个工厂实例, 实现了Supplier , 其实Factroy类 就是对我们缓存Map 第二层Map的一个封装。 只有当我们拿到Supplier调用里面的 get 方法时才会返回真正的代理类。注意当调用 get 方法首先从缓存的第二层Map中取, 如果没有
    那么调用

    value = Objects.requireNonNull(valueFactory.apply(key, parameter));
    1
    其中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);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    待会我们利用这段代码, 来生成一下我们的字节码文件。
    下面就是包装代理类的工厂的代码:

    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;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    有了上面的准备, 下面看: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);
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    到了这里我们捋一捋调用过程:
    首先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); 拿到代理类的包装类。
    缓存总结: 缓存逻辑的key 和value 都是工厂方法产生, 能够学习到怎么利用弱引用来写一个缓存管理器。

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

    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();
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    下面给出了反编译$Proxy0.class 之后的代码。代理类继承了Proxy 实现了HelloService 我们的代理接口
    我们看看$Proxy0 构造函数? 是不是有感觉? 还记得当初留得疑问么

    return cons.newInstance(new Object[]{h});
    1
    其中的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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    好了, 上面的疑问,都解释差不多了。。。
    ---------------------
    作者:lsgqjhAC哈理工
    来源:CSDN
    原文:https://blog.csdn.net/lsgqjh/article/details/68486433
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    java 三目运算符
    增强for 可以用于ArrayList
    shell SORT
    BASH if/while/until loop
    设置临时环境变量
    2015年创业中遇到的技术问题:121-130
    2015年创业中遇到的技术问题:121-130
    JDK8新特性之Lambda表达式
    使用Html5开发Android和iOS应用:HBuilder、Html5Plus、MUI
    使用Html5开发Android和iOS应用:HBuilder、Html5Plus、MUI
  • 原文地址:https://www.cnblogs.com/zhoading/p/10112477.html
Copyright © 2020-2023  润新知