【设计模式】-代理模式

在软件开发中,经常遇到需要对某个对象进行控制或者监控的场景。而直接修改对象的代码可能使代码变得复杂且难以维护。这时,使用代理模式(Proxy Pattern)可以很好地解决这个问题。

代理模式是一种结构型设计模式,通过引入一个代理对象来替代原始对象 ,实现对原有对象的控制或扩展。Java中的代理模式常用于实现日志记录权限控制事务控制等功能。


原理及实现思路

代理模式的核心思想是通过引入代理对象作为中间层,将客户端的请求转发给真正的对象,从而实现对真实对象的控制。

代理模式包含三个主要角色:

  • 抽象主题(Subject):定义了代理对象和真实对象的共同接口。

  • 真实主题(RealSubject):实现了抽象主题接口,是真正的业务逻辑处理对象。

  • 代理主题(ProxySubject):实现了抽象主题接口,内部持有一个真实主题对象的引用,通过代理对象间接调用真实对象。

实现代理模式的步骤如下:

  1. 创建抽象主题接口,定义需要代理的方法。

  2. 创建真实主题类,实现抽象主题接口,完成真正的业务逻辑。

  3. 创建代理主题类,实现抽象主题接口,持有一个真实主题对象的引用,在代理方法中调用真实主题的方法。

静态代理

静态代理是最简单的一种代理技术,由程序员手动编写代理类来代替真实对象。静态代理在编译期 生成代理类,在运行时代理类不会发生变化

静态代理的优点是简单易懂、易于实现,但缺点 也显而易见,++每个代理类只能代理一个具体类,当代理类的数量较多时,会导致代码冗余,并且每个代理类只能代理一个固定的类++。

示例代码如下:

java 复制代码
// 抽象主题接口
interface Subject {
    void doSomething();
}

// 真实主题类
class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject do something.");
    }
}

// 代理主题类
class ProxySubject implements Subject {
    private Subject realSubject;

    public ProxySubject(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void doSomething() {
        // 对真实主题方法的增强
        System.out.println("Before do something.");
        realSubject.doSomething();
        System.out.println("After do something.");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
        proxySubject.doSomething();
    }
}

动态代理

动态代理是在运行时动态地生成代理对象,相比于静态代理,动态代理更加灵活。Java中提供了两种动态代理的实现方式:基于接口的动态代理基于类的动态代理

基于接口的动态代理使用java.lang.reflect.Proxy类以及java.lang.reflect.InvocationHandler接口来实现。

这种方式要求被代理类实现一个接口,并通过代理类来间接调用真实对象的方法。

示例代码如下:

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 抽象主题接口
interface Subject {
    void doSomething();
}

// 真实主题类
class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject do something.");
    }
}

// InvocationHandler实现类
class MyInvocationHandler implements InvocationHandler {
    private Object realSubject;

    public MyInvocationHandler(Object realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 对真实主题方法的增强
        System.out.println("Before do something.");
        Object result = method.invoke(realSubject, args);
        System.out.println("After do something.");
        return result;
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        InvocationHandler handler = new MyInvocationHandler(realSubject);
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
                Client.class.getClassLoader(),
                new Class[]{Subject.class},
                handler);
        proxySubject.doSomething();
    }
}

基于类的动态代理使用cglib库,不要求被代理类实现接口,通过生成子类来实现代理。

示例代码如下:

java 复制代码
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

// 真实主题类
class RealSubject {
    public void doSomething() {
        System.out.println("RealSubject do something.");
    }
}

// MethodInterceptor实现类
class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 对真实主题方法的增强
        System.out.println("Before do something.");
        Object result = methodProxy.invokeSuper(object, args);
        System.out.println("After do something.");
        return result;
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(new MyMethodInterceptor());
        RealSubject proxySubject = (RealSubject) enhancer.create();
        proxySubject.doSomething();
    }
}

不同代理模式的优缺点及适用场景

优缺点

  • 静态代理的优点在于简单易懂、易于实现。缺点是每个代理类只能代理一个具体类,导致代码冗余,不够灵活。

  • 基于接口的动态代理的优点是可以代理实现了指定接口的任意对象,不需要修改原有代码。缺点是只能代理接口中定义的方法。

  • 基于类的动态代理的优点是可以代理任意类的对象,不需要修改原有代码。缺点是不能代理final修饰的类和方法。

适用场景:

  • 静态代理适用于只需要代理少数几个类,并且不需要频繁地修改代理类的情况。

  • 基于接口的动态代理适用于需要对接口中的方法进行控制和扩展的情况。

  • 基于类的动态代理适用于不需要修改原有代码、对类的任意方法进行控制和扩展的情况。

相关推荐
爱读源码的大都督几秒前
为什么有了HTTP,还需要gPRC?
java·后端·架构
Lucky_Turtle20 分钟前
【Java Xml】Apache Commons Digester3解析
xml·java·apache
聪明的笨猪猪39 分钟前
Java Redis “缓存设计”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
FIavor.39 分钟前
我发送给Apifox是http://localhost:9002/goods/getByUserName?name=张三 为什么会是500哪里错了?
java·服务器·网络协议·http
ID_1800790547340 分钟前
京东获取整站实时商品详情数据|商品标题|数据分析提取教程
java·开发语言
微露清风1 小时前
系统性学习C++-第五讲-内存管理
java·c++·学习
计算机毕业设计木哥1 小时前
计算机毕业设计选题推荐:基于SpringBoot和Vue的快递物流仓库管理系统【源码+文档+调试】
java·vue.js·spring boot·后端·课程设计
235161 小时前
【LeetCode】146. LRU 缓存
java·后端·算法·leetcode·链表·缓存·职场和发展
聪明的笨猪猪2 小时前
Java Redis “运维”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
FIavor.2 小时前
怎么办这是Apifox里执行http://localhost:9002/goods/getByUserName?name=“张三“为什么我改了还是500?
java·网络·网络协议·http