关闭

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

74人阅读 评论(0) 收藏 举报
分类:

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

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

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

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:992次
    • 积分:78
    • 等级:
    • 排名:千里之外
    • 原创:7篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档