目录
七、代理模式
1、概述
2、应用场景
3、优缺点
优点
缺点
4、主要角色
5、静态代理实现
公共出租接口
房东类
代理类
测试类
运行结果
评价
6、动态代理实现
公共出租接口
房东类
计时器类
动态代理类
测试类
运行结果
评价
七、代理模式
1、概述
第二次梳理:代理模式强调两点:静态代理适合提供个性化的服务,动态代理适合提供统一的服务!时间:2021年05月14日 10时07分01秒
代理模式就是字面意思:代理、中介;现实生活中,有很多这样的角色,比如婚介所、房屋中介、经纪人等;代理人角色往往是提供了更多的服务,比如婚介所,帮你完成相亲前前后后的工作;通过代理,我们获得了额外的服务!我们去一个饭店吃饭,饭店不仅提供了饭菜,还提供了就餐场所、桌椅、碗筷、辣椒、粗、蒜、饮料、空调等等,我们本来是要去吃饭,除了饭菜之外的都是额外的服务,如果有第三方把这些服务接管了,这也是一种代理(当然这种代理似乎不存在)!再比如格力空调代理商,格力生产空调,代理商从格力拿货,卖给经销商/零售商,代理商也提供了比如运输等额外的服务!也就是说,代理的目的是为了提供额外的服务,如果没有提供额外的服务,那么代理就没有意义!拼多多原本是一个消灭代理商的生意,号称工厂直接发货,其实未必,因为不是所有的工厂都愿意把活干完,有的工厂就想纯粹生产产品,不想操销售的心,那么就需要代理商!代理商也是非常重要的销售渠道!很多企业不仅自己有网上旗舰店,也把或分给代理商,看上的就是代理商的销售渠道!某种意义上来讲,代理商的额外服务就是销售渠道!所以说代理肯定是提供了额外的服务!(这部分当时思路满天飞,不懂没关系!时间:2021年05月14日 14时07分22秒)
代理模式:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式分为静态代理和动态代理;
2、应用场景
当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
- 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间;
- 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉;
- 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限;
- 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它;
- 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载;
3、优缺点
在现实世界,代理提供了额外的服务,减轻了交易双方的负担,强化了社会分工,为社会提供了更多的就业岗位!
优点
代理提供了额外的服务(功能)
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性;
缺点
代理确实增加了逻辑关系的复杂性
- 代理模式会造成系统设计中类的数量增加;
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;
4、主要角色
- 抽象主体(Subject)类:通过接口或抽象类声明真实主体和代理对象实现的业务方法;
- 真实主体(Real Subject)类:实现了抽象主体中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象;
- 代理(Proxy)类:提供了与真实主体相同的接口,其内部含有对真实主体的引用,它可以访问、控制或扩展真实主体的功能;
5、静态代理实现
这是一个租房的例子,房东只想出租自己的房子,不想操额外的心,这个时候就有房屋中介,租客租房子的时候,不直接找房东,而是由中介带着看房、签合同什么的,中介提供了额外的服务。
公共出租接口
公共出租接口被房东和中介实现,房东是真正出租房子的人,中介做一些出租房子前后的辅助工作,出租房子调用房东的出租方法
package com.zibo.design.three;
public interface Rent {
void rent();
}
房东类
真正出租房子的人,出租房子具体的行为是房东自己做的
package com.zibo.design.three;
// 房东
public class Landlord implements Rent {
@Override
public void rent() {
System.out.println("房东出租房子!");
}
}
代理类
帮助房东出租房子的人,负责前后打理,真正出租房子的时候调用房顶出租的方法
package com.zibo.design.three;
// 房屋代理
public class HouseAgent implements Rent {
private final Landlord landlord;
public HouseAgent(Landlord landlord) {
this.landlord = landlord;
}
@Override
public void rent() {
System.out.println("带租客看房……");
System.out.println("签合同……");
landlord.rent();
System.out.println("收房租……");
}
}
测试类
package com.zibo.design.three;
public class Tenant {
public static void main(String[] args) {
new HouseAgent(new Landlord()).rent();
}
}
运行结果
带租客看房……
签合同……
房东出租房子!
收房租……
评价
静态代理很常见,适合提供个性化的服务,每个被代理方法前后有着不同的处理逻辑!
6、动态代理实现
代理类在程序运行时被创建的方式成为动态代理。我们静态代理的例子代理类是自己定义好的,在程序运行之前就已经编写完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理,动态代理的优势在于可以很方便地对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
也可以通过CG Lib实现动态代理,这里就省略了!主要学习代理的思想!
公共出租接口
package com.zibo.design.three;
public interface Rent {
void rent();
}
房东类
package com.zibo.design.three;
// 房东
public class Landlord implements Rent {
@Override
public void rent() {
try {
// 假设房东出租房子花了1秒时间
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("房东出租房子!");
}
}
计时器类
package com.zibo.design.three;
public class MonitorUtil {
private static final ThreadLocal<Long> tl = new ThreadLocal<>();
public static void start(){
tl.set(System.currentTimeMillis());
}
//结束时打印耗时
public static void finish(String methodName){
long finishTime = System.currentTimeMillis();
System.out.println(methodName + "方法耗时" + (finishTime-tl.get()) + "ms");
}
}
动态代理类
package com.zibo.design.three;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LandlordInvocationHandler implements InvocationHandler {
// InvocationHandler持有的被代理对象
Object target;
// 带参构造函数
public LandlordInvocationHandler(Object target) {
this.target = target;
}
/**
* @param proxy 动态代理的对象
* @param method 正在执行的方法
* @param args 调用目标方法时传入的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行" +method.getName() + "方法");
// 代理过程中插入计算方法,计算方法耗时
MonitorUtil.start();
// 执行被代理的方法,方法前后可以执行一些其他方法(额外的服务)
Object result = method.invoke(target, args);
MonitorUtil.finish(method.getName());
return result;
}
}
测试类
package com.zibo.design.three;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
// 创建一个房东对象——被代理对象
Rent rent = new Landlord();
// 创建一个与被代理对象相关联的InvocationHandler
LandlordInvocationHandler landlordHandler = new LandlordInvocationHandler(rent);
// 创建一个中介(接口对象)来代理房东,代理对象执行的每个方法都会替换执行Invocation中的invoke方法
Rent agent = (Rent) Proxy.newProxyInstance(Rent.class.getClassLoader(),new Class<?>[]{Rent.class},landlordHandler);
// 代理执行方法
agent.rent();
}
}
运行结果
代理执行rent方法
房东出租房子!
rent方法耗时1004ms
评价
Spring AOP就是通过动态代理实现的,动态代理用于提供统一的服务,每个被代理方法前后处理逻辑是一样的;