设计模式[5]-代理模式
代理模式
代理模式需要给某对象提供一个代理来访问该对象。是客户端和目标对象之间的一个媒介。
代理模式主要包括三种角色
- 抽象主题:接口或抽象类,有着业务方法,可以让真实主题和代理对象来实现。
- 真实主题:实现了抽象主题的方法,是代理类所代理的真实对象。
- 代理类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
代理模式又分为静态代理和动态代理,静态代理是程序运行前,.class文件已经存在,而动态代理则是利用反射原理,动态创建类。
例子
我们如果有一个计算类,它在方法中进行复杂计算,然后我们要在计算前后进行日志记录,并统计计算的耗时,这时可以用代理模式来实现。
1. 静态代理
Calculate 抽象主题:
public interface Calculate {
long execute(int time);
}
ComplexCalculate 真实主题:
public class ComplexCalculate implements Calculate {
/**
* 在方法里面进行计算,要用到time参数
*/
@Override
public long execute(int time) {
System.out.println("调用了 ComplexCalculate 类中的 execute 方法。参数是" + time);
long i = 1;
for (int j = 0; j < time; j++) {
i += 5;
}
return i;
}
}
ProxyCalculate 代理类:
public class ProxyCalculate implements Calculate {
private ComplexCalculate calculate = new ComplexCalculate();
@Override
public long execute(int time) {
long start = System.currentTimeMillis();
System.out.println("开始执行");
long result = calculate.execute(time);
long end = System.currentTimeMillis();
System.out.println("结束执行, 耗时:" + (end - start) + " ms");
return result;
}
}
测试:
@Test
void execute() {
ProxyCalculate calculate = new ProxyCalculate();
long result = calculate.execute(1000000);
System.out.println("result:" + result);
}
输出:
开始执行
调用了 ComplexCalculate 类中的 execute 方法。参数是1000000
结束执行, 耗时:1 ms
result:5000001
分析:
在ProxyCalculate 代理类中,实现了抽象接口,并且声明了ComplexCalculate真实主题类属性,而实际执行execute方法的则是ComplexCalculate的实例对象。从而实现了通过代理类去访问真实对象,并且在执行方法前可以编写自己的逻辑。
2. 动态代理 jdk
我们用jdk自带的Proxy类来实现动态代理,抽象主题和真实主题不变,和上面的静态代理一样,不同的只是ProxyCalculate 代理类我们改成代理工厂,生成代理对象的实例。
ProxyFactory :
public class ProxyFactory {
private ComplexCalculate calculate = new ComplexCalculate();
/**
* Proxy.newProxyInstance 三个参数:
* ClassLoader loader:类加载器
* Class<?>[] interfaces:代理类接口字节码
* InvocationHandler h:代理对象的调用程序,此处用lambda实现,有三个参数
* Object proxy: 代理对象
* Method method : 调用方法的method对象
* Object[] args : 调用方法的参数
*/
public Calculate getCalculateObj() {
return (Calculate) Proxy.newProxyInstance(
calculate.getClass().getClassLoader(),
calculate.getClass().getInterfaces(),
(proxy, method, args) -> {
long start = System.currentTimeMillis();
System.out.println("开始执行");
// ==== 实际执行的就是这一行,其他的都是额外的自定义逻辑 ====
long result = (long) method.invoke(calculate, args);
// ==== ======================================= ====
long end = System.currentTimeMillis();
System.out.println("结束执行, 耗时:" + (end - start) + " ms");
return result;
});
}
}
测试:
@Test
void execute() {
ProxyFactory factory = new ProxyFactory();
long result = factory.getCalculateObj().execute(1000000);
System.out.println("result:" + result);
}
输出:
开始执行
调用了 ComplexCalculate 类中的 execute 方法。参数是1000000
结束执行, 耗时:1 ms
result:5000001
注意:
被代理的真实对象是需要实现接口才可以用Proxy进行代理的,如果类没有实现接口的话,可以引入cglib包进行代理。
优缺点
代理模式的优点有:
-
可以对目标对象的功能进行拓展,比如上边例子上的日志打印等,
-
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
代理模式的缺点有:
- 增加系统的复杂性
- 在目标对象之前又增加了代理对象,会使请求的处理速度变慢