代理模式 (静态/动态)

代理模式

简单来说,代理模式就是通过访问代理对象替代对目标对象的访问,以实现在不修改目标对象的前提下,可以提供额外的功能或扩展目标对象的功能

举个简单例子,如果目标对象有两个方法:

如果此时客户端想要:获取 x 的值加 1、获取 y 的值加 2、获取 x + y 的值,那么在不修改目标对象的前提下可以提供一个代理对象,它包含三个方法:

对于Proxy类中的getXPlus()getYPlus()方法,相当于扩展了目标对象的功能;对于Proxy类中的sum()方法,相当于提供了额外的功能

用大白话来说,客户端和目标对象通信,实际上是通过代理对象作为中间方来实现,至于中间方传话的过程中有无添油加醋就由代理类的实现来决定了,如下图所示:

1

从上面的介绍中可以总结出代理模式中有三个主要角色:

三个角色的关系如下图所示:

2

注意:对于代理对象,更多的是增强目标对象的功能,也就是在调用目标对象方法前后添加个性化处理,而并非改变目标对象的功能

在 Spring 中的应用

本部分用大白话介绍一下代理模式在 Spring 中的应用,因为这玩意很重要,但又很抽象,如果一开始对「代理」的定位不明确,很有可能会影响后面对动态代理的理解

在 Spring 中,很多地方都用到了代理模式,更准确来说是动态代理,但这里先不区分到底用到了哪种代理,仅仅介绍代理模式的思想如何在 Spring 中体现,用 Spring 中的「拦截器」例子展开讨论

在 Spring 中,访问一个 URL 首先会进入 Controller 层对应的方法中,如果要对某个网页进行拦截,一般会在拦截 Controller 层的方法

举个简单的应用场景,如果用户在没有登陆的情况下,访问了某个需要登录后才能访问的页面,会跳转到登陆界面

假设登陆页面为/login,对应的 Controller 层方法为login();需要登录后才能访问的页面为/test,对应的 Controller 层方法为test()

当用户访问/test页面时,实际并不是直接调用 Controller 对象的test()方法,而是调用 Controller 对象的代理对象的对应方法

在代理对象中,会在调用test()方法之前判断用户是否合法,如果未登录,直接跳转到/login页面;如果已登录,才会调用test()方法,具体如下图所示:

4

拦截器在代理层实现,它已内置到框架中,程序员只需要简单的配置即可实现拦截的功能

我们在使用 Spring 框架时,它已经被编译打包好,所以对于代理层来说,应用程序属于黑盒,Spring 打包时完全不知道它的存在

静态代理

从 JVM 角度来说,类会先经过编译生成.class字节码文件,然后通过类加载子系统加载到内存中,并生成对应 Class 对象

静态代理就是在编译时将接口、真实类、代理类生成为一个个实际的.class字节码文件,而且需要为每一个真实类创建一个对应的代理类,一旦接口有修改,真实类和代理类都需要修改,麻烦且不灵活

代理类由 Spring 框架生成,而真实类由用户创建,在 Spring 打包时完全不知道用户程序的存在,所以静态代理实际应用场景非常少,日常开发过程中几乎看不到使用它的场景

静态代理实现步骤:

上述步骤如下图所示:

3

Demo

下面给出一个发送短信的 Demo!!

一:定义发送短信的接口

二:实现发送短信的接口,充当真实类

三:创建代理类并实现发送短信的接口

四:测试

五:输出

动态代理

动态代理不再需要为每一个目标类创建一个对应的代理类,而且也并没有强制要求代理类必须实现接口,更加的灵活。从 JVM 角度来说,动态代理是在运行时动态的生成所需的类字节码,并加载到 JVM 中

在 Spring 打包时并不知道应用程序的存在,只有在程序运行过程中,Spring 才能感知到应用程序,所以动态代理可以让 Spring 在运行时生成应用程序的代理类

就 Java 来说,动态代理实现的方式有很多种,如:JDK 动态代理、CGLIB 动态代理等,下面主要介绍这两种实现方式

JDK 动态代理

JDK 动态代理和静态代理主要有两点不同:

JDK 动态代理结构如下图所示:

5

下面介绍 JDK 动态代理中用到的Proxy类和InvocationHandler接口

同样的,给出一个小 Demo~

CGLIB 动态代理

JDK 动态代理最致命的问题是只能代理实现了接口的类,也就是必须要有接口。为了解决这个问题,可以使用 CGLIB 动态代理机制,它可以代理没有实现接口的类。除了该区别外,感觉 GCLIB 和 JDK 很像

下面直接给出一个 Demo。首先 GCLIB 是一个开源项目,需要导包:

然后给出其它代码:

JDK 动态代理和 CGLIB 动态代理对比

静态代理和动态代理对比