目录
一、代理模式
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
二、代理模式角色介绍
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
想象这样一个场景:你来到北京,你需要租房,但因为你不熟悉这边的环境,所以你想请一名中介帮你找房子。这个例子我们可以看到两种角色,真实角色,代理角色。但又是如何将这两类角色联系起来的呢,在这个例子中,买房是真实角色(我)的业务,这里我们将真实角色的业务方法叫做抽象角色。代理模式的关键在于代理角色实现真实角色的业务,基于这个例子的逻辑就是我(真实角色)买房(抽象角色),但因为环境不熟悉,我寻找了中介(代理角色)去买房(抽象角色)。
为了建立真实角色和代理角色的联系,我们应该解决的其实是怎么让代理角色去执行真实角色的业务,解决方法可以通过接口或者抽象的方式来实现,在接下来我们只通过接口的方式来实现代理模式。
三、代理模式结构图
四、代理模式实现
代理模式分为静态代理、动态代理。静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。而动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
1. 静态代理模式
// 真实角色
public class OrderServiceImpl implements OrderService {
@Override
public void show() {
System.out.println("展示订单详情");
}
@Override
public void generate() {
System.out.println("订单已生成");
}
@Override
public void delete() {
System.out.println("删除订单");
}
@Override
public int getRows() {
System.out.println("获取总订单条目");
return 100;
}
}
// 抽象角色
public interface OrderService {
void show();
void generate();
void delete();
int getRows();
}
// 代理角色
public class ProxyOrderServiceImpl implements OrderService {
private OrderService target;
public ProxyOrderServiceImpl(OrderService target) {
this.target = target;
}
@Override
public void show() {
System.out.println("查找订单中");
target.show();
}
@Override
public void generate() {
System.out.println("订单生成中,请稍等...");
target.generate();
}
@Override
public void delete() {
target.delete();
}
@Override
public int getRows() {
System.out.println("统计订单数目");
return 100;
}
}
// 客户端
public class StaticProxyClient {
public static void main(String[] args) {
OrderService orderServiceImpl = new OrderServiceImpl();
ProxyOrderServiceImpl proxyOrderService = new
ProxyOrderServiceImpl(orderServiceImpl);
proxyOrderService.show();
proxyOrderService.generate();
}
}
运行结果:
静态代理模式实现对实际对象的访问控制,提高了系统的安全性,此外通过静态代理实现了原始代码与额外逻辑的分离,没有违背OCP原则。然而这种模式的缺点也很明显,实现代理需要建立实际对象和代理对象一对一的关系,如果有大量的实际对象,那么需要创建多个代理对象,显然这会造成大量类的管理,导致代码的冗余。为了解决这个问题,引出动态代理模式。
2. 动态代理模式
// 真实角色
public class OrderServiceImpl implements OrderService {
@Override
public void show() {
System.out.println("展示订单详情");
}
@Override
public void generate() {
System.out.println("订单已生成");
}
@Override
public void delete() {
System.out.println("删除订单");
}
@Override
public int getRows() {
System.out.println("获取总订单条目");
return 100;
}
}
// 抽象角色
public interface OrderService {
void show();
void generate();
void delete();
int getRows();
}
// 处理器
public class TimeInvocationHandler implements InvocationHandler {
private Object target;
public TimeInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke");
Object obj = method.invoke(target, args);
return obj;
}
}
// 客户端
public class Client {
public static void main(String[] args) {
// 创建目标角色
OrderService target = new OrderServiceImpl();
// 创建代理角色
OrderService proxyObject =
(OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new TimeInvocationHandler(target));
// 调用代理对象的代理方法
proxyObject.show();
int rows = (int)proxyObject.getRows();
System.out.println(rows);
}
}
之前我们说如果有大量实际角色的话,通过静态代理的方式需要创建大量的代理角色,这造成了代码的冗余。动态代理模式的出现很好地解决了这个问题。那么我们带着这个问题分析这种模式是如何实现的。
从代码实现来看,是没有设计代理类的。那么是如何创建代理角色的呢,在client类中调用newProxyInstance方法,这是JDK内置的动态代理创建代理对象的方法,这个方法有三个参数,第一个参数是类加载器,第二个参数是Class<?>[] interfaces,第三个参数实现处理器接口的对象。首先我们先清楚,动态代理是在运行时动态获取代理角色,它不同于静态代理的死编码。接下来分析如何写这个方法的参数,这个方法的第一个参数是实际角色的类加载器,这个很好理解,因为我们要拿到代理角色,所以需要告诉内存谁需要被加载,加载的类其实就是真实角色。真实角色和代理角色实现共同的接口,在内存中生成代理类的时候,需要告诉代理类需要实现哪些接口,因而第二个参数就是需要让代理类实现的接口,参数是通过真实对象使用反射技术获取到的。通过这两个参数,我们其实已经可以创建一个代理类对象。
如果我们需要添加业务逻辑来增强代码,第三个参数的作用就显现了InvocationHandler 是 Java 反射包中的一个接口,用来在实现 JDK 动态代理时提供动态执行增强逻辑的方法。在使用 JDK 动态代理时,需要创建一个 InvocationHandler 的实现类,并实现其中的 invoke() 方法。invoke() 方法会在目标对象的方法被调用时自动被调用,从而可以在目标对象的方法执行前后添加一些自定义的逻辑。
使用 InvocationHandler 需要注意以下几点:
1. InvocationHandler 不能直接对目标对象进行操作,而是通过 Method 对象来调用目标对象的方法。
2. InvocationHandler 中的 invoke() 方法需要返回一个与目标对象方法的返回值类型相同的返回值。3. 在使用 JDK 动态代理时,必须为每一个需要代理的接口都创建一个对应的 InvocationHandler 实现类。
运行结果:
上述内容如果有错误的地方,希望大佬们可以指正。我一直在学习的路上,您的帮助使我收获更大!觉得对您有帮助的话,还请点赞支持!我也会不断更新文章!