代理(Proxy)设计模式

本贴最后更新于 1851 天前,其中的信息可能已经时异事殊

概述

正文开始之前我们先考虑一个问题:什么叫做 代理(Proxy)
按照 维基百科 定义:
代理(英语:Proxy)也称网络代理,是一种特殊的网络服务,允许一个网络终端(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。一些网关、路由器等网络设备具备网络代理功能。一般认为代理服务有利于保障网络终端的隐私或安全,防止攻击。
通俗讲 代理 就类似一个连接在客户端和服务器之间的桥梁,连通客户端和服务器之间的请求和响应,代理的存在一方面可以保护服务器的安全,在代理部分可以对请求信息进行过滤,隔绝一部分非法的请求信息吗,另一方面可以提高用户的访问速度,其具体功能可以借助下边的图来帮助理解。
测试

如果你理解上述 代理 的概念,那么 代理设计模式 也就不难理解了。代理设计模式 就是对上边上 客户端-代理-服务器 三者链式关系的一种抽象,进而应用到软件开发中的一种通用设计模式。
代理设计模式有如下三个优点:

  1. 保护真实对象
  2. 让对象职责更加明确
  3. 易于扩展

在 java 开发中代理设计模式有三种实现方法:

  • 静态代理
  • 动态代理 jdk 实现
  • 动态代理 cglib 实现
    下边我们分三种情况对这三种代理设计模式的实现进行讨论和分析

静态代理

UML 类图

  • KeHu :客户端
  • MiShhu:中介
  • LaoZong:服务器
  • GongNeng:服务器和中介要同时实现的功能接口

代码实现

GongNeng 的 java 代码:


/**
 * @program: TestBlog
 * @description:
 秘书和老总都要实现的功能接口
 * @author: vcjmhg
 * @create: 2019-10-07 16:31
 **/
public interface GongNeng {
    public void ZuoShengYi();
    public void eat();
}

Kehu 的 java 代码

/**
 * @program: TestBlog
 * @description:
 客户相当于客服端
 * @author: vcjmhg
 * @create: 2019-10-07 16:31
 **/
public class KeHu {
    public static void main(String[] args) {
        MiShu miShu=new MiShu();
        miShu.ZuoShengYi();
    }
}

MiShu 的 java 代码

/**
 * @program: TestBlog
 * @description:
 * @author: vcjmhg
 * @create: 2019-10-07 16:31
 **/
public class MiShu implements GongNeng{
    private LaoZong laoZong=new LaoZong();
    public void ZuoShengYi() {
        System.out.println("秘书:请问您预约来吗?");
        laoZong.ZuoShengYi();
        System.out.println("秘书备注访客信息");
    }

    public void eat() {
        System.out.println("秘书:请问您预约来吗?");
        laoZong.eat();
        System.out.println("秘书备注访客信息");
    }
}

LaoZong 的 java 代码:

package proxy.staticproxy;

/**
 * @program: TestBlog
 * @description:
 * @author: vcjmhg
 * @create: 2019-10-07 16:31
 **/
public class LaoZong implements GongNeng{

    public void ZuoShengYi() {
        System.out.println("老总:谈个小项目!!");
    }

    public void eat() {
        System.out.println("老总:吃饭!!");
    }
}

运行结果为:

秘书:请问您预约来吗?
老总:谈个小项目!!
秘书备注访客信息

Process finished with exit code 0

代码地址

详细的代码可以参看 github 的上的代码

静态代理的不足

毫无疑问静态代理作为最容易实现或者说最直观的的代理设计模式的实现方式,代理模式具有的优点它必然也具有,但另一方面它也有许多缺点:

  1. 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
  2. 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。
    为了解决该问题我们引入了动态代理

动态代理之 jdk 实现

UML 类图

  • Client :客户端
  • MiShhu:中介
  • LaoZong:服务器
  • GongNeng:服务器和中介要同时实现的功能接口

代码实现

Client 的 java 代码

/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 17:04
**/
public class Client {
   public static void main(String[] args) {
       //第一个参数:反射时使用的类加载器
       //第二个参数:Proxy需要实现什么接口
       //第三个参数:通过接口对象调用方法时,需要调用哪个类的invoke方法
       GongNeng gongneng = (GongNeng) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{GongNeng.class}, new MiShu());
       gongneng.eat();
   }
}

GongNeng 的 java 代码:

public interface GongNeng {
   public void ZuoShengYi();
   public  void eat();
}

MiShu 的 java 代码

/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:56
**/
public class MiShu implements InvocationHandler {
   private LaoZong laozong=new LaoZong() ;
   //代理类针对被代理对象类似的功能不需要重复实现多次
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       System.out.println("预约时间");
       Object result = method.invoke(laozong, args);
       System.out.println("记录访客信息");
       return result;
   }
}

LaoZong 的 java 代码:

/**
 * @program: TestBlog
 * @description:
 * @author: vcjmhg
 * @create: 2019-10-07 16:49
 **/
public class LaoZong implements GongNeng{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public void ZuoShengYi() {
        System.out.println("老总:谈生意");
    }

    public void eat() {
        System.out.println("老总:吃饭!!");
    }
}

运行结果为:

预约时间
老总:吃饭!!
记录访客信息

Process finished with exit code 0

利用 JDK 实现动态代理的优点

相比与静态代理,利用 JDK 实现动态代理的方式实现了代理类和功能接口之间的解耦。对于委托类如果增加某个方法,对于代理类代码几乎可以不变,减少了代码的复杂性,使其更加易于维护。另一方面在代理不同类型对象时可以实现代码一定程度的复用。

利用 JDK 实现动态代理的不足

但是该方法实现动态代理也有一定不足,由于其内部借助反射实现代理设计模式,系统开销大效率低。而且其委托类仍需实现功能接口,代码耦合性还是不够低。

代码地址

详细的代码可以参看 github 的上的代码

动态代理之 cglib 实现

UML 类图

  • Client :客户端
  • MiShhu:中介
  • LaoZong:服务器

代码实现

Client 的 java 代码

public class Client {
   public static void main(String[] args) {
   	Enhancer enhancer = new Enhancer();
   	enhancer.setSuperclass(LaoZong.class);
   	enhancer.setCallback(new MiShu());
   	
   	LaoZong laozong = (LaoZong) enhancer.create();
   	laozong.chifan();
   	
   }
}

MiShu 的 java 代码:

public class MiShu implements MethodInterceptor{
	public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
		System.out.println("预约时间");
		Object result = arg3.invokeSuper(arg0, arg2);
		System.out.println("备注");
		return result;
	}

}

LaoZong 的 java 代码:

public class LaoZong {
	
	public void chifan() {
		System.out.println("吃饭");
	}

	public void mubiao() {
		System.out.println("目标");
	}

运行结果为:

预约时间
吃饭
备注

Process finished with exit code 0

利用 cglib 实现动态代理的优点

通过 cglib 方式几乎完美的解决来 jdk 方式所具有的缺点一方面 cglib 方式内部是通过字节码方式实现动态代理,效率高,执行速度快;另一方面,该方式解耦了委托类和功能接口之间的耦合,提高了代码的灵活性。

代码地址

详细的代码可以参看 github 的上的代码

  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...