四、JDK代理模式机制

JDK动态代理实现

        IBuyCar buyCar1=new BuyCarImpl();
        IBuyCar buyCarProxy=(IBuyCar)Proxy.newProxyInstance(IBuyCar.class.getClassLoader(),
                new Class[]{IBuyCar.class},new DynamicProxyHandler(buyCar1));
        buyCarProxy.buyCar("福特");

可以看到newProxyInstance方法有三个参数:

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

其中ClassLoader是指定接口类的类加载器,Class<?>[] interfaces是接口类中的接口列表,InvocationHandler 是处理器。
底层原理
一、主要关于InvocationHandler和Proxy,前面看不懂没事,坚持看完你就懂了,我也是这么过来的 InvocationHandler接口

public class DynamicProxyHandler implements InvocationHandler {

    private   Object obj;
    public  DynamicProxyHandler(Object obj)
    {
        this.obj=obj;
    }
    @Override
    public  Object  invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("动态代理实现买车前准备");
        Object object=method.invoke(obj,args);
        System.out.println("动态代理实现买车后");
        return  object;
    }
}

子类重写的时候,需要将具体实现类对象(不是代理类)传递进去,然后用反射调用具体实现类的方法,同时增加其他业务逻辑。
二、Proxy类

public static Object newProxyInstance(ClassLoader loader,

                                          Class<?>[] interfaces,

                                          InvocationHandler h)

        throws IllegalArgumentException

    {

        Objects.requireNonNull(h);handler的非空判断

 

        final Class<?>[] intfs = interfaces.clone();

        final SecurityManager sm = System.getSecurityManager();

        if (sm != null) {

            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);

        }

 

       

        Class<?> cl = getProxyClass0(loader, intfs);根据classloader和接口数组生成代理类的class对象

 

       

        try {

            if (sm != null) {

                checkNewProxyPermission(Reflection.getCallerClass(), cl);

            }

 获取动态生成的代理类中参数为InvocationHandler的构造方法

//private static final Class<?>[] constructorParams = { InvocationHandler.class };     

      final Constructor<?> cons = cl.getConstructor(constructorParams);

            final InvocationHandler ih = h;

得到cl代理类class中类的修饰符,然后判断是不是public类型,如果是,就返回指定构造器cons的实例,即代理对象的实例,如果不是,就将cons构造方法设置为可访问。

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

        }

    }

1、根据传递进来的ClassLoader,以及我们的代理对象的父接口数组,来动态创建二进制的class文件,然后根据创建好的Class二进制文件,获取到创建的动态代理类的Class对象。

2、通过代理类的class对象,获取class对象中参数为InvocationHandler的构造方法

3、判断构造方法的访问修饰符,如果不是public的,将其设置成可以访问的

4、调用构造器的newInstance方法,参数为InvocationHandler,返回代理类的实例

WeakCache这个对象当中会在get取不到值时,去生成一个值,放入进去;


image.png

ProxyClassFactory是Proxy的一个静态内部类,主要就是用来根据classLoader和接口数组来生成Class对象的。


image.png

subKeyFactory.apply(key, parameter) 方法调用的是ProxyClassFactory@apply(ClassLoader loader, Class<?>[] interfaces) 方法

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

        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);用来存传入的接口

        for (Class<?> intf : interfaces) {

        

            Class<?> interfaceClass = null;

            try {

根据接口全限定类名和classLoader,获取到接口class对象

                interfaceClass = Class.forName(intf.getName(), false, loader);

            } catch (ClassNotFoundException e) {

            }

如果两次接口class对象不一致,直接抛出异常,说明创建错误

            if (interfaceClass != intf) {

                throw new IllegalArgumentException(

                    intf + " is not visible from class loader");

            }

            判断创建出来的接口是不是接口类型,不是就抛异常

            if (!interfaceClass.isInterface()) {

                throw new IllegalArgumentException(

                    interfaceClass.getName() + " is not an interface");

            }

            判断接口的set集合是否已经存在了该接口类,存在抛出异常,不存在就添加进去

            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {

                throw new IllegalArgumentException(

                    "repeated interface: " + interfaceClass.getName());

            }

        }



        String proxyPkg = null;     // package to define proxy class in

        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

      判断非public接口是不是在同一个包内,如果不是,抛出异常;

      声明代理类所在的包位置,

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

                }

            }

        }

如果都是public接口设定全限定类名com.sun.proxy.$proxy0

        if (proxyPkg == null) {

            // if no non-public proxy interfaces, use com.sun.proxy package

            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";

        }



       

        long num = nextUniqueNumber.getAndIncrement();

        String proxyName = proxyPkg + proxyClassNamePrefix + num;



        根据代理类全限定类名,接口数组,访问修饰符,生成代理类的字节码

        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(

            proxyName, interfaces, accessFlags);

        try {

根据生成的字节码,创建class对象并返回。Native方法

            return defineClass0(loader, proxyName,

                                proxyClassFile, 0, proxyClassFile.length);

        } catch (ClassFormatError e) {

            throw new IllegalArgumentException(e.toString());

        }

    }

}

可以看到这段代码就是设置好需要生成的类的类名,然后调用ProxyGenerator.generateProxyClass来生成代理类的字节码

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {

    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);

    final byte[] var4 = var3.generateClassFile();

 //中间省略掉一部分代码

    return var4;

}

private byte[] generateClassFile() {

    //将object类当中的 hashcode,equals,toString方法添加到动态代理类当中

    this.addProxyMethod(hashCodeMethod, Object.class);

    this.addProxyMethod(equalsMethod, Object.class);

    this.addProxyMethod(toStringMethod, Object.class);

    Class[] var1 = this.interfaces;

    int var2 = var1.length;



    int var3;

    Class var4;

   

    //遍历父接口数据

    for(var3 = 0; var3 < var2; ++var3) {

        var4 = var1[var3];



        //获取每个接口当中的方法

        Method[] var5 = var4.getMethods();

        int var6 = var5.length;



        //遍历接口当中的方法,将接口当中的方法都添加至动态代理类当中

        for(int var7 = 0; var7 < var6; ++var7) {

            Method var8 = var5[var7];

            this.addProxyMethod(var8, var4);

        }

    }



    Iterator var11 = this.proxyMethods.values().iterator();



    //检查代理类当中的返回类型

    List var12;

    while(var11.hasNext()) {

        var12 = (List)var11.next();

        checkReturnTypes(var12);

    }



    Iterator var15;

    try {

        // 将构造方法添加至代理类当中的方法集合中

        this.methods.add(this.generateConstructor());

        var11 = this.proxyMethods.values().iterator();



        //遍历代理类当中的方法,此处使用两层循环,是因为方法签名相同的,可能有多个方法

        while(var11.hasNext()) {

            var12 = (List)var11.next();

            var15 = var12.iterator();



            while(var15.hasNext()) {

                ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();

                this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));

                this.methods.add(var16.generateMethod());

            }

        }

        // 将静态代码块添加进去

        this.methods.add(this.generateStaticInitializer());

    } catch (IOException var10) {

        throw new InternalError("unexpected I/O Exception", var10);

    }



    if(this.methods.size() > '\uffff') {

        throw new IllegalArgumentException("method limit exceeded");

    } else if(this.fields.size() > '\uffff') {

        throw new IllegalArgumentException("field limit exceeded");

    } else {

            /**

             * 省略部分代码

             **/

            return var13.toByteArray();

        } catch (IOException var9) {

            throw new InternalError("unexpected I/O Exception", var9);

        }

    }

}

生成了代理类对象,继承自Proxy,实现了subject接口,其构造方法是传递了InvocationHandler参数,同时实现了equals ,hashcode,toString方法,以及我们自己定义的request方法。

这里就能清楚的看到,代理类中,request方法,其实就是调用InvocationHandler实现类中invoke方法,m3就是RealSubject中定义的request方法

这里就能知道,调用Subject的request的时候,为什么能够调用动态代理类的invoke方法了。因为在调用bind中的Proxy.newProxyInstance的时候,传入的是我们classLoader,代理类的父接口,和自定义的InvocationHandler,因此生成的代理类对象中的h就是自定义的InvocationHandler。

整体流程:

使用方法:

定义一个接口Subject,里面有抽象方法request,一个接口的实现类RealSubject,里面有真正的request方法,定义个ProxyHandler,实现InvocationHandler接口,有ProxyHandler中有个目标对象target,用于传入RealSubject的实例;里面有两个方法,bind方法,将传入的RealSubject对象,赋值给target,并调用Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this)返回代理类对象的实例;还有一个方法,invoke(Object proxy, Method method, Object[] args)方法,在method.invoke前后可以加逻辑功能;method方法对象包含方法的相关信息,method.invoke就会去找target对象中,指定参数与该方法匹配的方法。

底层原理:

首先分析代理对象的产生过程,最后分析代理对象的产生后的样子

产生过程:

Proxy.newInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this) 方法,该方法主要做了

①获取代理类的接口Subject.class对象的副本intfs。

②调用getProxyClass0(loader, intfs)方法,获取代理类的class对象

--2.1 getProxyClass方法会去调用proxyClassCache.get(loader,interfaces)方法,该方法的作用,如果缓存中已经有该接口对应的代理类的副本,那么返回代理类的副本,否则使用ProxyClassFactory的apply方法创建一个class对象;get方法中有一句表现了这个过程,如下图

SubkeyFactory与ProxyClassFactory都是BiFunction接口的的实现类,此处调用的是ProxyClassFactory的apply方法。

----2.1.1 apply方法:根据接口的全限定类名以及指定的classLoader,调用class.forName方法得到接口的class对象interfaceClass,进行判断是否是接口类型,是否有重复,

----2.1.2 设定代理类所在的包路径,如果有非public接口,且不是在同一个包内,抛异常,如果在同一个包内,就设定代理类的包路径为proxyPkg,如果都是public接口,那么生成的代理类的包路径proxyPkg设定为com.sum.proxy.

----2.1.3 根据包路径,类名前缀和num数字,确定代理类的名字

----2.1.4 调用ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);内部生成一个ProxyGenerator对象,调用ProxyGenerator.generateClassFile()得到代理类的字节码文件;

------2.1.4.1 ProxyGenerator.generateClassFile()方法,为代理类添加了三个object类的hashcode,equals,toString方法,遍历父接口,获取每个接口中的方法,添加方法,添加构造方法等,都加入到代理中;返回二进制字节码文件

----2.1.5 调用defineClass0(loader, proxyName, proxyClassFile, 0,proxyClassFile.length);将字节码文件创建为class对象,返回

③获取到代理类的class对象后,得到代理类参数为InvocationHandler的构造方法,如果是非public类型,就使用setAccessible设定为true,然后调用cons.newInstance(new Object[]{h})来生成代理类对象;

④在代理类对象中,有个request方法,方法中默认会调用Proxy类里面的InvocationHandler也就是我们自定义的InvocationHandler的invoke方法。这就实现了动态代理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 我拿起龙之叹息对准海格力斯,的角质长枪猛的咋了过去,“碰!”那角质长枪挡住了我的剑,我被他连着长枪抛了出去,我拿起...
    葛a阅读 460评论 0 0
  • 一道锁链出现了裂缝,“600,”“碰!”那道锁链终于断裂了,四周的锁链一下子消失了,身后的亡灵也都消失,我走向前...
    葛a阅读 524评论 0 3
  • “谢谢你的力量!使得我更加强大,神圣一击,”[由于黑化,力量增加百分之八十]他用触手挡在前面试图阻挡我的攻击 但是...
    葛a阅读 429评论 2 6
  • 突然一个声音传来“看到这一幕相信你现很痛恨神鹰,现在你想选择什么,告诉我,我可以让你免遭厄运!”一道白光闪现在...
    葛a阅读 431评论 0 5
  • 烦烦烦烦烦烦烦烦烦烦烦烦。。。。。。。。。 ......................................
    离题万里阅读 184评论 0 0