常见设计模式-代理模式

定义:

代理模式提供了一个对象的代理,以控制对这个对象的访问。代理对象作为客户端和目标对象之间的中介,可以在不修改目标对象的前提下,增加额外的功能,如访问控制、延迟加载、日志记录、缓存等。

代理模式分类:

代理模式通常分为 静态代理动态代理 两种。静态代理需要为每个目标类创建一个代理类,而动态代理则可以 在运行时 动态地创建代理类,无需为每个目标类编写单独的代理类代码。

静态代理(模拟记录日志):

在加载图片之前和之后添加了一些额外的逻辑,比如记录日志。

// 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字节码操控和分析框架)来转换字节码并生成新的类。
  • 新生成的类会 继承 被代理类,并重写被代理类的方法,以实现代理逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值