定义:
代理模式提供了一个对象的代理,以控制对这个对象的访问。代理对象作为客户端和目标对象之间的中介,可以在不修改目标对象的前提下,增加额外的功能,如访问控制、延迟加载、日志记录、缓存等。
代理模式分类:
代理模式通常分为 静态代理 和 动态代理 两种。静态代理需要为每个目标类创建一个代理类,而动态代理则可以 在运行时 动态地创建代理类,无需为每个目标类编写单独的代理类代码。
静态代理(模拟记录日志):
在加载图片之前和之后添加了一些额外的逻辑,比如记录日志。
// ImageService 接口
interface ImageService {
void loadImage(String imagePath);
}
// RealImageService 类,实现 ImageService 接口
class RealImageService implements ImageService {
@Override
public void loadImage(String imagePath) {
// 模拟加载图片的过程
System.out.println("加载图片:" + imagePath);
}
}
// ImageServiceProxy 类,作为静态代理
class ImageServiceProxy implements ImageService {
private RealImageService realService;
public ImageServiceProxy(RealImageService realService) {
this.realService = realService;
}
@Override
public void loadImage(String imagePath) {
// 在加载图片之前记录日志
System.out.println("加载图片之前日志信息。。。");
realService.loadImage(imagePath);
// 在加载图片之后记录日志
System.out.println("加载图片之后日志信息。。。");
}
}
// 客户端代码
public class StaticProxyDemo {
public static void main(String[] args) {
RealImageService realService = new RealImageService();
ImageService proxy = new ImageServiceProxy(realService);
proxy.loadImage("path/to/image.jpg");
}
}
动态代理(模拟更新缓存):
在调用 getData 方法时会首先检查缓存,如果缓存中有数据则直接返回,否则调用 RealDataService 来获取数据并更新缓存。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
// DataService 接口
interface DataService {
String getData(String key);
}
// RealDataService 类,实现 DataService 接口
class RealDataService implements DataService {
@Override
public String getData(String key) {
// 模拟从数据库或其他来源获取数据
System.out.println("获取数据:" + key);
return "缓存数据为:" + key;
}
}
// DataServiceInvocationHandler 类,作为动态代理的处理器
class DataServiceInvocationHandler implements InvocationHandler {
private Object target;
private Map<String, String> cache = new HashMap<>();
public DataServiceInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String key = (String) args[0];
//判断是否为 getData 方法
if ("getData".equals(method.getName())) {
// 检查缓存
if (cache.containsKey(key)) {
System.out.println("存在缓存: " + key);
return cache.get(key);
}
// 缓存中没有数据,调用目标对象的方法获取数据
String data = (String) method.invoke(target, args);
// 更新缓存
cache.put(key, data);
System.out.println("更新缓存" + data);
return data;
}
// 如果不是 getData 方法,则直接调用目标对象的方法
return method.invoke(target, args);
}
}
// 客户端代码
public class DynamicProxyDemo {
public static void main(String[] args) {
RealDataService realService = new RealDataService();
DataService proxyInstance = (DataService) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new DataServiceInvocationHandler(realService)
);
// 第一次调用,数据不在缓存中,应该调用 RealDataService
System.out.println(proxyInstance.getData("key1"));
// 第二次调用,数据已经在缓存中,应该直接返回缓存的数据
System.out.println(proxyInstance.getData("key1"));
}
}
动态代理工作原理:
- 动态代理通常使用反射机制或第三方库(如JDK动态代理、CGLIB动态代理等)来实现。
- 在运行时,动态代理根据需求生成相应的代理类,并创建代理对象。
- 当调用代理对象的方法时,代理对象会拦截调用,并执行自定义逻辑(如日志记录、权限检查等),然后将调用转发给目标对象。
动态代理优缺点:
优点:
- 灵活性高:可以根据需要动态生成不同的代理对象和代理逻辑。
- 可扩展性强:通过修改配置或代码,可以轻松添加新的功能或支持新的被代理类。
- 降低耦合度:代理对象与目标对象之间通过接口进行交互,降低了系统的耦合度。
- 提高安全性:通过代理对象可以对目标对象的方法进行权限检查,提高系统的安全性。
缺点:
- 性能开销:由于动态代理涉及反射机制,因此在性能上可能会比静态代理略逊一筹。
- 目标对象限制:在某些实现中(如JDK动态代理),目标对象必须实现一个或多个接口,否则无法使用动态代理。
JDK动态代理:
- JDK动态代理要求被代理的类必须 实现一个或多个接口。
- 在运行时,JDK动态代理会为被代理类生成一个实现了相同接口的代理类实例。
- 通过拦截器(InvocationHandler)来定义代理方法的具体实现,当调用代理对象的方法时,会转发到拦截器的invoke方法进行处理。
CGLIB动态代理:
- CGLIB利用ASM(一个Java字节码操控和分析框架)来转换字节码并生成新的类。
- 新生成的类会 继承 被代理类,并重写被代理类的方法,以实现代理逻辑。


被折叠的 条评论
为什么被折叠?



