美文网首页
深入浅出理解—代理模式

深入浅出理解—代理模式

作者: JackDaddy | 来源:发表于2020-05-11 10:05 被阅读0次

众所周知,Java中存在着23种设计模式,今天本少爷要来介绍的就是其中一个比较常见的设计模式-代理模式。
主要通过以下几个问题来解释代理模式:

  1. 什么是代理模式?
  2. 代理模式有什么好处?
  3. 在Java中是如何实现代理模式的?
  4. 代理模式的实际应用场景有哪些?

那接下来我们来一步一步解开代理模式的神秘面纱。

代理模式

我们知道在面向对象的世界中,万物都是对象,代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

通俗来讲,代理模式就是我们生活中的中介。比如,张三按照小卡片上的电话打过去寻求服务,一般不是由本人,可能是一个成年雄性接听电话,然而真正做事情的可能是另一个小姐姐。

通过使用代理模式,主要有以下两个好处:
1) 通过引入代理对象来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
2) 通过代理对象对访问进行控制

在Java中主要是通过接口的方式来实现代理模式的:
主要有以下三个角色:
1) 抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个 接口

2) 真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。

3) 代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理。 代理模式

代理模式中又分为了 静态代理 和 动态代理,两者的区别主要是代理对象的生成方式。
静态代理:代理对象的生成的是在JVM编译.java文件时生成一个.class文件,存在于硬盘中。

动态代理:代理对象是在JVM编译时,通过反射,以内存的方式生成一个.class文件,存在于内存中。 字节码生命周期 下面先来看看静态代理的实现方式:
1) 首先定义一个抽象接口,也就是前面提到的 抽象角色
public interface Drive {
    //所需要的代理方法
    void drive();
}

2) 接下来定义两个真实角色类,让他去实现刚才的接口,在接口中做这个角色所需要的任务

public class Daslen implements Drive{
    //真实角色实现代理接口,进行真实操作
    @Override
    public void drive() {
        System.out.println("达乐森——>开车很流弊");
    }
}
-------------------------------------------------------------------------------
public class Eason implements Drive{
    @Override
    public void drive() {
        System.out.println("依乐森——>开车很奔放");
    }
}

3)定义一个代理类,通过实现抽象接口来持有真实角色,同时在执行真实角色的动作 前/后 都可以添加自己所需要的操作

public class Agent implements Drive{

    private Drive drive;

    public Agent(Drive drive) {
        this.drive = drive;
    }

    //代理前动作
    private void beforeDrive(){

    }

    //代理后动作
    private void afterDrive(){

    }

    @Override
    public void drive() {
        //通过代理类可以在实际操作代理对象之前和之后进行操作
        beforeDrive();
        this.drive.drive();
        afterDrive();
    }
}

4)进行调用

public class Client {

    //静态代理
    public static void main(String[] args) {

        //真实角色->达乐森
        Drive daslen = new Daslen();
        //真实角色->依乐森
        //当实现同一个代理接口,代理功能的时候,静态代理可以实现一个 实际代理类 
        //代理 具有相同代理功能的多个真实角色代理类
        Drive Eason = new Eason();

        //代理角色->Daslen
        Agent agentDaslen = new Agent(daslen);
        //通过代理角色操作真实角色
        agentDaslen.drive();

        //代理角色->Eason
        Agent agentEason = new Agent(daslen);
        agentEason.drive();
    }
}

而当需要实现新的功能时,此时就需要去添加新的代理类对象,当这种情况出现成千上万的时候仍然使用静态代理的方式显然是不可取的,此时就需要通过 动态代理 ,通过反射从内存中去生成一个我们所需要的代理对象类:
Java中为我们提供了一系列的API,首先来看一下是如何使用的吧:

1)再定义一个接口,体现出与静态代理的区别:

public interface Message {
    //所需要的代理方法
    void message();
}

2) 动态代理的的实现

    //动态代理
    public static void main(String[] args) throws Exception {

        proxy();


        //真实角色
        final Daslen daslen = new Daslen();
        final Eason eason = new Eason();


        //动态代理 ->通过动态获取实际代理对象
        Object o = Proxy.newProxyInstance(Client.class.getClassLoader(),
                new Class[]{Drive.class, Message.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        String name = method.getName();
                        if (name.equals("drive")) {
                            method.invoke(daslen, args);
                        } else if (name.equals("message")) {
                            method.invoke(eason, args);
                        }
                        return null;
                    }
                });

        //通过代理对象操作实际对象
        ((Drive) o).drive();

        ((Message) o).message();

    }

其中有一个 InvocationHandler ,可以理解为一个监听,类似于点击监听(onClickListenen),通过这个监听可以知道调用了真实角色的哪些方法,当有传参数时也可以通过这个Handler的 invoke 方法获取到。
Proxy.newProxyInstance生成的对象就是我们传入的 new Class[]{Drive.class, Message.class} 的代理类对象。

我们通过进入到 Proxy.newProxyInstance 的 newProxyInstance 方法中去找找是如何生成一个代理类对象的。
1)首先将传入的接口对象复制一次,然后调用 getProxyClass0 方法。

public static Object newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2) throws IllegalArgumentException {
        Objects.requireNonNull(var2);
        //复制传入的接口对象
        Class[] var3 = (Class[])var1.clone();
        SecurityManager var4 = System.getSecurityManager();
        if (var4 != null) {
            checkProxyAccess(Reflection.getCallerClass(), var0, var3);
        }
        //生成代理类
        Class var5 = getProxyClass0(var0, var3);

        try {
            if (var4 != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), var5);
            }
            //通过反射生成构造方法
            final Constructor var6 = var5.getConstructor(constructorParams);
            if (!Modifier.isPublic(var5.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        var6.setAccessible(true);
                        return null;
                    }
                });
            }
            //生成接口对象实例
            return var6.newInstance(var2);
        } catch (InstantiationException | IllegalAccessException var8) {
            throw new InternalError(var8.toString(), var8);
        } catch (InvocationTargetException var9) {
            Throwable var7 = var9.getCause();
            if (var7 instanceof RuntimeException) {
                throw (RuntimeException)var7;
            } else {
                throw new InternalError(var7.toString(), var7);
            }
        } catch (NoSuchMethodException var10) {
            throw new InternalError(var10.toString(), var10);
        }
    }

当调用了 getProxyClass0 方法时,会调用到ProxyGenerator.generateProxyClass,通过以下这段代码可以查看通过内存的生成的代理类是什么

private static void proxy() throws Exception {
        String name = Drive.class.getName() + "$Proxy0";
        //生成代理指定接口的Class数据
        byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Drive.class});
        FileOutputStream fos = new FileOutputStream("lib/" + name + ".class");
        fos.write(bytes);
        fos.close();
    }

以下是生成的代理类对象

public final class Drive$Proxy0 extends Proxy implements Drive {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public Drive$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 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 drive() throws  {
        try {
            super.h.invoke(this, m3, (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);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.example.lib.dynamic_agency.Drive").getMethod("drive");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

从以上可以看得出来里面除了有我们传入的 Drive 方法,同时还有equalstoString以及 hashCode这三个方法,这间接也 说明了所有类都是继承自 Object类。

有一点值得注意的是调用这里面所有的方法,都会调用到 hinvoke 方法:

invoke
而这个 h 就是我们新建的传入的 InvocationHandler,因此,这样才能监听到调用了代理对象执行了哪个方法。

应用场景

说完对动态代理,下面来说一下代理模式的应用场景吧。
相信我们在开发的过程都会去请求网络,如果我们采用 Java 自带的网络框架去请求网络就会显得很臃肿,因此我们一般会选用一些网络请求框架进行封装,最后进行调用,整个框架如图:

网络层架构 在这种网络架构下,看起来很完美,对网络框架都进行了封装,当应用层需要请求网络时调用一下工具层的 post 方法,最后通过框架的具体方法去请求网络。

如果在这种架构下需要更换网络框架会发生什么问题呢?
虽然对每个框架都进行了封装,但是每个框架的入参以及其他返回参数等都不一样,这就导致了工具层的 post 方法需要做出对应的修改,同时应用层在调用的地方也需要做出相应的修改,这显然是不符合开闭原则的。

而代理模式就可以很好的解决这个问题,在上面这种架构下的工具层下面在加多一层代理层,也就是一个网络请求接口,而在工具层中的 post 方法也实现了这个接口,在构造方法中传入一个代理对象,而不同的框架也去实现这个接口,在实现方法中去做不同的请求方法,架构如下图:

代理模式在网络架构中的应用 代理层是一个接口(post),如果有多个框架,则每个框架都去实现该接口(post),在实现方法中做每个框架的不同的请求操作。

应用层也去实现代理层的接口,构造方法传入的代理层接口对象,在接口的实现方法中调用 传入的代理层接口对象 的post方法

这就印证了开闭原则

以上就是本篇文章的全部内容,希望对你有所帮助~

相关文章

  • 深入浅出理解—代理模式

    众所周知,Java中存在着23种设计模式,今天本少爷要来介绍的就是其中一个比较常见的设计模式-代理模式。主要通过以...

  • Java动态代理解析

    动态代理原理解析 一. 代理模式例子: 目标类及代理类统一接口 目标实现类 自定义的代理模式处理程序 4.代理模式...

  • 理解代理模式

    1.代理模式的主旨 要想实现代理模式,就要有三个组成部分,一个是协议,一个是代理者,一个是委托者。 我们需要定义一...

  • 理解代理模式

    原创博客地址 简介 代理模式,也叫做委托模式,分为:静态代理动态代理 代理模式也是平时比较常用的设计模式之一,代理...

  • 阿里P8从设计模式基础知识入手,抽丝剥茧总结出高并发核心笔记

    内容简介 本书从动态代理模式、Reactor模式、三大限流策略等知识入手,深入浅出地剖析Spring Cloud+...

  • Java动态代理

    一,简介 在将动态代理之前,先按照我自己的理解来讲讲代理模式,和静态代理 二,代理模式 我们常常说代理模式是一种j...

  • 设计模式(六) -- 代理模式

    什么是代理模式 代理模式可以理解成一个类代表另一个类的功能并可加自己的代理功能。简单来说可以理解成汽车代理商代理汽...

  • 代理模式和动态代理实战应用

    代理设计模式 java有20多种设计模式,代理模式肯定是非常出名的一种。代理模式可以理解为不直接访问对象,让代理对...

  • 委派模式的使用

    一、模式介绍 委派模式负责任务的分配和调用,是一种特殊的静态代理模式,可以理解为全权代理模式,代理模式注重过程,委...

  • Proxy代理者模式(一)

    摘要 本篇笔记针对Java设计模式中最难理解的代理者模式进行讲解,从静态代理、动态代理,及Java相关代理类的应用...

网友评论

      本文标题:深入浅出理解—代理模式

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