JAVA设计模式之JDK动态代理模式二

前言

看了上一遍 《动态代理模式一》应该会有如下疑问:

  1. 动态代理实现类实例是怎么生成的?
  2. 是通过生成Java文件->编译成class文件->ClassLoad->new 对象;还是直接生成class文件->ClassLoad->new 对象?

一、代理类UML类图

在这里插入图片描述
这个类图是根据上一遍 动态代理模式一 的代码来绘制的,$Proxy0类是被JVM生成的,全局流程先说一下,让大家在脑海里有一个全局观

  1. 客户端调用DynamicProxy#newProxyInstance()方法生成代理类$Proxy0实例,这个$Proxy0类是继承Proxy类并实现了HouseSubject接口
  2. 客户端继续执行$Proxy0#rent()方法就会执行方法体内的DynamicProxy#invoke()方法了,invoke()方法里使用了反射执行了具体RealHouseSubjectA#rent()方法或者是RealHouseSubjectB#rent()方法

二、$Proxy0.class是怎么生成的?

public class Client {

    public static void main(String[] args) {
        // 添加这行代码,会在项目跟目录下com.sun.proxy看到$Proxy0.class;idea打开会自动反编译
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 创建一个动态代理
        DynamicProxy dynamicProxy = new DynamicProxy();
        // 创建两个目标对象(房东A和房东B)
        HouseSubject subjectA = new RealHouseSubjectA();
        HouseSubject subjectB = new RealHouseSubjectB();
        // 动态代理拿到相应的操作权限:也就是一个中介有出租多套房子的权限
        HouseSubject proxyA = (HouseSubject) dynamicProxy.newProxyInstance(subjectA);
        HouseSubject proxyB = (HouseSubject) dynamicProxy.newProxyInstance(subjectB);

        // 通过调用动态代理对象方法从而调用目标对象方法
        proxyA.rent();
        proxyB.rent();

    }
}

注意看第五行代码,是本次新增的代码
在这里插入图片描述
部分代码截图,红框重点部分

三、实现原理分析

在这里插入图片描述

上图为生成代理对象主要方法调用链,我们依次对各个阶段进行分析,说明一下:以下代码只留下核心代码,以下断言,异常判断,安全检查等代码都去掉,目前主要是为了清晰和节约篇幅

Proxy.newProxyInstance()

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException {

        final Class<?>[] intfs = interfaces.clone();
        /*
         * Look up or generate the designated proxy class.
         */
         // 根据提供的接口(intfs:HouseSubject接口),生成代理类
        Class<?> cl = getProxyClass0(loader, intfs);

        final Constructor<?> cons = cl.getConstructor(constructorParams);
            
        // 调用代理类的构造方法,返回实例对象(示例中:继承了Proxy并实现了HouseSubject接口)    
        return cons.newInstance(new Object[]{h});
    }

Proxy.getProxyClass0()

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
                                           
        // 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创建
        return proxyClassCache.get(loader, interfaces);
    }

ProxyClassFactory.apply()

private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

			// 1.确定包名,如果没有非public的代理接口,则包名为com.sun.proxy
            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.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) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
             // 2.确定类名,示例中代理类名为:com.sun.proxy.$Proxy0
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
             // 3.根据类名,代理接口信息生成代理类,即jvm字节码.class文件
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            
            // 4.根据生成的.class文件,返回一个代理类Class;此方法为本地方法
            return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
        }
    }

四、总结

文章一开始全局的介绍JDK动态代理脉络,紧接着分析实现原理,主要以源码核心代码进行讲解,没有每一行代码进行注释讲解,大家最好debug一下,加深印象~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值