当前位置: 首页 > news >正文

代理模式 vs AOP:支付服务中的日志增强实践(含执行顺序详解)

目录

  • 一、实现支付服务
    • 1. 定义支付服务接口
    • 2. 实现支付服务接口
    • 3. 测试支付服务
    • 4. 引入代理模式
    • 5. 测试代理模式
    • 6. 总结
  • 二、execution和annotation区别
    • 1. execution 表达式
    • 2. @annotation 表达式
    • 3. 对比
  • 三、AOP实现(执行顺序)
  • 四、代理模式 vs AOP

一、实现支付服务

在本篇博客中,我们将展示如何使用Java中的接口和代理模式实现一个简单的支付服务系统。我们会先定义一个基础的支付服务接口,并为其提供一个实现类。接着,我们将通过代理模式为支付服务添加额外的行为,例如支付前后的日志输出。

1. 定义支付服务接口

首先,我们定义一个简单的PayService接口,所有的支付方式都需要实现该接口。接口中包含一个pay()方法,用于执行支付操作。

public interface PayService {void pay();  // 支付操作
}

2. 实现支付服务接口

接下来,我们创建一个支付服务的实现类PayServiceImpl,模拟微信支付的支付逻辑。我们将打印出一个模拟的支付交易号,并且暂时注释掉一些错误的代码(如除零错误),避免程序崩溃。

@Service
public class PayServiceImpl implements PayService {@Overridepublic void pay() {System.out.println("微信支付real   " + IdUtil.simpleUUID());// int age = 10 / 0;  // 模拟错误,如果需要,可以开启这一行来测试异常}
}
  • @Service注解表明PayServiceImpl是一个Spring的服务类,Spring会管理它的生命周期。
  • IdUtil.simpleUUID()用于生成一个唯一的UUID,模拟支付交易号。

3. 测试支付服务

在这里,我们简单测试一下PayServiceImpl的实现:

public class ClientTest {private static void payNormalV1() {PayService payService = new PayServiceImpl();payService.pay();  // 执行支付}public static void main(String[] args) {payNormalV1();  // 调用普通支付}
}

执行时,控制台会输出类似以下内容:
在这里插入图片描述

4. 引入代理模式

在实际开发中,我们经常使用代理模式来增强现有功能,比如增加日志记录、权限检查、事务管理等。在本例中,我们将使用代理模式包装PayService,并在支付前后增加一些自定义逻辑(例如打印日志)。

public class PayProxy implements PayService {private PayService payService;  // 目标对象// 构造注入public PayProxy(PayService payService) {this.payService = payService;}// 支付前的准备工作private void before() {System.out.println("-----before,打开微信支付");}@Overridepublic void pay() {before();  // 执行支付前的操作payService.pay();  // 委托目标对象执行实际的支付after();  // 执行支付后的操作}// 支付后的操作private void after() {System.out.println("-----after,关闭微信支付");}
}

PayProxy类中,我们通过构造器注入了一个PayService实例。pay()方法首先调用before()方法打印日志,然后调用目标对象(即PayServiceImpl)的pay()方法,最后执行after()方法,输出支付后的日志。

5. 测试代理模式

我们将创建一个新的测试方法来演示代理模式的使用:

public class ClientTest {private static void payNormalV1() {PayService payService = new PayServiceImpl();payService.pay();  // 执行普通支付}private static void payProxyV2() {PayService payService = new PayProxy(new PayServiceImpl());payService.pay();  // 执行代理支付}public static void main(String[] args) {payNormalV1();  // 调用普通支付System.out.println("=====================================");payProxyV2();  // 调用代理支付}
}

当执行payNormalV1()时,控制台会输出普通的支付信息;而执行payProxyV2()时,代理模式会在支付前后打印出更多的日志。

输出结果如下:
在这里插入图片描述

6. 总结

  • 我们首先定义了PayService接口和PayServiceImpl实现类,用于模拟支付操作。
  • 然后我们使用代理模式PayProxy来包装PayServiceImpl,为支付操作添加了额外的行为(支付前后的日志输出)。
  • 最后,我们通过测试类ClientTest展示了如何分别使用普通支付和代理支付。

这种代理模式可以在不修改原始支付逻辑的情况下,添加更多的功能,例如性能监控、日志记录、事务管理等。


二、execution和annotation区别

1. execution 表达式

  • 作用:根据方法的签名(访问修饰符、返回值、包名、类名、方法名、参数)来定义切点。

  • 特点:语法比较强大,可以非常精准地匹配方法。

  • 示例

    @Before("execution(* com.example.service.*.*(..))")
    public void beforeExecution() {System.out.println("通过 execution 拦截 service 包下的所有方法");
    }
    

2. @annotation 表达式

  • 作用:根据方法上是否存在某个注解来匹配切点。

  • 特点:更灵活,尤其适合只想增强带某个自定义注解的方法。

  • 示例

    // 定义一个自定义注解
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PayLog {
    }
    
    @Aspect
    @Component
    public class PayAspect {// 匹配所有带 @PayLog 注解的方法@Before("@annotation(com.example.annotation.PayLog)")public void beforeAnnotation() {System.out.println("通过 @annotation 拦截带 @PayLog 注解的方法");}
    }
    
    public class PayServiceImpl {@PayLogpublic void pay() {System.out.println("执行支付逻辑");}
    }
    

    当调用 pay() 方法时,就会触发切面逻辑。


3. 对比

特性execution@annotation
匹配方式方法签名(类、方法名、参数、返回值)方法上的注解
灵活性精准匹配方法,但规则写起来可能冗长给方法加注解即可,简洁直观
适用场景横切关注点范围明确,比如整个包、某类的所有方法只增强特定方法,比如需要日志、事务的方法
维护性如果方法签名变动,切点表达式容易失效注解写在方法上,不依赖方法名

总结

  • execution → 适合批量拦截,比如一个包下所有方法。
  • @annotation → 适合精确控制,尤其是只想拦截某些“打标记”的方法。

三、AOP实现(执行顺序)

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Aspect
@Component
public class PayAspect
{@Before("execution(public void com.donglin.interview2.aopreview.PayServiceImpl.pay(..))")public void beforeNotify(){System.out.println("-----@Before前置通知");}@After("execution(public void com.donglin.interview2.aopreview.PayServiceImpl.pay(..))")public void afterNotify(){System.out.println("-----@After后置通知");}@AfterReturning("execution(public void com.donglin.interview2.aopreview.PayServiceImpl.pay(..))")public void afterReturningNotify(){System.out.println("-----@AfterReturning返回通知");}@AfterThrowing("execution(public void com.donglin.interview2.aopreview.PayServiceImpl.pay(..))")public void afterThrowingNotify(){System.out.println("-----@AfterThrowing异常通知");}@Around("execution(public void com.donglin.interview2.aopreview.PayServiceImpl.pay(..))")public Object aroundNotify(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{Object retValue = null;System.out.println("-----@Around环绕通知AAA");retValue = proceedingJoinPoint.proceed();//放行System.out.println("-----@Around环绕通知BBB");return retValue;}
}
import cn.hutool.core.util.IdUtil;
import com.atguigu.interview2.aopreview.PayService;
import jakarta.annotation.Resource;
import org.springframework.boot.SpringBootVersion;
import org.springframework.core.SpringVersion;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class PayAopReviewController
{@Resourceprivate PayService payService;@GetMapping(value = "/pay/aop")public String pay(){System.out.println("SpringVersion: "+ SpringVersion.getVersion()+"\t"+ "SpringBootVersion: "+ SpringBootVersion.getVersion());payService.pay();return IdUtil.simpleUUID();}
}

1. 代码含义

PayAspect 里,你定义了五种通知类型:

  • @Before:方法执行前运行
  • @After:方法执行后(无论是否异常)都会运行
  • @AfterReturning:方法正常返回后运行
  • @AfterThrowing:方法抛出异常时运行
  • @Around:环绕通知,可以在方法前后都插入逻辑,还能决定是否调用目标方法

对应的切点是:

execution(public void com.donglin.interview2.aopreview.PayServiceImpl.pay(..))

也就是专门拦截 PayServiceImpl 里的 pay() 方法。


2. 实际执行顺序

在 Spring AOP 里,执行顺序大致是:

  1. @Around(进入时,前半段逻辑)

  2. @Before

  3. 目标方法执行

    • 如果成功 → 进入 @AfterReturning
    • 如果异常 → 进入 @AfterThrowing
  4. @After(无论成功还是失败都会执行)

  5. @Around(退出时,后半段逻辑)

在这里插入图片描述

如果 pay() 方法里抛异常,顺序就会变成:
在这里插入图片描述

四、代理模式 vs AOP

回忆之前写的 PayProxy 手动代理,其实就是:

  • before() → 对应 @Before
  • after() → 对应 @After
  • 中间执行目标方法 → 对应 payService.pay()
  • 如果要加异常处理逻辑,也可以对应 @AfterThrowing

区别在于:

  • 手写代理:一个类只能代理一个目标,扩展性差。
  • AOP:Spring 自动生成代理,切点表达式可以选择很多目标方法,扩展性更强。
对比点代理模式Spring AOP
实现方式手写代理类,或用 JDK/CGLIB 动态代理基于动态代理,Spring 自动生成代理
增强范围单个目标类/接口可批量拦截一类方法(execution/@annotation)
扩展性差,新增逻辑需改代理类强,只需新增切面类
维护成本高,类多时代理爆炸低,非侵入式
适用场景小型系统、教学案例企业级系统,日志/事务/安全/缓存等
http://www.dtcms.com/a/486148.html

相关文章:

  • linux系统运维教程,linux系统运维攻略
  • string字符集
  • Linux 命令:fsck
  • 如何提升生物科技研发辅助的效率?
  • ECEF坐标转换库
  • 企业商务网被公司优化掉是什么意思
  • 网站虚拟主机购买教程专业网站设计工作室
  • 数据库管理-第376期 Oracle AI DB 23.26新特性一览(20251016)
  • 【Nature高分思路速递】 物理驱动的机器学习
  • word文档转pdf开源免费,可自定义水印
  • k8s(五)PV和PVC详解
  • 深度学习与自然语言处理
  • python 部署可离线使用的中文识别OCR(window)
  • 湖州微信网站建设网站301了不知道在哪做的
  • 请描述网站开发的一般流程图php网站开发经理招聘
  • 关于pkg-config的使用示例--g++编译过程引入第三方库(如Opencv、Qt)
  • 外贸soho先做网站wordpress如何把背景颜色调为白色
  • zk02-知识演进
  • 全自动相机在线标定方法
  • 设计模式篇之 中介者模式 Mediator
  • 卫星几何处理
  • 【个人成长笔记】在Ubuntu中的Linux系统安装sogoupinyin搜狗输入法完整版(亲测有效)
  • 3d驱动模型。如何让人物说话?什么情况下需要训练wav2lip模型,自己训练的好处是什么?操作步骤是?
  • 画册什么网站做方便做外汇著名网站
  • CRMEB标准版PHP订单发货全解析
  • Flink 窗口 Join 与区间 Join 实战详解
  • 分布式监控体系:从指标采集到智能告警的完整之道
  • 《Muduo网络库:实现one loop per thread设计模式》
  • 怎么注册网站卖东西哪有培训网站开发
  • makefile概述