java 动态代理

原创 2018年02月01日 17:28:39

java动态代理的作用?

可以将一个对象A的所有操作代理给另外一个对象B,这样可以在不改变A对象的情况下,通过修改B对象,实现在A对象的方法前方法后加上一些必要的代码,如打印日志。

那么java动态代理代码如何编写?

package com.wzt.www.proxy;

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

/**
 * Created by bfd on 2018/2/1.
 */
public class MyInvocationHandler implements InvocationHandler {

    // 目标对象
    private Object target;

    /**
     * 构造方法
     * @param target 目标对象
     */
    public MyInvocationHandler(Object target) {
        super();
        this.target = target;
    }


    /**
     * 执行目标对象的方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 在目标对象的方法执行之前简单的打印一下
        System.out.println("------------------before------------------");

        // 执行目标对象的方法
        Object result = method.invoke(target, args);

        // 在目标对象的方法执行之后简单的打印一下
        System.out.println("-------------------after------------------");

        return result;
    }

    /**
     * 获取目标对象的代理对象
     * @return 代理对象
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(), this);
    }
}
package com.wzt.www.proxy;

/**
 * Created by bfd on 2018/2/1.
 */
public interface UserService {

    /**
     * 目标方法
     */
    public abstract void add();

}
package com.wzt.www.proxy;

/**
 * Created by bfd on 2018/2/1.
 */
public class UserServiceImpl implements UserService {

    /* (non-Javadoc)
     * @see dynamic.proxy.UserService#add()
     */
    @Override
    public void add() {
        System.out.println("--------------------add---------------");
    }
}
package com.wzt.www.proxy;

import org.junit.Test;

/**
 * Created by bfd on 2018/2/1.
 */
public class ProxyTest {

    @Test
    public void testProxy() throws Throwable {
        // 实例化目标对象
        UserService userService = new UserServiceImpl();

        // 实例化InvocationHandler
        MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);

        // 根据目标对象生成代理对象
        UserService proxy = (UserService) invocationHandler.getProxy();

        // 调用代理对象的方法
        proxy.add();

    }
}
代码写起来灰常简单,关键就是实现InvocationHandler接口,在invoke方法中加上一些逻辑代码,然后使用Proxy.newProxyInstance 方法创建代理类那么java是如何实现的呢?如果让我实现我觉得很简单,就是在jvm运行时动态生成字节码,然后创建对象放在jvm里面,那么jdk是不是这么实现的呢,这就需要读一下Proxy里的源码
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.
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        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;
                }
            });
        }
        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);
    }
}
主要还是看看
Class<?> cl = getProxyClass0(loader, intfs);
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);
}
该方法做了两件事情,
1、校验interfaces的长度
2、从proxyClassCache中取出对象
详细看proxyClassCache.get(loader,interfaces)
proxyClassCache是一个WeakCache类型,也就是说动态代理有缓存机制。接下来看看缓存机制如何实现,也即WeakCache的逻辑
看下类结构,由于懒得整理,此处转载别人总结的

看这个类的结构

//用了Reference记录引用队列,java gc时配合清除缓存用
private final ReferenceQueue<K> refQueue  = new ReferenceQueue<>();
//缓存的底层实现
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
//记录所有缓存中的CacheKey,配合缓存的过期机制
private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>();
//两个二元操作函数
private final BiFunction<K, P, ?> subKeyFactory;
private final BiFunction<K, P, V> valueFactory;

这里最重要的的是map,将key-value介绍如下

字段意义备注
key通过classLoader来进行一级缓存实际类型为CacheKey
value二级缓存后续会介绍

源码中把这个value的变量称为valuesMap,这里顺便借用,介绍这个“二级缓存”是干嘛的

字段意义备注
key二级缓存的key,由classLoader和interfaces[]标识代理类实际类型在java.lang.reflect.Proxy.KeyFactory#apply,为Key1或者Key2或者KeyX或者object,取决于代理类实现的接口数量
value用于返回代理类(即Class<?>)第一次存储实际类型为java.lang.reflect.WeakCache.Factory#Factory,之后取出时,都是java.lang.reflect.WeakCache.CacheValue

那么为什么要用缓存,缓存的内容是什么
缓存就是在动态代理生成代理类时,只用生成一次后面尽量直接复用

3.实现机制

java.lang.reflect.WeakCache#get

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

注意下面几点

3.1 ConcurrentMap的get以及putIfAbsent的使用

可以看到有几处代码,形如

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

这个是为了保证在多线程下,去获取同一个key,拿到的是同一个引用对象
可以理解像单例模式里面要判断两次,来保证不会多次生成对象

if(instance==null) {
  synchronize(XXX.class) {
    if(instance==null){
      instance = new XXX();
    }
  }
}

可以参考http://wxl24life.iteye.com/blog/1746794

3.2 为什么说第一次valuesMap里面value的实际类型和之后不一样

根据Supplier<V> supplier = valuesMap.get(subKey);中supplier值以及在while循环第几次,按照时间先后考虑

3.2.1 supplier==null,第一次while循环

这种出现在第一次请求中,二级缓存没有该key

逻辑为

supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
    // successfully installed Factory
    supplier = factory;
}

此时valuesMap里面的key是subKey
subKey是

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

根据java.lang.reflect.Proxy.KeyFactory#apply
subKey实际类型是Key1,Key2,Object或者KeyX,根据实现的接口数量决定

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

3.2.2 supplier==null,第二次while循环(或者因其他原因,第n次while循环)

这种出现在第一次请求中,二级缓存没有该key,supplier被赋值之后
逻辑为

if (supplier != null) {
  // supplier might be a Factory or a CacheValue<V> instance
  V value = supplier.get();
  if (value != null) {
     return value;
  }
 }

supplier在第一次循环时,已经为factory了,这里调用get方法,就是进入
java.lang.reflect.WeakCache.Factory#get的逻辑

        @Override
        public synchronized V get() { // serialize access
            // re-check
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                //supplier和当前supplier不等,验证不正确
                return null;
            }
            V value = null;
            try {
                //生成代理类对应的Class<?>信息
                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);

            //把之前二级缓存的value替换为当前的CacheValue
            if (valuesMap.replace(subKey, this, cacheValue)) {
                // put also in reverseMap
                reverseMap.put(cacheValue, Boolean.TRUE);
            } else {
                throw new AssertionError("Should not reach here");
            }
            return value;
        }

其中, value = Objects.requireNonNull(valueFactory.apply(key, parameter));就是代理类

java.lang.reflect.Proxy.ProxyClassFactory#apply //返回Class<?>
//具体生成细节参考上一节

在生成value之后

//把当前的值即this(类型为Factory)替换为了cacheValue(类型CacheValue)
valuesMap.replace(subKey, this, cacheValue)

并且这一次get()最终返回的是value;也就是Class<?>

3.2.3 supplier!=null

出现在之后的某一次请求中,二级缓存已经有该key-value

if (supplier != null) {
  // supplier might be a Factory or a CacheValue<V> instance
  V value = supplier.get();
  if (value != null) {
     return value;
  }
 }

此时,supplier的类型为CacheValue
在Factory#get中生成时,就是
CacheValue<V> cacheValue = new CacheValue<>(value);
CacheValue#get方法,最终转入java.lang.ref.Reference#get,返回的就是这个value,即构造函数的参数value

3.3缓存的过期机制

这里用了java.lang.reflect.WeakCache.CacheKey是继承WeakReference的
即弱引用,在java gc中,满足适当的条件时,当一个referent是ROOT可达且为弱引用时,会将其放入对应的referenceQueue。
核心函数在

private void expungeStaleEntries() {
        CacheKey<K> cacheKey;
        while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {
            cacheKey.expungeFrom(map, reverseMap);
        }
    }

可能会奇怪这里并没有refQueue的enqueue操作,这个操作是java gc的时候做的,这里不深究。
只要知道gc时,某些场景会进行refQueue的enqueue即可。
那么,之后再调用expungeStaleEntries()就能清除过期的缓存。

4. 思考

4.1 为什么要二级缓存

一级缓存用来区分classLoader,二级缓存用来区分实现的接口
生成类的缓存是按照ClassLoader来划分的,这是因为类的区分不仅根据类名还根据装载类的ClassLoader,也就是说同一个类被不同的ClassLoader加载,那么它们也是不同的.
参考
http://www.cnblogs.com/cruze/p/3843996.html
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3

4.2 ConcurrentHashMap想要获取SingleValue的方法(严格来说是同一个引用)

如果putIfAbsent返回值不为null,那么返回该值,不要重新put
http://wxl24life.iteye.com/blog/1746794

4.3 二级缓存valueMaps的value的类型不一样

第一次是Factory,调用get()时,生成代理类信息之后,将CacheValue作为值替换了当前key对应的value
之后调用,都是CacheValue.get()返回java.lang.ref.Reference#get,最终就是CacheValue的构造函数参数,即代理类信息Class<?>

4.4 一级缓存的key-value与二级缓存的key-value

4.5 在'背景'中提出的问题,在上文中都有解释

4.6 缓存的过期机制

利用java.lang.reflect.WeakCache.CacheKey继承WeakReference,配合ReferenceQueue使用。
在java gc时某些条件下触发。

5.暂时不明白的问题

5.1 reverseMap的value有什么用?

感觉纯粹是利用ConcurrentHashMap的"Concurrent"的特性,value并没有什么用,代码里面并没有处理里面的value,都是Boolean.TRUE;

5.2 实现二级缓存的逻辑为什么要这么写,不能一步到位吗?

为什么先放入Factory,调用Factory.get时进行replace操作再放入CacheValue,不能一步到位写入CacheValue后面直接用吗



作者:赤子心_d709
链接:https://www.jianshu.com/p/9f5566b5e7fb
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

版权声明:本文为博主原创文章,未经博主允许不得转载。

Java反射机制详细示例及动态代理

反射可以在程序运行过程中动态获取类的相关信息,包括类由哪个类加载器进行加载,类中的成员变量,成员方法,访问修饰符,返回值类型,构造方法等等; 首先要获取类的Class对象.获取Class对象有三种方法...
  • qgfjeahn
  • qgfjeahn
  • 2016年09月30日 10:14
  • 2398

Java动态代理示例代码

Proxy 提供用于创建动态代理类和实例的静态方法。简洁方法:Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),     ...
  • anerou
  • anerou
  • 2007年06月20日 11:35
  • 3735

模拟JDK动态代理 ; 自己动手模拟实现java动态代理

大家在看java设计模式之 代理模式这篇文章的时候, 可以发现动态代理无非就是以下四个步骤,我们完全可以自己模拟实现。因为java的class文件格式是公开的,只要最终生成的class格式正确并且可以...
  • fgyibupi
  • fgyibupi
  • 2017年02月04日 18:59
  • 1671

JAVA动态代理用法与实现过程

jdk动态代理实现已即原理
  • qq1004642027
  • qq1004642027
  • 2016年08月02日 11:33
  • 2085

java动态代理原理及解析

java动态代理, jdk反射与代理模式
  • Scplove
  • Scplove
  • 2016年09月06日 17:25
  • 41447

JAVA 动态代理与AOP编程(面向切面编程)

前奏: 问题:如果我们在编码中遇到三个代码块,在每个代码块中都有相同的一段代码,我们可能会考虑将相同的代码块重构成一个公用的方法,然后再这个代码块中分别调用这个方法,但是又出现一个问题 那就是这三个代...
  • cuigaochong
  • cuigaochong
  • 2015年05月30日 19:07
  • 1138

Java动态代理的两种实现方法

1、定义接口和实现 package com.meituan.hyt.test3.service; /** * Created by heyutao on 15/11/9. */ public i...
  • HEYUTAO007
  • HEYUTAO007
  • 2015年11月09日 15:24
  • 87449

java动态代理实现步骤解析

代理模式的基本目的: 代理模式包含如下角色: ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。 RealSubject:真实主题角色,是实现抽象主题接口的类。 Proxy...
  • yaoxiaoyang
  • yaoxiaoyang
  • 2017年01月04日 20:39
  • 479

匿名内部类和动态代理备忘

匿名内部类 android中仿照xUtils我把http请求的callback回调函数,使用匿名内部类,其中的实现通过annotation注解的形式,关联到外部类的某个方法中。 field.set...
  • mapsong
  • mapsong
  • 2016年02月02日 11:12
  • 635

Java设计模式之—静态代理和动态代理

代理从字面意思来看就是,替代XX去做某事,在我们的程序中,一般替代实际对象去进行操作,扮演着中间人的角色: 客户端 –> 业务类 客户端 –> 代理 –>业务类(代理) 代理接口int...
  • jeffleo
  • jeffleo
  • 2016年08月17日 00:08
  • 633
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:java 动态代理
举报原因:
原因补充:

(最多只允许输入30个字)