【Java】从源码分析动态代理机制

原创 2016年05月31日 22:10:13

使用动态代理机制的原因有很多,抛开这些原因的讲述,我们先来看看动态代理的实现原理,看看你能不能从中有所启迪:
首先我们来定义一个表示房子的接口,里面有一个函数可以输出这个房子的颜色:

public interface House {

    void Color();
}

接下来我们定义一个继承自该接口的实例:

public class RedHouse implements House{

    public void Color(){
        System.out.print("red");
    }
}

接下来定义一个handler:

public class HouseHandler implements InvocationHandler{

    RedHouse redHouse;
    public HouseHandler(RedHouse redHouse){
        this.redHouse = redHouse;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable{
        return method.invoke(redHouse,args);
    }
}

把handler里绑定上RedHouse:

public class Client {
    public static void main(String args[]){

        RedHouse redHouse = new RedHouse();

        HouseHandler houseHandler = new HouseHandler(redHouse);

        //绑定代理
        House house = (House) Proxy.newProxyInstance(
                House.class.getClassLoader(),
                new Class<?>[]{House.class},
                houseHandler);
        //调用一下试试看
        house.Color();

    }
}

输出结果为:
red
至此一个简单的动态代理就做完了,下面我们来分析一下绑定代理的那几行究竟做了那些工作:

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
            throws IllegalArgumentException
    {
        //判断handler为空
        if (h == null) {
            throw new NullPointerException();
        }

        Class<?> cl = getProxyClass0(loader, interfaces);

代码到这里我们可以看到调用了一个getProxyClass0这个方法,这个方法可以说是整个动态代理的核心,我们来详细地分析一下:
首先,它对参数进行了严格的检查,包括检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是否完全相同,确保是interface类型等等,代码中都有详细描述,最后会得到一个包含所有接口名称的字符串数组interfaceNames。

        //接口个数超出限制
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        Class<?> proxyClass = null;
        String[] interfaceNames = new String[interfaces.length];
        Set<Class<?>> interfaceSet = new HashSet<>();

        //循环对每个接口进行检查,检查完了之后会得到一个包含所有接口名字的数组
        for (int i = 0; i < interfaces.length; i++) {

            //就是判断类是否对loader可见,先判断有没有,再判断是不是一样的
            String interfaceName = interfaces[i].getName();
            Class<?> interfaceClass = null;
            try {
                //是否能拿到这个类
                interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) {
            }
            //类装载器是否对这个类可见
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(
                        interfaces[i] + " is not visible from class loader");
            }
            //接口类型的检查
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
            }
            //接口类重复
            if (interfaceSet.contains(interfaceClass)) {
                throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass);
            interfaceNames[i] = interfaceName;
        }

接下来我们按照loader从loaderToCache映射表中取出cache(一个hashmap<接口名字列表,代理类>):

        Map<List<String>, Object> cache;
        synchronized (loaderToCache) {
            cache = loaderToCache.get(loader);
            if (cache == null) {
                cache = new HashMap<>();
                loaderToCache.put(loader, cache);
            }

下面这部分我也不是太懂是干嘛的。。。。

        synchronized (cache) {
            do {
                //拿到接口列表对应的代理类
                Object value = cache.get(key);
                //下面这两行没怎么看懂,哪位大神要是知道赐教一下
                if (value instanceof Reference) {
                    proxyClass = (Class<?>) ((Reference) value).get();
                }
                //标志着已经被创建了,直接返回
                if (proxyClass != null) {
                    return proxyClass;
                } else if (value == pendingGenerationMarker) {
                    // 代理类正在被创建
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
                    }
                    // 等待被唤醒,继续循环并通过二次检查以确保创建完成,否则重新等待
                    continue;
                } else {
                    //新来的,标志为正在被创建,插入进去
                    cache.put(key, pendingGenerationMarker);
                    break;
                }
            } while (true);
        }

接下来就是正式生成这个代理类了,首先是确定包名:

String proxyPkg = null;
            //按照接口类型进行一个小小的判断,public的包名就设为空,其他的包名就是接口类的包名
            //如果非public有不通同包,抛异常
            for (int i = 0; i < interfaces.length; i++) {
                int flags = interfaces[i].getModifiers();
                if (!Modifier.isPublic(flags)) {
                    String name = interfaces[i].getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                    }
                }
            }
            if (proxyPkg == null) {
                // 全是public的设为默认的包名
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

然后是确定类名,这里用了一个小技巧,用num来使每次生成的类名都不同:

 long num;
                synchronized (nextUniqueNumberLock) {
                    num = nextUniqueNumber++;
                }
                //生成代理类的类名
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

有了包名和类名,接下来正式的生成这个代理类:

 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                        proxyName, interfaces);
                try {
                    proxyClass = defineClass0(loader, proxyName,
                            proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    throw new IllegalArgumentException(e.toString());
                }
            }
            proxyClasses.put(proxyClass, null);

遗憾的是ProxyGenerator这个类sun公司并未公开,所以我也不知道它是怎么生成的。
接下来根据结果更新缓存表,如果成功则将代理类的类对象引用更新进缓存表,否则清楚缓存表中对应关键值,最后唤醒所有可能的正在等待的线程

            synchronized (cache) {
                if (proxyClass != null) {
                    cache.put(key, new WeakReference<Class<?>>(proxyClass));
                } else {
                    cache.remove(key);
                }
                cache.notifyAll();
            }

至此一个代理类已经生成完毕。
其他的牵扯到太多关于安全方面检查的代码太烦了,等有时间再看看吧。

相关文章推荐

Hadoop源码解析之java动态代理机制

在前一篇文章Hadoop源码解析之YARN客户端作业提交流程中,介绍了创建客户端代理阶段用到Java中的动态代理:  protected void serviceStart() throws Exce...

java 动态代理机制综合分析

  • 2011年04月29日 23:35
  • 929KB
  • 下载

JAVA 动态代理(proxy)的实现和源码分析

http://blog.csdn.net/mantantan/article/details/51873755 JDK动态代理(proxy)可以在运行时创建一个实现一组给定接口的新类。但...

Java 动态代理源码分析

Java 动态代理 本文为 Android 开源项目源码解析 公共技术点中的 动态代理 部分 项目地址:Jave Proxy,分析的版本:openjdk 1.6,Demo 地址:Proxy...
  • jQuerys
  • jQuerys
  • 2015年08月31日 17:04
  • 1815

JAVA 动态代理(proxy)的实现和源码分析

JDK动态代理(proxy)可以在运行时创建一个实现一组给定接口的新类。但是略有限制,即被代理的类必须实现某个接口,否则无法使用JDK自带的动态代理,因此,如果不满足条件,就只能使用另一种更加灵活,功...

Java动态代理的源码分析;Proxy与InvocationHandler

代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问。 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念。 代理模式示例代码 pub...

jdk 源码分析(21)java 动态代理和反射

java动态代理用在很多地方。spring框架,RPC框架中都使用了动态代理,动态代理很重要。 首先看看静态代理: 相对静态代理而已,静态代理需要对每一个方法做一次处理。如下: 定义...

Java 动态代理源码分析

本文为 Android 开源项目源码解析 公共技术点中的 动态代理 部分 项目地址:Jave Proxy,分析的版本:openjdk 1.6,Demo 地址:Proxy Demo 分析者:Ca...

java-动态代理-从源码分析

现在有一个接口ForumServicepublic interface UserService { /** * 目标方法 */ public abstract void add(); ...

Java动态代理详解,以及底层JDK源码实现分析(基于Java8)。

Java动态代理详解,以及底层JDK源码实现分析(基于Java8)。
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【Java】从源码分析动态代理机制
举报原因:
原因补充:

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