首页 > 编程语言 >Java设计模式之代理模式:静态代理VS动态代理,与其他模式的对比分析和案例解析

Java设计模式之代理模式:静态代理VS动态代理,与其他模式的对比分析和案例解析

时间:2024-08-22 19:52:02浏览次数:15  
标签:动态 对象 代理 模式 日志 设计模式 public

一、代理模式简介

代理模式(Proxy Pattern)是一种结构型设计模式,它提供了一个代理对象,用来控制对另一个对象的访问。这种模式通常用于在访问对象时引入额外的功能,而不改变对象的接口。代理模式的核心思想是为其他对象提供一种代理,以控制对这个对象的访问。

在现实生活中,代理模式的典型例子是房屋中介。购房者并不会直接联系房主,而是通过中介进行房屋的购买,这个中介相当于代理。

二、代理模式的结构

代理模式的典型结构如下:

  1. Subject:定义了 RealSubject 和 Proxy 的公共接口。
  2. RealSubject:定义了代理类所代表的真实对象。
  3. Proxy:保存一个指向 RealSubject 对象的引用,并且可以访问、控制、甚至可以改变它的行为。

在 Java 中,代理模式常见的应用场景包括远程代理、虚拟代理、保护代理和智能引用代理。
类图如下:
在这里插入图片描述

三、代理模式的使用场景
  1. 远程代理:为一个对象在不同地址空间提供局部代表。
  2. 虚拟代理:根据需要创建开销较大的对象。
  3. 保护代理:控制对原始对象的访问。代理对象可以控制对原始对象操作的权限。
  4. 智能引用代理:在访问对象时增加一些额外的操作,比如缓存访问结果、统计访问次数等。
四、动态代理与静态代理的区别

代理模式可以分为静态代理和动态代理两种形式。

1. 静态代理

静态代理是在编译时确定代理类,通过在代理类中预先定义代理对象和目标对象之间的关系。实现相对简单,但扩展性和灵活性有一定局限。

1.1 静态代理的实现

假设在电商交易系统中,我们有一个订单处理服务接口OrderService,实现类负责订单的具体处理。我们希望在订单处理前后记录日志,可以使用静态代理来实现。

1.1.1 代码示例
// 定义订单处理服务接口
public interface OrderService {
    void processOrder(String orderId);
}

// 订单处理服务实现类
public class OrderServiceImpl implements OrderService {
    @Override
    public void processOrder(String orderId) {
        System.out.println("Processing order: " + orderId);
    }
}

// 静态代理类
public class OrderServiceProxy implements OrderService {
    private OrderServiceImpl orderService;

    public OrderServiceProxy(OrderServiceImpl orderService) {
        this.orderService = orderService;
    }

    @Override
    public void processOrder(String orderId) {
        // 在处理订单前记录日志
        System.out.println("Logging: Before processing order " + orderId);
        // 调用实际的订单处理方法
        orderService.processOrder(orderId);
        // 在处理订单后记录日志
        System.out.println("Logging: After processing order " + orderId);
    }
}

// 客户端调用示例
public class Main {
    public static void main(String[] args) {
        OrderServiceImpl orderService = new OrderServiceImpl();
        OrderServiceProxy proxy = new OrderServiceProxy(orderService);
        proxy.processOrder("12345");
    }
}
1.1.2 运行结果分析

执行上述代码,输出结果如下:

Logging: Before processing order 12345
Processing order: 12345
Logging: After processing order 12345

通过静态代理,我们成功在订单处理前后插入了日志记录的逻辑。

1.2 静态代理的优缺点
  • 优点
    • 实现简单,代理类和目标对象之间的关系清晰。
    • 易于理解和调试。
  • 缺点
    • 如果接口有多个实现类,每个实现类都需要创建一个对应的代理类,代码冗余。
    • 如果接口增加了新方法,代理类需要同步修改,增加了维护成本。
    • 缺乏灵活性,代理逻辑固定在代理类中,不易扩展。
2. 动态代理

动态代理是在运行时创建代理类,能够动态处理目标对象的方法调用。Java中的动态代理主要依赖java.lang.reflect.Proxy类(JDK动态代理)和CGLIB库(CGLIB动态代理)实现。相比静态代理,动态代理更加灵活。

2.1 动态代理的实现

继续以电商交易系统为例,我们希望在订单处理前后记录日志,但这次使用动态代理来实现。

2.1.1 JDK动态代理

JDK动态代理只能代理实现了接口的类。它通过Proxy.newProxyInstance方法在运行时生成代理对象。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 动态代理处理器
public class LoggingInvocationHandler implements InvocationHandler {
    private Object target;

    public LoggingInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在方法调用前记录日志
        System.out.println("Logging: Before method " + method.getName());
        // 调用实际的目标对象方法
        Object result = method.invoke(target, args);
        // 在方法调用后记录日志
        System.out.println("Logging: After method " + method.getName());
        return result;
    }
}

// 客户端调用示例
public class Main {
    public static void main(String[] args) {
        // 创建目标对象
        OrderServiceImpl orderService = new OrderServiceImpl();
        // 创建动态代理
        OrderService proxy = (OrderService) Proxy.newProxyInstance(
                orderService.getClass().getClassLoader(),
                orderService.getClass().getInterfaces(),
                new LoggingInvocationHandler(orderService)
        );
        // 调用代理对象的方法
        proxy.processOrder("12345");
    }
}
2.1.2 CGLIB动态代理

CGLIB动态代理通过生成目标类的子类来创建代理对象,因此即使目标类没有实现接口,也可以使用CGLIB进行代理。

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// CGLIB动态代理处理器
public class LoggingInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 在方法调用前记录日志
        System.out.println("Logging: Before method " + method.getName());
        // 调用实际的目标对象方法
        Object result = proxy.invokeSuper(obj, args);
        // 在方法调用后记录日志
        System.out.println("Logging: After method " + method.getName());
        return result;
    }
}

// 客户端调用示例
public class Main {
    public static void main(String[] args) {
        // 创建目标对象
        OrderServiceImpl orderService = new OrderServiceImpl();
        // 创建CGLIB动态代理
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderServiceImpl.class);
        enhancer.setCallback(new LoggingInterceptor());
        OrderServiceImpl proxy = (OrderServiceImpl) enhancer.create();
        // 调用代理对象的方法
        proxy.processOrder("12345");
    }
}
2.1.3 运行结果分析

无论是JDK动态代理还是CGLIB动态代理,执行上述代码后的输出结果类似:

Logging: Before method processOrder
Processing order: 12345
Logging: After method processOrder

动态代理在运行时生成代理对象,并在方法执行前后插入日志逻辑。与静态代理不同的是,动态代理更具灵活性,特别是当接口或目标类有多个方法或复杂逻辑时。

2.2 动态代理的优缺点
  • 优点
    • 灵活性高,能够动态处理目标对象的方法调用。
    • 代码复用性强,一个代理处理器可以代理多个对象。
    • 如果接口增加新方法,代理类无需修改,减少了维护成本。
  • 缺点
    • 调试困难,尤其是异常堆栈信息较复杂时。
    • 性能相对较低,因为动态代理在运行时进行反射调用。
JDK动态代理与CGLIB动态代理的比较
  • JDK动态代理
    • 仅适用于实现了接口的类。
    • 使用java.lang.reflect.Proxy类,性能较CGLIB略高,但在处理大量方法时会受到一定限制。
  • CGLIB动态代理
    • 可以代理没有实现接口的类。
    • 使用底层字节码生成库,性能较好,但在使用时会增加项目的依赖和复杂性。
3. 动态代理与静态代理的使用场景
  • 静态代理:适用于接口较少、代理逻辑简单的场景,例如单个服务的权限控制或简单的日志记录。
  • 动态代理:适用于需要代理多个接口或方法,且代理逻辑复杂的场景,例如系统级别的事务管理、复杂的AOP切面处理。
五、代理模式与其他模式的区别

1. 代理模式 vs 装饰器模式

  • 目的:代理模式的主要目的是控制对对象的访问,而装饰器模式则是动态地增加或扩展对象的功能。
  • 设计意图:代理模式关注的是如何控制对象的访问,装饰器模式则关注对象功能的扩展。
  • 结构区别:代理模式通常只有一个代理对象,而装饰器模式可以有多个装饰器层次。

示例

 // 代理模式中的订单处理
class LoggingOrderProxy implements OrderService {
    private RealOrderService realOrderService;

    public LoggingOrderProxy(RealOrderService realOrderService) {
        this.realOrderService = realOrderService;
    }

    public void processOrder(String orderId) {
        logOrder(orderId);
        realOrderService.processOrder(orderId);
    }

    private void logOrder(String orderId) {
        System.out.println("Logging order: " + orderId);
    }
}

// 装饰器模式中的订单处理
class DiscountOrderDecorator extends RealOrderService {
    private RealOrderService realOrderService;

    public DiscountOrderDecorator(RealOrderService realOrderService) {
        this.realOrderService = realOrderService;
    }

    public void processOrder(String orderId) {
        applyDiscount(orderId);
        realOrderService.processOrder(orderId);
    }

    private void applyDiscount(String orderId) {
        System.out.println("Applying discount to order: " + orderId);
    }
}

在这个示例中,代理模式和装饰器模式都扩展了订单处理功能,但它们的设计意图和应用方式有所不同。

2. 代理模式 vs 中介者模式

  • 职责不同:代理模式的职责是控制对单个对象的访问,中介者模式的职责是协调多个对象之间的交互。
  • 复杂度:中介者模式通常更复杂,因为它要处理多个对象之间的关系,而代理模式只需关注一个对象。

3. 代理模式 vs 外观模式

  • 抽象层次不同:外观模式提供一个简化的接口来访问子系统,而代理模式则提供对单个对象的访问控制。
  • 功能范围:外观模式通常用于简化系统接口,代理模式则用于对象级别的控制。
六、代理模式的优缺点

优点

  • 控制复杂度:通过代理对象来间接访问真实对象,能够减少直接访问复杂对象的复杂度。
  • 扩展功能:可以在不修改真实对象代码的情况下,通过代理对象增加功能。

缺点

  • 性能开销:代理模式会增加一些内存消耗和处理时间,因为它需要保存一份代理对象。
  • 复杂度增加:使用代理模式会使代码变得更加复杂,尤其是嵌套代理时。
七、代理模式在Spring AOP中的应用

在Java的开源框架中,代理模式(Proxy Pattern)被广泛应用,其中最为典型的就是Spring AOP(面向切面编程)。Spring AOP通过代理对象拦截方法调用,注入横切关注点(如事务管理、日志记录等),从而实现业务逻辑与非功能性需求的解耦。

1.1 动态代理的实现

Spring AOP在实现代理时,主要依赖两种方式:JDK动态代理和CGLIB代理。

  • JDK动态代理:当目标对象实现了接口时,Spring AOP默认使用JDK动态代理。
  • CGLIB代理:当目标对象没有实现接口时,Spring AOP会使用CGLIB代理,它通过生成目标类的子类来创建代理对象。

以下我们通过一个实际的Spring AOP示例,来详细讲解代理模式在Spring中的应用。

1.2 代码示例分析
1.2.1 目标对象接口和实现类

首先,我们定义一个简单的服务接口及其实现类。

public interface UserService {
    void createUser(String username);
}

public class UserServiceImpl implements UserService {
    @Override
    public void createUser(String username) {
        System.out.println("User " + username + " has been created.");
    }
}
1.2.2 切面类

接下来,我们定义一个切面类,用于在方法执行前后添加日志。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;

@Aspect
public class LoggingAspect {

    @Before("execution(* com.example.UserService.createUser(..))")
    public void logBefore() {
        System.out.println("Before creating user");
    }

    @After("execution(* com.example.UserService.createUser(..))")
    public void logAfter() {
        System.out.println("After creating user");
    }
}

在这个切面类中,我们使用了@Before@After注解来定义在createUser方法执行前后的日志操作。

1.2.3 Spring配置

为了使Spring能够识别并应用切面类,我们需要进行相应的配置。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="com.example.UserServiceImpl"/>

    <bean id="loggingAspect" class="com.example.LoggingAspect"/>

    <aop:config>
        <aop:aspect ref="loggingAspect">
            <aop:pointcut id="createUserPointcut" expression="execution(* com.example.UserService.createUser(..))"/>
            <aop:before method="logBefore" pointcut-ref="createUserPointcut"/>
            <aop:after method="logAfter" pointcut-ref="createUserPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

在这个XML配置文件中,我们使用了标签来配置AOP,指定在哪些方法调用前后执行切面逻辑。

1.2.4 运行结果分析

当我们通过Spring容器获取UserService的代理对象并调用createUser方法时,AOP代理会拦截方法调用,并执行我们在切面类中定义的日志操作。

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");

userService.createUser("JohnDoe");

执行上面的代码,输出结果如下:

Before creating user
User JohnDoe has been created.
After creating user

从输出结果可以看出,createUser方法的执行被代理对象拦截,且在方法执行前后插入了日志操作。通过代理模式,Spring AOP实现了业务逻辑与日志记录的解耦,使得代码更加模块化和可维护。

1.3 代理模式的优势

在Spring AOP中使用代理模式,开发者可以非常方便地将横切关注点(如事务、日志、安全性)独立出来,避免这些非功能性需求侵入核心业务逻辑。代理模式使得代码更加清晰,并且能够在不修改业务代码的情况下,灵活地添加或移除横切关注点。

在这个示例中,LoggingAspect 使用了 AOP 代理来在订单处理之前记录日志。这种方式极大地简化了业务逻辑的实现,同时保持了代码的整洁性和可维护性。

八、总结

代理模式在 Java 开发中是一个非常实用的模式,尤其是在权限控制、日志记录、性能优化等方面。通过代理模式,开发者可以在不修改原始代码的情况下,动态地增加功能,同时保持系统的稳定性和扩展性。

在设计模式中,代理模式、装饰器模式、中介者模式和外观模式有着各自的应用场景和设计意图。理解它们之间的区别,并在实际项目中合理应用,是提高系统设计质量的重要一步。

标签:动态,对象,代理,模式,日志,设计模式,public
From: https://blog.csdn.net/weixin_39996520/article/details/141402579

相关文章

  • 设计模式之责任链模式
    责任链模式是面向对象的23种设计模式中的一种,属于行为模式范围。责任链模式(ChainofResponsibility),见名知意:就是每一个处理请求的处理器组合成一个链表,链表中的每个节点(执行器)都有机会处理发送的请求。大致的结构是这个样子: 举一个简单的例子:某公司有一名新员工要入职,则入职......
  • Flannel Wireguard 模式
    FlannelWireGuard模式一、环境信息主机IPubuntu172.16.94.141软件版本docker26.1.4helmv3.15.0-rc.2kind0.18.0clab0.54.2kubernetes1.23.4ubuntuosUbuntu20.04.6LTSkernel5.11.5内核升级文档二、安装服务kind配置......
  • 大模型 Agent 任务 ,如何开发一个 AI 代理?
    代理AgentAgent最初出现在强化学习任务中,智能体拥有状态空间和动作空间,每执行一个action都需要通过状态空间和激励来决定下一个action。而大模型代理使用类似的思想,利用大模型的自然语言分析能力根据当前对话梳理达到目标需要执行的策略,然后一边执行操作一边根据反馈分......
  • Flannel IPsec 模式
    FlannelIPSec模式一、环境信息主机IPubuntu172.16.94.141软件版本docker26.1.4helmv3.15.0-rc.2kind0.18.0clab0.54.2kubernetes1.23.4ubuntuosUbuntu20.04.6LTSkernel5.11.5内核升级文档二、安装服务kind配置文件......
  • 分布式事务的Seata AT模式原理
    Seata官网地址:https://seata.apache.org/zh-cn/AT模式优点:无侵入式代码,只需要添加注解,底层采用Seata代理的数据源DataSourceProxy缺点:依赖于数据库,目前只适用于postgresql、oracle、mysql、polardb-x、sqlserver、达梦数据库等数据库,比如业务逻辑中含有redis、es等操作需要控......
  • 设计模式简介及PHP的35种设计模式(上)
    什么是模式??        有经验的00开发者(以及其他的软件开发者)建立了既有通用原则又有惯用方案的指令系统来指导他们编制软件。如果以结构化形式对这些问题、解决方案和命名进行描述使其系统化,那么这些原则和习惯用法就可以称为模式。例如,下面是一个模式样例:    ......
  • 加速网络体验,Squid缓存代理:让浏览如飞,畅享无限网络速度!
     作者简介:我是团团儿,是一名专注于云计算领域的专业创作者,感谢大家的关注 座右铭:   云端筑梦,数据为翼,探索无限可能,引领云计算新纪元 个人主页:团儿.-CSDN博客目录前言:squid代理的基本类型squid是如何工作的?实验目标:配置squid缓存代理,实现web访问速度的提高Squid的......
  • 深度学习设计模式之策略模式
    文章目录前言一、介绍二、特点三、详细介绍1.核心组成2.代码示例3.优缺点优点缺点4.使用场景总结前言策略模式定义一系列算法,封装每个算法,并使它们可以互换。一、介绍策略模式(StrategyPattern)是一种行为型设计模式,它定义了一系列算法,并将每一个算法封装起来,使......
  • Python——常用行为模式
    行为模式(BehavioralDesignPatterns)主要解决的是对象之间的职责划分与协作问题。这类模式通过定义对象间的通信方式、责任分配和行为组织,帮助构建可扩展、灵活的系统,并且通过减少耦合和提高复用性来优化系统行为。常见的行为模式有:责任链模式(ChainofResponsibility)命......
  • 深入理解命令模式:设计模式中的行为型模式解析
    深入理解命令模式:设计模式中的行为型模式解析1.引言设计模式是软件开发中一种经过实践验证的、解决常见问题的方案。行为型设计模式关注对象间的职责分配和通信方式。命令模式(CommandPattern)作为一种重要的行为型模式,主要用于将请求封装成对象,从而将请求的发起者和处理......