代理模式是一种结构型设计模式,其目的是在不改变原始类(被代理类)的前提下,通过引入代理类来间接访问原始类,并且可以在代理类中添加额外的逻辑或控制访问原始类的方式。
同样的代理模式也分很多种
这里只抽几个常见的讲一下
静态代理
静态代理是代理模式的一种实现方式,它在编译时就已经确定好代理关系,代理类和被代理类的关系在程序运行前就已经确定。在静态代理中,代理类需要显式地声明代理对象,并在编译期间生成代理类的代码。
直接上代码
首先是主题
// 抽象主题
interface Subject {
void request();
}
然后是具体的实现类
// 具体主题
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
接着就是我们的代理类
// 代理
class Proxy implements Subject {
private RealSubject realSubject;
public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
System.out.println("Proxy: Pre-processing request.");
realSubject.request();
System.out.println("Proxy: Post-processing request.");
}
}
测试一下
public class Test {
public static void main(String[] args) {
// 创建真实主题对象
RealSubject realSubject = new RealSubject();
// 创建代理对象,并将真实主题对象传递给代理对象
Proxy proxy = new Proxy(realSubject);
// 通过代理对象调用请求方法
proxy.request();
}
}
运行结果
从这里可以看出代理模式的主要作用其实就是在执行目标方法前后添加其它逻辑,然后不改变原有业务逻辑,这个就是代码的封装性和可复用性,其它模式大多也是为了实现这么个目的
动态代理
动态代理是在运行时生成代理对象的代理模式。与静态代理不同,动态代理不需要显式地声明代理对象,而是在运行时通过反射等机制动态地创建代理对象。Java 中的动态代理通常使用 java.lang.reflect.Proxy 类来实现。
直接上代码,一样的首先是抽象主题
// 抽象主题
interface Subject {
void request();
}
然后具体实现类
// 具体主题
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
这里有些区别就是我们需要调用InvocationHandler处理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// 调用处理器
class DynamicProxyHandler implements InvocationHandler {
private Object realSubject;
public DynamicProxyHandler(Object realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Dynamic Proxy: Pre-processing request.");
Object result = method.invoke(realSubject, args);
System.out.println("Dynamic Proxy: Post-processing request.");
return result;
}
}
测试一下
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
// 创建真实主题对象
RealSubject realSubject = new RealSubject();
// 创建调用处理器
DynamicProxyHandler handler = new DynamicProxyHandler(realSubject);
// 创建代理对象
Subject proxy = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),
new Class<?>[] { Subject.class },
handler
);
// 通过代理对象调用请求方法
proxy.request();
}
}
运行结果
这里有一点要注意,乍一看这玩意似乎和我们常见的拦截器HandlerInterceptor差不多,但是HandlerInterceptor是基于接口实现的,和这个不一样
不过MyBatis 在执行 SQL 映射接口时就使用了动态代理。MyBatis 中的 Mapper 接口并没有具体的实现类,而是由 MyBatis 在运行时动态生成的代理对象来实现的。当应用程序调用 Mapper 接口的方法时,实际上是调用了动态代理对象的对应方法,而动态代理对象会将方法调用转发给 SqlSession,最终执行相应的 SQL 语句。
远程代理
远程代理是代理模式的一种变体,用于处理位于不同地址空间的对象,为客户端提供本地代表以访问远程对象。在远程代理中,代理对象充当客户端和远程对象之间的中介,隐藏了远程对象的实际位置和实现细节,使得客户端可以像访问本地对象一样访问远程对象。
还是先定义一个抽象主题
// 抽象主题
interface Image {
void display();
}
然后实现这个主题
// 具体主题
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
private void loadFromDisk() {
System.out.println("Loading " + filename);
}
}
然后创建一个远程代理
// 远程代理
class RemoteImageProxy implements Image {
private String filename;
private RealImage realImage;
public RemoteImageProxy(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
// 在需要时创建并连接到远程对象
realImage = new RealImage(filename);
}
realImage.display();
}
}
测试一下
public class Test {
public static void main(String[] args) {
// 创建远程代理对象
Image image = new RemoteImageProxy("test.jpg");
// 通过代理对象调用请求方法
image.display();
}
}
执行结果
这个远程代理目前还没有看出和静态代理有太大区别,但是据说有以下作用
缓存代理
缓存代理是代理模式的一种变体,用于为经常访问的数据提供一个临时存储,以提高访问速度。在缓存代理中,代理对象在执行方法前先检查缓存,如果缓存中存在数据,则直接返回缓存数据,否则调用原始对象的方法获取数据,并将结果缓存起来,以备下次访问使用。
我们定义一个数据服务
// 抽象主题
interface DataService {
String getData(String key);
}
然后实现它
// 具体主题
class RealDataService implements DataService {
@Override
public String getData(String key) {
// 模拟从数据库或其他数据源获取数据的操作
System.out.println("Fetching data for key: " + key);
return "Data for key: " + key;
}
}
再添加一个缓存代理
import java.util.HashMap;
import java.util.Map;
// 缓存代理
class CachedDataServiceProxy implements DataService {
private RealDataService realDataService;
private Map<String, String> cache;
public CachedDataServiceProxy() {
this.realDataService = new RealDataService();
this.cache = new HashMap<>();
}
@Override
public String getData(String key) {
if (cache.containsKey(key)) {
System.out.println("Retrieving cached data for key: " + key);
return cache.get(key);
} else {
String data = realDataService.getData(key);
cache.put(key, data);
return data;
}
}
}
测试一下
public class Test {
public static void main(String[] args) {
// 创建缓存代理对象
DataService dataService = new CachedDataServiceProxy();
// 第一次获取数据,会调用真实服务并缓存结果
System.out.println(dataService.getData("key1"));
// 第二次获取相同的数据,直接从缓存中获取,不会调用真实服务
System.out.println(dataService.getData("key1"));
}
}
运行结果
这个缓存代理目前看来实用性不大,因为现在都在用Redis做缓存服务,目前也有一些好用的缓存框架比如Spring Cache等