美文网首页
动态代理

动态代理

作者: Davisxy | 来源:发表于2019-04-21 01:20 被阅读0次

java动态代理实现与原理详细分析这篇文章我感觉讲的很好,所以我也顺着作者的思路加上自己的理解再来撸一遍。

java中根据创建代理类的时间点,分为静态代理动态代理

静态代理

代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息,过滤消息,把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。


代理模式(Proxy)结构图

静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

总结一句话就是:专业的事让专业的人干

小栗子

歌星商演,经纪人就充当一个代理人;前期接洽,面谈,商量出演费用,预付款等等事情都是由经纪人出面来处理,等到了商演的时候,经纪人告诉歌星,什么时间,什么地点要去唱个歌,之后又是经纪人负责尾款追收。

package com.principle.staticproxy;

public interface Star {
    
    //面谈
    void confer();
    
    //签合同
    void signContract();
    
    //订票
    void bookTicket();
    
    //唱歌
    void sing();
    
    //收钱
    void getMoney();

}
package com.principle.staticproxy;

public class RealStar implements Star{

    @Override
    public void confer() {
        System.out.println("confer");
    }

    @Override
    public void signContract() {
        System.out.println("signContract");
    }

    @Override
    public void bookTicket() {
        System.out.println("bookTicket");
    }

    @Override
    public void sing() {
        System.out.println("sing");
    }

    @Override
    public void getMoney() {
        System.out.println("getMoney");
    }
}
package com.principle.staticproxy;

public class ProxyStar implements Star {

    private Star star;

    public ProxyStar(Star star) {
        super();
        this.star = star;
    }

    @Override
    public void confer() {
        star.confer();
    }

    @Override
    public void signContract() {
        star.signContract();
    }

    @Override
    public void bookTicket() {
        star.bookTicket();
    }

    @Override
    public void sing() {
        star.sing();
    }

    @Override
    public void getMoney() {
        star.getMoney();
    }

}
package com.principle.staticproxy;

public class Client {
    
    public static void main(String[] args) {
        Star reStar=new RealStar();
        ProxyStar proxyStar=new ProxyStar(reStar);
        
        proxyStar.confer();
        proxyStar.signContract();
        proxyStar.bookTicket();
        proxyStar.sing();
        proxyStar.getMoney();
    }

}

静态代理就说这么多,今天主要说动态代理

动态代理

代理类在程序运行时创建的代理方式被成为动态代理。

我们上面静态代理的例子中,代理类是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。

动态代理简单实现

  • 创建一个InvocationHandler对象
//创建一个与代理对象相关联的InvocationHandler
InvocationHandler handler=new MyInvocationHandler<Star>(realStar);
  • 使用Proxy类的getProxyClass静态方法生成一个动态代理类proxy
Class<?> proxyClass = Proxy.getProxyClass(Star.class.getClassLoader(), new Class<?>[] {Star.class});
  • 获得proxyClass中一个带InvocationHandler参数的构造器constructor
Constructor<?> constructor = StarProxy.getConstructor(InvocationHandler.class);
  • 通过构造器constructor来创建一个动态实例proxy
Star proxy = (Star) cons.newInstance(handler);

就此,一个动态代理对象就创建完毕,当然,上面四个步骤可以通过Proxy类的newProxyInstances方法来简化:

Star realStar=new RealStar();
StarHandler handler=new StarHandler(realStar);  
Star star=(Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Star.class}, handler);       
package com.principle.dynamicproxy;

import java.lang.reflect.Proxy;

public class Client {
    
    public static void main(String[] args) {
        Star realStar=new RealStar();
        StarHandler handler=new StarHandler(realStar);
        
        Star star=(Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Star.class}, handler);
        
        star.sing();
    }

}

我们先创建了委托类(RealStar)的实例,而这个委托类是实现了Star的接口得;然后实例化了一个InvocationHandler的实例,并将委托类(RealStar)的实例传过去了;再就获取到了委托类的代理star;最后调用代理类的sing方法;这里我有点迫不及待说出结论:

在创建代理类的时候,代理类的构造方法中是获取到InvocationHandler的实例对象,然后在对应的接口方法中,调用了InvocationHandler的invoke方法,并将Method和参数传了过去,InvocationHandler在调用invoke方法的时候(已经开始我们自己做主了),我们在该方法中拿到了对应的方法和参数,InvocationHandler中又有对应的委托类(RealStar)的对象,我们可以直接调用对应的方法。
总的来说就是当调用代理类的方法的时候,我们会调用InvocationHandler的invoke方法,在invoke方法中,我们会调用委托类(RealStar)的对应方法(自己处理),我们在这个方法(invoke)中可以拦截预处理,事后处理等等操作。

上面这个你可以先不看,或者也可以先简单的看一遍,不用深入追究,接下来我们看源码,然后再来看看这个结论就有感觉了。

package com.principle.dynamicproxy;

import java.lang.reflect.Proxy;

public class Client {
    
    public static void main(String[] args) {
        Star realStar=new RealStar();
        StarHandler handler=new StarHandler(realStar);
        
        Star star=(Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Star.class}, handler);
        
        star.sing();
    }

}

我们调用了Proxy.newProxyInstance方法;

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

    //标记1
    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
    //标记2
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        //标记3
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        //标记4
        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
final Class<?>[] intfs = interfaces.clone();
//标记2
Class<?> cl = getProxyClass0(loader, intfs);
//标记3
final Constructor<?> cons = cl.getConstructor(constructorParams);
//标记4
cons.newInstance(new Object[]{h});
  • 获取到对应的接口类
  • 获取到对应的代理类Class
  • 获取到代理类的构造器
  • 构造器实例化

主要就是获取到对应的代理类Class
可以看出,这个类的产生就是整个动态代理的关键,由于是动态生成的类文件,这里不具体进入分析如何产生的这个类文件,只需要知道这个类文件时缓存在java虚拟机中的,我们可以通过下面的方法将其打印到文件里面,一睹真容:

package com.principle.dynamicproxy;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Proxy;

import sun.misc.ProxyGenerator;


public class Client {
    
    public static void main(String[] args) {
        Star realStar=new RealStar();
        StarHandler handler=new StarHandler(realStar);
        
        Star star=(Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Star.class}, handler);
        
        star.sing();
        
        System.out.println(star.getClass().getName());
        
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", RealStar.class.getInterfaces());
        String path = "G:/java3/Base/src/com/principle/dynamicproxy/StarProxy.class";
        File parent=new File("G:/java3/Base/src/com/principle/dynamicproxy");
        if (!parent.exists()) {
            parent.mkdirs();
        }
        
        try(FileOutputStream fos = new FileOutputStream(path)) {
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类class文件写入成功");
        } catch (Exception e) {
            e.printStackTrace();
           System.out.println("写文件错误");
        }
    }

}

对这个class文件进行反编译,我们看看jdk为我们生成了什么样的内容:

package defpackage;

import com.principle.dynamicproxy.Star;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

/* renamed from: $Proxy0 */
public final class C$Proxy0 extends Proxy implements Star {
    private static Method m0;
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m5;
    private static Method m6;
    private static Method m7;

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m6 = Class.forName("com.principle.dynamicproxy.Star").getMethod("signContract", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m5 = Class.forName("com.principle.dynamicproxy.Star").getMethod("getMoney", new Class[0]);
            m7 = Class.forName("com.principle.dynamicproxy.Star").getMethod("bookTicket", new Class[0]);
            m3 = Class.forName("com.principle.dynamicproxy.Star").getMethod("sing", new Class[0]);
            m4 = Class.forName("com.principle.dynamicproxy.Star").getMethod("confer", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (Throwable e) {
            throw new NoSuchMethodError(e.getMessage());
        } catch (Throwable e2) {
            throw new NoClassDefFoundError(e2.getMessage());
        }
    }

 /**
  *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
  *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
  *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
  *
  *super(paramInvocationHandler),是调用父类Proxy的构造方法。
  *父类持有:protected InvocationHandler h;
  *Proxy构造方法:
  *    protected Proxy(InvocationHandler h) {
  *         Objects.requireNonNull(h);
  *         this.h = h;
  *     }
  *
  */
    public C$Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    public final void bookTicket() {
        RuntimeException e;
        try {
            this.h.invoke(this, m7, null);
        } catch (Error e2) {
            e = e2;
            throw e;
        } catch (RuntimeException e3) {
            e = e3;
            throw e;
        } catch (Throwable th) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(th);
        }
    }

    public final void confer() {
        RuntimeException e;
        try {
            this.h.invoke(this, m4, null);
        } catch (Error e2) {
            e = e2;
            throw e;
        } catch (RuntimeException e3) {
            e = e3;
            throw e;
        } catch (Throwable th) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(th);
        }
    }

    public final boolean equals(Object obj) {
        RuntimeException e;
        try {
            return ((Boolean) this.h.invoke(this, m1, new Object[]{obj})).booleanValue();
        } catch (Error e2) {
            e = e2;
            throw e;
        } catch (RuntimeException e3) {
            e = e3;
            throw e;
        } catch (Throwable th) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(th);
        }
    }

    public final void getMoney() {
        RuntimeException e;
        try {
            this.h.invoke(this, m5, null);
        } catch (Error e2) {
            e = e2;
            throw e;
        } catch (RuntimeException e3) {
            e = e3;
            throw e;
        } catch (Throwable th) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(th);
        }
    }

    public final int hashCode() {
        RuntimeException e;
        try {
            return ((Integer) this.h.invoke(this, m0, null)).intValue();
        } catch (Error e2) {
            e = e2;
            throw e;
        } catch (RuntimeException e3) {
            e = e3;
            throw e;
        } catch (Throwable th) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(th);
        }
    }

    public final void signContract() {
        RuntimeException e;
        try {
            this.h.invoke(this, m6, null);
        } catch (Error e2) {
            e = e2;
            throw e;
        } catch (RuntimeException e3) {
            e = e3;
            throw e;
        } catch (Throwable th) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(th);
        }
    }
 /**
  * 
  *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
  *this.h.invoke(this, m3, null);这里简单,明了。
  *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
  *再联系到InvacationHandler中的invoke方法。嗯,就是这样。
  */
    public final void sing() {
        RuntimeException e;
        try {
            this.h.invoke(this, m3, null);
        } catch (Error e2) {
            e = e2;
            throw e;
        } catch (RuntimeException e3) {
            e = e3;
            throw e;
        } catch (Throwable th) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(th);
        }
    }

    public final String toString() {
        RuntimeException e;
        try {
            return (String) this.h.invoke(this, m2, null);
        } catch (Error e2) {
            e = e2;
            throw e;
        } catch (RuntimeException e3) {
            e = e3;
            throw e;
        } catch (Throwable th) {
            UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(th);
        }
    }
}

可以看到上述代码和我们结论是一样的,上面的方法有点多,不过基本上内容都长一样的。
再把我们结论拿过来看看

在创建代理类的时候,代理类的构造方法中是获取到InvocationHandler的实例对象,然后在对应的接口方法中,调用了InvocationHandler的invoke方法,并将Method和参数传了过去,InvocationHandler在调用invoke方法的时候(已经开始我们自己做主了),我们在该方法中拿到了对应的方法和参数,InvocationHandler中又有对应的委托类(RealStar)的对象,我们可以直接调用对应的方法。
总的来说就是当调用代理类的方法的时候,我们会调用InvocationHandler的invoke方法,在invoke方法中,我们会调用委托类(RealStar)的对应方法(自己处理),我们在这个方法(invoke)中可以拦截预处理,事后处理等等操作。

我们可以对InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。

代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的invoke方法,从而达到代理执行被代理对象的方法。也就是说,动态代理通过中介类实现了具体的代理功能。



这里再次原作者表示感谢:java动态代理实现与原理详细分析





参考:

java动态代理实现与原理详细分析


相关文章

  • 面试系列~动态代理实现与原理

    动态代理有JDK动态代理, CGLIB动态代理, SpringAOP动态代理 一,JDK动态代理  jdk动态代理...

  • 编程常用的设计模式

    动态代理和静态代理 静态代理 动态代理 静态代理与动态代理的区别 JDK中的动态代理和CGLIB 实现动态代理的方...

  • Spring的AOP原理分析

    一 动态代理 动态代理分为JDK动态代理和CGLIB动态代理 jdk动态代理 被代理类(目标类)和代理类必须实现同...

  • 设计模式之代理模式

    代理分为静态代理和动态代理。 动态代理又包括基于JDK的动态代理、基于CGlib 的动态代理、基于Aspectj实...

  • Java高级主题(五)——动态代理

    代理可以分为静态代理、动态代理,动态代理又可以分为 jvm的动态代理 和 cglib的动态代理。像spring框架...

  • 动态代理

    动态代理分为两类:1、基于接口的动态代理; (JDK动态代理 )2、基于类的动态代理;(cglib动态代理)3、J...

  • 动态代理的两种方式

    静态代理就不说了,基本用到的都是动态代理。 Java中动态代理有JDK动态代理和CGLIB动态代理。 JDK代理的...

  • Java动态代理

    通过以下几种方式介绍动态代理 动态代理涉及到的类 动态代理用法 Proxy类解析 动态代理类解析 动态代理涉及到的...

  • Spring之代理模式

    九、代理模式 目录:静态代理、动态代理AOP的底层机制就是动态代理。代理模式分为静态代理和动态代理。接触aop之前...

  • Java 代理

    静态代理 动态代理 动态代理, 日志切片使用反射获得方法 动态代理, 自定义注解(对注解的方法,使用动态代理添加切...

网友评论

      本文标题:动态代理

      本文链接:https://www.haomeiwen.com/subject/znwrgqtx.html