首页 > 其他 > 详细

代理模式详解(二)-动态代理

时间:2020-04-25 14:15:14      阅读:52      评论:0      收藏:0      [点我收藏+]

  上一篇用一个小案例的方式引入了代理模式(实际上是静态代理)。相对来说,自己去写代理类的方式就是静态代理,比如上一章的GamePlayerProxy类、OrderServiceStaticProxy类,他们都属于我们自己写的代理类。那这么说的话,动态代理就不用自己写代理类了吗?我觉得是也不是,比如spring大名顶顶的aop(面向切面编程),只需要配置,spring框架就帮我们自动实现了代理,而我们只需要关注切面的业务代码。那什么是动态代理呢?动态代理指的是,在代码的实现阶段我们不需要关心代理谁,而在运行阶段才指定代理哪个对象。

  在java中,常见的实现动态代理的方式有两种:(1)jdk动态代理;(2)cglib动态代理。

  我们还是看游戏代练的例子通过动态代理的实现:

  定义一个GamePlayerJdkDynProxy类:

  

public class GamePlayerJdkDynProxy implements InvocationHandler {

    private Object target; //目标代理类,不需要持有具体的代理对象

    public Object getProxyInstance(Object target){
        this.target = target;
        Class clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equalsIgnoreCase("login")){  //前置方法可以根据条件是否调用
            before(args[0]);//登陆的时候才打印
        }
        Object object = method.invoke(target,args);
        if(method.getName().equalsIgnoreCase("upgrade")){  //后置方法可以根据条件是否调用
            after();    //升级之后才打印
        }
        return object;
    }

    private void before(Object args){
        System.out.println("正在使用"+args.toString()+"登陆游戏");
    }

    private void after(){
        System.out.printf("完成任务!");
    }
}

 

测试代码: 

public class Client {
    public static void main(String[] args) {
        //获取代理类
        GamePlayerJdkDynProxy gamePlayerJdkDynProxy = new GamePlayerJdkDynProxy();
        IGamePlayer playerProxy = (IGamePlayer) gamePlayerJdkDynProxy.getProxyInstance(new GamePlayer("小A"));
        System.out.println(playerProxy.getClass().getName());  //这里我打印了代理类的类名
        playerProxy.login("我是小A呀","123");  //登陆账号
        playerProxy.win(); //赢了一局
        playerProxy.upgrade(); //上星耀了
    }
}

 

运行结果:


 

com.sun.proxy.$Proxy0

正在使用我是小A呀登陆游戏

小A登陆游戏,用户名为:我是小A呀

我是小A呀 玩家赢了一局

恭喜:我是小A呀 玩家上到星耀了

完成任务!  

 


  我们可以看到,动态代理与静态代理实现的效果是一样的。但是它却可以对一些代理的方法是否需要前置通知,后置通知做一个灵活的处理,比如有人登陆小A的账号,小A立马就可以知道,上到星耀完成认为了,也立马会得到这样的一个通知,真的是太好了!这种方式对于代码的解藕是非常有帮助的,对于日志、事物、权限等都可以在系统设计初期不用考虑,而在设计后通过动态代理的方式切过去(这就是AOP)。动态代理居然如此诱人,那我们再细探它的究竟吧!

  看到 com.sun.proxy.$Proxy0相信小伙们肯定会有疑问,这个玩意是什么?!为了一探这个类的究竟,我们准备把这个类的字节码输出到本地。

我们在测试代码的下面追加文件输出的代码:

public static void main(String[] args) throws IOException {
        //获取代理类
        GamePlayerJdkDynProxy gamePlayerJdkDynProxy = new GamePlayerJdkDynProxy();
        IGamePlayer playerProxy = (IGamePlayer) gamePlayerJdkDynProxy.getProxyInstance(new GamePlayer("小A"));
        System.out.println(playerProxy.getClass().getName());  //这里我打印了代理类的类名
        playerProxy.login("我是小A呀","123");  //登陆账号
        playerProxy.win(); //赢了一局
        playerProxy.upgrade(); //上星耀了
        try{
            byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{GamePlayer.class});
            FileOutputStream os = new FileOutputStream("/Users/wangpei/Downloads/$Proxy.class");
            os.write(bytes);
            os.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

运行之后我们可以看到$proxy类输出到了下载目录下面,我们可以用反编译工具jad打开,或者直接用idea打开:

技术分享图片

 

 

具体类容如下:

 

 

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.proxy.statics.game.GamePlayer;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements GamePlayer {
    private static Method m1;
    private static Method m6;
    private static Method m2;
    private static Method m5;
    private static Method m11;
    private static Method m13;
    private static Method m0;
    private static Method m10;
    private static Method m3;
    private static Method m12;
    private static Method m9;
    private static Method m4;
    private static Method m8;
    private static Method m7;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void win() throws  {
        try {
            super.h.invoke(this, m6, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void upgrade() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final Class getClass() throws  {
        try {
            return (Class)super.h.invoke(this, m11, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void notifyAll() throws  {
        try {
            super.h.invoke(this, m13, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void wait() throws InterruptedException {
        try {
            super.h.invoke(this, m10, (Object[])null);
        } catch (RuntimeException | InterruptedException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String getName() throws  {
        try {
            return (String)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void notify() throws  {
        try {
            super.h.invoke(this, m12, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void wait(long var1) throws InterruptedException {
        try {
            super.h.invoke(this, m9, new Object[]{var1});
        } catch (RuntimeException | InterruptedException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    public final void login(String var1, String var2) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    public final void wait(long var1, int var3) throws InterruptedException {
        try {
            super.h.invoke(this, m8, new Object[]{var1, var3});
        } catch (RuntimeException | InterruptedException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

    public final String getUserName() throws  {
        try {
            return (String)super.h.invoke(this, m7, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m6 = Class.forName("com.proxy.statics.game.GamePlayer").getMethod("win");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m5 = Class.forName("com.proxy.statics.game.GamePlayer").getMethod("upgrade");
            m11 = Class.forName("com.proxy.statics.game.GamePlayer").getMethod("getClass");
            m13 = Class.forName("com.proxy.statics.game.GamePlayer").getMethod("notifyAll");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m10 = Class.forName("com.proxy.statics.game.GamePlayer").getMethod("wait");
            m3 = Class.forName("com.proxy.statics.game.GamePlayer").getMethod("getName");
            m12 = Class.forName("com.proxy.statics.game.GamePlayer").getMethod("notify");
            m9 = Class.forName("com.proxy.statics.game.GamePlayer").getMethod("wait", Long.TYPE);
            m4 = Class.forName("com.proxy.statics.game.GamePlayer").getMethod("login", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
            m8 = Class.forName("com.proxy.statics.game.GamePlayer").getMethod("wait", Long.TYPE, Integer.TYPE);
            m7 = Class.forName("com.proxy.statics.game.GamePlayer").getMethod("getUserName");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

 

有没有一种离真理越来越近的感觉!接下来这个半路出家的一个代理类。我们可以看到它继承了Proxy类,并且实现了GamePlay类接口,并且重写了login()等方法。在静态块中用反射查找到了目标对象的所有方法并且保存了所有方法的引用,重写的方法用反射调用目标对象的方法。然来如此!这不是把静态代理给还原了吗!?接下来应该就很容易推测到,为什么我们需要实现InvocationHandler接口了,答案一定是在继承的Proxy类上!我们再来看看Proxy类(有兴趣的小伙伴可以自行去看看Proxy类的究竟,这里贴出部分代码):

public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;

    /** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    /**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;  //这里持有了InvocationHandler的引用,我们的GamePlayerJdkDynProxy InvocationHandler吗?
}

  从Proxy类中我们可以看出,Proxy持有了InvocationHandler的引用,我们的GamePlayerJdkDynProxy InvocationHandler吗?看到这里,大家是不是就能理解动态代理的原理了呢,实际上JDK的动态采用了字节码重组,重新生成对象来替代原始对象,以达到动态代理的目的。

  JDK动态代理生成对象的步骤如下:

  1. 获取被代理对象的引用,并且反射获取它的所有接口;
  2. JDk动态代理类重新生成生成一个新的类,同时新的类要实现被代理类实现的所有接口(这个类可以理解为静态代理的代理类);
  3. 动态生成java代码,新加的业务逻辑方法由一定的逻辑代码调用。(这种感觉很像用代码写代码,其实很多自动生成代码的技术都是这个原理);
  4. 编译新生成的Java代码.class文件;
  5. 重新加载到JVM中运行。 

 

  

  




代理模式详解(二)-动态代理

原文:https://www.cnblogs.com/chalice/p/12727254.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!