动态代理实现原理----JDK1.8

1.代理模式

Proxy里面有个RealSubject对象,初始化的时候将RealSubject对象传入,进行Proxy中RealSubject的对象进行初始化,然后Proxy中的方法,都是调用Proxy对象的方法,可在前后加需求,客户端只需使用多态,构造一个接口,即可。调用接口的方法,实际上就是调用Proxy方法,即RealSubject方法。

2.静态代理

按上述类图进行操作就是静态代理,静态代理如果要对多个方法进行处理,就得在多个方法前后进行修改,不方便,因此产生动态代理。

3.动态代理

代理类在程序运行时创建的代理方式,可以方便的对代理类的函数进行统一的处理,而不用修改代理类的每个方法。

3.1整体的用法

接口

具体对象

扫描二维码关注公众号,回复: 3093323 查看本文章

代理类

测试类

 

3.2底层原理

主要关于InvocationHandler和Proxy,前面看不懂没事,坚持看完你就懂了,我也是这么过来的

InvocationHandler接口

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

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,返回代理类的实例

private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {

        if (interfaces.length > 65535) {

            throw new IllegalArgumentException("interface limit exceeded");

        }

proxyClassCacheWeakCache类型,如果缓存中包含接口数组和classloaderclass类已经存在,就返回缓存副本,否则通过ProxyClassFactory创建一个对应的class对象

        return proxyClassCache.get(loader, interfaces);

    }

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

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

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方法。这就实现了动态代理

 

猜你喜欢

转载自blog.csdn.net/huangwei18351/article/details/82460589
今日推荐