设计模式——代理模式之动态代理源码分析JDK1.8(一)

    代理模式其本质是,对于需要调用的方法,在其原由逻辑上进行修改,或者说是在原有方法的基础上前后都加上log日志,或者是对原有方法的结果进行二次处理等等。

其结构图如下

设计模式——代理模式之动态代理源码分析JDK1.8(一)_第1张图片

Subject

     目标接口,定义RealSubject和Proxy的共同接口,

RealSubject

   具体的实现接口类,实现目标接口的功能

Proxy

   调用具体的实现接口类,并在具体实现类的基础上,调用其他相关方法。

静态代理方法

静态代理的实现,我们在原来方法上,添加了前后各打印一条数据

public interface Subject {
    void request();
}

具体方法调用

public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("具体方法实现");
    }
}

代理类方法实现

public class StaticProxy implements Subject {
    private Subject subject;

    public StaticProxy(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void request() {
        System.out.println("-------start-------------");
        subject.request();
        System.out.println("-------end----------------");
    }
}

测试代码

public class Test {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        StaticProxy proxy = new StaticProxy(subject);
        proxy.request();
    }
}

执行结果

-------start-------------
具体方法实现
-------end----------------

以上就是静态代理的具体实现,接下来来看jdk提供的的动态代理实现方法

接口不发生变化

public interface Subject {
    void request();
}

具体接口的实现也不变

public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("hello!");
    }
}

主要是代理类,这个时候继承的是jdk给我们提供的InvocationHandler

public class DynamicProxy implements InvocationHandler {

    private Object target;

    public Object bind(Object object){
        this.target = object;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before invoke");
         method.invoke(target,args);
        System.out.println("after invoke");
        return null;
    } 
}

接下来是测试类

public class TestProxy {
    public static void main(String[] args) {
        DynamicProxy proxy = new DynamicProxy();
        Subject service =(Subject) proxy.bind(new RealSubject());
        service.request();
    }
}

执行结果

before invoke
hello!
after invoke

对于动态代理我们可以在调用的时候,传递进去任何一个接口的显现类,都会在具体实现类的基础上,增加上我们代理类的逻辑

关于动态代理的实现,其主要是涉及到的就是Proxy类和InvocationHandler这个接口,我们分别来看下,对于InvocationHandlerl接口

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

这个接口只提供了一个抽象发法用来实现,子类在实现的时候,需要将具体实现类对象传递进去,然后再用反射调用具体实现类的方法,同时可以增加其他的业务逻辑。

Proxy类当中提供的方法,我们这里是用到newProxyInstance来创建对象的,我们来看下源码实现,基于JDK1.8

 public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        // handler的非空判断
        Objects.requireNonNull(h);

        //将代理类的接口复制出来一份
        final Class[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 根据classLoader和接口数组生成Class对象
         */
        Class cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

           // 这个地方是获取到动态生成的代理类的构造方法
            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    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、获取到动态创建的代理的构造方法,这个构造方法的参数就是定好的,传递进去的InvocationHandler,这个是动态代理约束好的,生成的字节码中,构造方法参数就是InvocationHandler

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

4、根据传递进来的具体的Handler对象和我们上面的构造方法对象,生成一个动态代理类的对象。

上面就是整个动态代理对象的生成过程,接下来我们来看getProxyClass0方法实现

 private static Class getProxyClass0(ClassLoader loader,
                                           Class... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // 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
        return proxyClassCache.get(loader, interfaces);
    }

注释写的很清楚,如果当前的这个接口数组已经存在,就返回缓存当中的副本,否则的话,就会通过ProxyClassFactory去创建一个Class对象。我们来看下ProxyClassFactory是如何创建一个Class对象的吧

其实就是WeakCache这个对象当中会在get取不到值时,去生成一个值放入进去,这里不做详细分析

WeakCache<>(new KeyFactory(), new ProxyClassFactory());

ProxyClassFactory是Proxy的一个静态内部类,主要就是用来根据classLoader和接口数组来生成Class对象的。下面我就贴出其主要用来创建Class对象的代码

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

            Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class intf : interfaces) {
               
                Class interfaceClass = null;
                try {
                    //根据接口的全限定名获取到接口的Class对象
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                // 如果两次的Class对象不一致,直接抛出异常,说明当前classloader
                // 加载出来接口的Class对象和接口在项目当中的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;
           //下面这个循环主要是判断非pulic的接口是不是在同一个包内
            
            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");
                    }
                }
            }

            //如果是公共的接口,我们就设置其全限定名为 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;
    }

这个类当中的生成代码的核心部分就是通过构建好的ProxyGenerator对象,调用其 generateClassFile()方法

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

这部分就是根据父接口生成字节码,我们调用ProxyGenerator.generateProxyClass方法,然后根据我们的RealSubject对象生成一个代理类到本地。

 byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", RealSubject.class.getInterfaces());
        String path = "C:\\Users\\Administrator\\Desktop\\com\\RealSubject.class";
        try(FileOutputStream fos = new FileOutputStream(path)) {
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类class文件写入成功");
        } catch (Exception e) {
            System.out.println("写文件错误");
        }

打开生成好的RealSubject.class文件

设计模式——代理模式之动态代理源码分析JDK1.8(一)_第2张图片

可以看到动态生成了一个$Proxy0的代理类,继承自Proxy类,实现了我们的Subject接口,其构造方法是传递了一个InvocationHandler参数

同时实现了equals,hashCode,toString三个方法,以及我们自己定义的接口当中的方法,我们主要看接口当中定义的request方法如何实现

设计模式——代理模式之动态代理源码分析JDK1.8(一)_第3张图片

这里我们可以清楚地看到,调用的InvocationHandler实现类当中的invoke方法,m3就是我们自定类当中定义的方法

设计模式——代理模式之动态代理源码分析JDK1.8(一)_第4张图片

到这里为止,我们就可以清楚地看到为什么在调用request方法的时候,会调用DynamicProxy当中的我们自己实现的invoke方法,因为传递过去的就是我们自己的InvokationHandler对象。动态代理类的构造方法生成之后,传递进去的就是我们调用

Proxy.newProxyInstance()这个方法传递的自己的实现类

设计模式——代理模式之动态代理源码分析JDK1.8(一)_第5张图片

你可能感兴趣的:(设计模式)