JDK的动态代理

313 阅读7分钟

1、什么是代理模式?

所谓代理模式大家在生活中比较明显的可以理解为,当你想买某个商品的时候,不是直接通过跟厂家交易而是通过授权的代理商下面购买,不是每个商品都是瓜子二手车没有中间商赚差价;又比如你想找吴彦祖唱首歌(明星比较忙),我们大多数情况下是通过经纪人找到明星,此时经纪人也可以称为代理者;

Java语言描述:在某些场景下,我们不希望或者无法对一个对象(明星)进行访问时,我们就会采用代理模式,为保持行为的一致性,代理类(经纪人)和委托类(明星)拥有相同的实现接口,在使用委托类的地方都是可以用代理类来替换,可以这样理解委托类负责过程的具体实现,代理类负责过程实现前后的处理.这样能有效的控制对委托类的直接访问;大白话描述,就是你想找个明星唱歌,作为经纪人会知道那个明星唱歌比较符合你的口味,然后推荐给你,经纪人是对明星的技能了如指掌,这就是说明了,具体唱歌还是明星去实现,而请明星唱歌是经过经纪人处理;

在Java中按照编译阶段与运行时产生的代理分为静态代理与动态代理;

2、什么是静态代理?

静态代理就是在编译阶段就生成代理类来完成对代理对象的一系列操作,下面我们通过demo来理解下:

/**
 * 明星的技能  模拟主题接口
 */
public interface IStartSkills {

    /**
     * 唱歌
     */
    void singSong();
}


/**
 * 模拟明星  目标类
 */
public class WuYanZu implements IStartSkills{
    public void singSong() {
        System.out.println("吴彦祖唱了一首歌");
    }
}

/**
 * 经纪人   代理类
 */
public class StartAgent implements IStartSkills {

    public void singSong() {
        WuYanZu wuYanZu = new WuYanZu();
        wuYanZu.singSong();
    }
}

//你想邀请吴彦祖唱歌,只能通过代理类
public class ProxyTest {
    public static void main(String[] args) {
        StartAgent startAgent = new StartAgent();
        startAgent.singSong();
    }
}

result:吴彦祖唱了一首歌

3、什么是动态代理?

所谓动态代理,就是在程序运行时动态的生成代理类; 生成动态代理的方法有很多:例如JDK中自带动态代理,CGlib, javassist等;

Jdk实现动态代理:
public class ProxyTest {
    public static void main(String[] args) {
        /**
         *  public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
         *
         * 参数 1:类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。
         *          一般情况:当前类.class.getClassLoader();
         * 		 * 			目标类实例.getClass().getClassLoader().
         *
         * 参数 2:Class[] interfaces 代理类需要实现的所有接口
         * 		 方式 1:目标类实例.getClass().getInterfaces() 注意:只能获得自己接口,不能获得父元素接口
         * 		 方式 2:new Class[]{UserService.class}
         *
         * 参数 3:InvocationHandler  处理类,接口,必须进行实现类,一般采用匿名内部
         * 		提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke
         * 		 参数 3.1:Object proxy :代理对象
         * 		 参数 3.2:Method method : 代理对象当前执行的方法的描述对象(反射)
         * 		 	执行方法:method.invoke(对象,实际参数)
         * 		 参数 3.3:Object[] args :方法实际参数
         */
        final WuYanZu wuYanZu = new WuYanZu();
        IStartSkills dynamicProxy = (IStartSkills) Proxy.newProxyInstance(wuYanZu.getClass().getClassLoader(), wuYanZu.getClass().getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("吴彦祖唱歌前");
                Object object = method.invoke(wuYanZu, args);
                System.out.println("吴彦祖唱歌之后");
                return object;
            }
        });
        dynamicProxy.singSong();
    }
}

由上可以看出,并未创建代理类却实现了功能,并且在目标类执行前后还可以增强,是不是有点像SpringAop的味道,将目标类作为切点,增强方法作为通知,这样就构成了切面类(代理类),关于SpringAop的知识在后文记录,我们先对Proxy类一探究竟,看看是如何实现动态代理的;

4、动态代理源码解释(资历尚浅,未能深入理解)

/**
* 参数 1:类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。
*          一般情况:当前类.class.getClassLoader();
* 					目标类实例.getClass().getClassLoader().

* 参数 2:Class[] interfaces 代理类需要实现的所有接口数组
 		 方式 1:目标类实例.getClass().getInterfaces() 注意:只能获得自己接口,不能获得父元素接口
		 方式 2:new Class[]{UserService.class}

* 参数 3:InvocationHandler  处理类,接口,必须进行实现类,一般采用匿名内部
*/
@CallerSensitive
  public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
        throws IllegalArgumentException{
        //检查处理类是否为null
        Objects.requireNonNull(h);
	    //对象的拷贝,代理类需要实现的所有接口		
        final Class<?>[] intfs = interfaces.clone();
        //权限安全检查,  sm为null
        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;
            
         //判断代理类的构造函数是否是Public修饰的(反射) 
         	访问非public的方法或者字段都会产生IllegalAccessException异常
            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);
        }
    }
    
从上可以看出,代理类由 getProxyClass0(loader, intfs)产生,然后通过 cons.newInstance(new Object[]{h})将对象实例化(通过构造函数实例化对象)。

getProxyClass0()
// 参数 1 :类加载器
// 参数 2 :代理类需要实现的所有接口数组
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
		// 判断目标类实现的接口数量是否大于65535
		    因为在class文件中,这些个数都是用4位16进制表示的,所以最大值是2的16次方-1
        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
        	否则,它将通过ProxyClassFactory创建代理类
        	proxyClassCache是一个WeakCache类型的缓存,在此就不做过多的介绍,有意思的是get方法并没有用锁进行同步,却实现了线程安全,因为成员变量都使用了ConcurrentMap.....
        return proxyClassCache.get(loader, interfaces);
    }
    
    
proxyClassCache.get()
  public V get(K key, P parameter) {
  		//检查目标类是否有实现接口
        Objects.requireNonNull(parameter);
        //清理持有弱引用的WeakHashMap这种数据结构
        expungeStaleEntries();
        //从队列中获取cacheKey,key为类加载器
        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
         懒加载的方式封装第二层valueMap,ConcurrentMap是一种线程安全的Map
        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
                   调用Supplier类的get()方法实例化对象
                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);
                }
            }
        }
    }
JDK动态代理实现的过程确实很精彩,由于资历尚且,许多东西领悟不够透彻,在此推荐几篇文章:
(反射的理解)   https://zhuanlan.zhihu.com/p/80519709
(动态代理源码解析)  https://blog.csdn.net/u014427391/article/details/75460032
(动态代理缓存机制)  https://www.cnblogs.com/liuyun1995/p/8144676.html

鄙人不才,在您面前献丑只愿与您结伴而行,文章若有不当之处,望大佬指点一二;如果我对您有帮助的话,还希望您能点赞分享,成长是一场苦涩的独自修行,我很需要您的陪伴与支持,这也是鄙人不断前行的根本动力,让我们在互相陪伴见证彼此生长的同时能够感染身边最亲近的人一同成长,鄙人在此叩谢!