博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式——代理模式之动态代理源码分析JDK1.8(一)
阅读量:4149 次
发布时间:2019-05-25

本文共 12621 字,大约阅读时间需要 42 分钟。

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

其结构图如下

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 invokehello!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文件

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

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

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

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

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

你可能感兴趣的文章
看完老板就给加薪了!30岁以后搞Android已经没有前途?工作感悟
查看>>
绝了!这么香的技术还不快点学起来,大牛最佳总结
查看>>
网络优化软件apk,金九银十怎么从中小企业挤进一线大厂?我先收藏为敬
查看>>
美团安卓面试,这些年我所经历的所有面试,完整版开放下载
查看>>
美团安卓面试,阿里巴巴Android面试都问些什么?含小米、腾讯、阿里
查看>>
腾讯T2亲自讲解!阿里面试100%会问到的JVM,源码+原理+手写框架
查看>>
腾讯T3亲自讲解!字节大牛耗时八个月又一力作,大牛最佳总结
查看>>
你所不知道的Android原生开发的现状,手慢无
查看>>
做了3年Android还没看过OkHttp源码?小白也能看明白
查看>>
免费Android高级工程师学习资源,挥泪整理面经
查看>>
全世界都在问Android开发凉了吗?赶紧收藏!
查看>>
全世界都在问Android开发凉了吗?送大厂面经一份!
查看>>
全网最具深度的三次握手、四次挥手讲解,最全Android知识总结
查看>>
全网最具深度的三次握手、四次挥手讲解,看这一篇就够了!
查看>>
全网最具深度的三次握手、四次挥手讲解,知乎上转疯了!
查看>>
全靠这份Android知识点PDF大全,完整版开放下载
查看>>
关于Android程序员最近的状况,大厂内部资料
查看>>
从草根到百万年薪程序员的十年风雨之路,成功收获美团,小米安卓offer
查看>>
价值2000元的学习资源泄露,实战篇
查看>>
作为移动开发程序员,2021年Android开发者跳槽指南,实战篇
查看>>