# 代理模式
1.简介
代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
代理模式建议新建一个与原服务对象接口相同的代理类, 然后更新应用以将代理对象传递给所有原始对象客户端。 代理类接收到客户端请求后会创建实际的服务对象, 并将所有工作委派给它。
这有什么好处呢?如果需要在类的主要业务逻辑前后执行一些工作, 你无需修改类就能完成这项工作。 由于代理实现的接口与原类相同, 因此你可将其传递给任何一个使用实际服务对象的客户端。
2.UML图
- 服务接口(Service Interface) 声明了服务接口,代理必须遵循该接口才能伪装成服务对象。
- 服务(Service)类提供了一些实用的业务逻辑。
- 代理(proxy) 类包含一个指向服务对象的引用成员变量。代理完成其任务,后台会将请求传递给服务对象
- 客户端(Client) 能通过同一接口与服务或代理进行交互,所以你可以在一切需要服务对象的代码中使用代理。
3.代码示例
首先我们需要知道有3种代理模式。静态代理、jdk的动态代理和 cglib
的动态代理。
假设现在某一个打工人需要打官司来讨回亏欠的薪资,那么首先它需要找一名代理律师来负责处理诉讼等相关事宜。这里相当于代理律师会全权代理这个打工人。
静态代理:是指预先确定了代理和被代理者的关系。假如打工人A的代理律师确认为为B,相当于代理类和被代理类的依赖关系在编译期间就需要被确认。
定义一个诉讼的接口:
java
package com.gs.designmodel.daili.jingtai;
/**
* @author: Gaos
* @Date: 2023-08-07 10:12
*
* 诉讼接口
**/
public interface ILawSuit {
/**
* 提起诉讼
*/
void submit(String proof);
/**
* 法律辩护
*/
void defend();
}
打工人小明,需要实现 ILawSuit
接口:
java
package com.gs.designmodel.daili.jingtai;
/**
* @author: Gaos
* @Date: 2023-08-07 10:15
*
* 小明诉讼类型
**/
public class XiaoMing implements ILawSuit{
@Override
public void submit(String proof) {
System.out.println("老板带着小姨子欠薪跑路了,证据如下: " + proof);
}
@Override
public void defend() {
System.out.println("审判结果,必须还钱");
}
}
代理律师类 也需要实现 ILawSuit
接口并持有打工人对象的引用:
java
package com.gs.designmodel.daili.jingtai;
/**
* @author: Gaos
* @Date: 2023-08-07 10:19
*
* 代理律师诉讼类
**/
public class ProxyLawyer implements ILawSuit{
/**
* 需要代理的对象
*/
private ILawSuit plaintiff;
public ProxyLawyer(ILawSuit plaintiff) {
this.plaintiff = plaintiff;
}
@Override
public void submit(String proof) {
plaintiff.submit(proof);
}
@Override
public void defend() {
plaintiff.defend();
}
}
产生代理对象的静态代理工厂类:
java
package com.gs.designmodel.daili.jingtai;
/**
* @author: Gaos
* @Date: 2023-08-07 10:21
**/
public class ProxyFactory {
public static ILawSuit getProxy() {
return new ProxyLawyer(new XiaoMing());
}
}
测试类:
java
package com.gs.designmodel.daili.jingtai;
/**
* @author: Gaos
* @Date: 2023-08-07 10:22
**/
public class Test {
public static void main(String[] args) {
ProxyFactory.getProxy().submit("工资卡流水");
ProxyFactory.getProxy().defend();
}
}
结果:
makefile
老板带着小姨子欠薪跑路了,证据如下: 工资卡流水
审判结果,必须还钱
这样的好处是我们可以简单的进行扩展,比如说我们需要在原先接口的基础上加上证据校验、法官谈判等流程,就不需要改变原本类的代码。
但是缺点也很明显,静态代理需要为每一个对象都创建一个代理类,增加了维护成本以及开发成本。那么为了解决这个问题,动态代理的 概念就出来了,不需要在固定为每一个需要代理的类创建一个代理类,而是在运行时通过反射机制创建。
JDK动态代理:
首先要理解:Proxy
可以理解为调度器,用来创建动态代理类实例对象的,只有得到了这个对象我们才能调用那些需要代理的方法,InvocationHandler
增强服务接口可以理解为代理器,是给动态代理类实现的,负责处理被代理对象操作的。
在业务的层面上可以理解为小明的代理律师其实一直都没有确认,直到开庭的时候才为他匹配一个律师,说明他们的关系是在运行期间确认的。
小华诉讼类:
java
package com.gs.designmodel.daili.dongtai;
import com.gs.designmodel.daili.jingtai.ILawSuit;
/**
* @author: Gaos
* @Date: 2023-08-07 10:24
*
* 小华诉讼类
**/
public class XiaoHua implements ILawSuit {
@Override
public void submit(String proof) {
System.out.println("老板带着小姨子欠薪跑路了,证据如下: " + proof);
}
@Override
public void defend() {
System.out.println("审判结果,10号之前必须还钱");
}
}
构建一个动态代理类:
java
package com.gs.designmodel.daili.dongtai;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author: Gaos
* @Date: 2023-08-07 10:25
*
* 动态代理类
**/
public class DynProxyLawyer implements InvocationHandler {
private Object target;
public DynProxyLawyer(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("法庭公告案件进展: " + method.getName());
// 执行
Object result = method.invoke(target, args);
return result;
}
}
静态工厂方法:
java
package com.gs.designmodel.daili.dongtai;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @author: Gaos
* @Date: 2023-08-07 10:28
**/
public class DongProxyFactory {
public static Object getDynProxy(Object target) {
InvocationHandler handler = new DynProxyLawyer(target);
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
}
}
测试类:
java
package com.gs.designmodel.daili.dongtai;
import com.gs.designmodel.daili.jingtai.ILawSuit;
/**
* @author: Gaos
* @Date: 2023-08-07 10:29
**/
public class Test {
public static void main(String[] args) {
ILawSuit proxy = (ILawSuit) DongProxyFactory.getDynProxy(new XiaoHua());
proxy.submit("工资卡流水如下:");
proxy.defend();
}
}
结果:
java
法庭公告案件进展: submit
老板带着小姨子欠薪跑路了,证据如下: 工资卡流水如下:
法庭公告案件进展: defend
审判结果,10号之前必须还钱
JdK
的动态代理实现方法是依赖于接口的,首先使用接口来定义好操作的规范,然后通过Proxy
类产生的代理对象调用被代理对象的方法,而这个操作又被分发给 InvocationHandler
接口的 invoke
方法具体执行。
java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
proxy
:代表当前动态代理对象method
:代表正在执行的方法args
:代表当前执行方法传入的实参
cglib动态代理:
由于 JDK
只能针对实现了接口的类做动态代理,而不能对没有实现的类做操作,所以还是有一定的局限性。CGLib(Code Generation Library)是一个强大、高性能的Code生成类库,它可以在程序运行期间动态扩展类或接口,它的底层是使用java字节码操作框架ASM实现。
定义业务类,被代理的类没有实现任何接口:
java
package com.gs.designmodel.daili.cglib;
/**
* @author: Gaos
* @Date: 2023-08-07 10:33
*
* 定义业务类,被代理的类没有实现任何接口
**/
public class Frank {
public void submit(String proof) {
System.out.println("老板带着小姨子欠薪跑路了,证据如下: " + proof);
}
public void defend() {
System.out.println("审判结果,10号之前必须还钱");
}
}
定义拦截器:在调用目标方法时,会回调 MethodInterceptor
接口方法拦截,来实现你自己的代理逻辑:
java
package com.gs.designmodel.daili.cglib;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author: Gaos
* @Date: 2023-08-07 10:34
*
* 拦截器 类似jdk中的InvocationHandler
**/
public class CgLibDynProxyLawyer implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if(method.getName().equals("submit")) {
System.out.println("案件提交成功,证据如下: " + Arrays.asList(objects));
}
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
}
定义动态代理工厂,生成动态代理:
java
package com.gs.designmodel.daili.cglib;
import org.springframework.cglib.proxy.Enhancer;
/**
* @author: Gaos
* @Date: 2023-08-07 10:37
**/
public class CgProxyFactory {
public static Object getCgLibDynProxy(Object targe) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targe.getClass());
enhancer.setCallback(new CgLibDynProxyLawyer());
Object targetProxy = enhancer.create();
return targetProxy;
}
}
测试类:
java
package com.gs.designmodel.daili.cglib;
/**
* @author: Gaos
* @Date: 2023-08-07 10:38
**/
public class Test {
public static void main(String[] args) {
Frank cProxy = (Frank) CgProxyFactory.getCgLibDynProxy(new Frank());
cProxy.submit("银行卡记录在此");
cProxy.defend();
}
}
结果:
css
案件提交成功,证据如下: [银行卡记录在此]
老板带着小姨子欠薪跑路了,证据如下: 银行卡记录在此
审判结果,10号之前必须还钱
可以看到 cglib
对没有实现任何接口的类做了动态代理,达到了和之前一样的效果。
它的原理是动态生成一个要代理类的子类,子类重写要代理的类所有不是final
修饰的方法。在子类种采用方法拦截的技术拦截所有父类方法的调用,然后走自己的逻辑。它要比java反射的jdk动态代理快。
4.总结
JDK和Cglib的区别:
jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
cglib动态代理是利用ASM开源包,对被代理对象类的class文件加载进来,通过修改其字节码生成子类来处理
ASM: 一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
动态代理的常见的运用是在大名鼎鼎的Aop
当中。使用了动态代理后,不会侵入业务代码中,在以后的维护过程中对代码毫无影响,广泛地应用到了日志记录、性能统计、安全控制、异常处理等场景中。