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

Spring XML AOP配置实战指南

🔍 总体概览

Spring 提供了两种方式定义 AOP 切面:

  1. @AspectJ 注解风格:使用 Java 类 + 注解(如 @Aspect, @Before, @After 等)
  2. Schema-based(基于 XML 的 aop 命名空间):通过 <aop:xxx> 标签在 XML 中配置切面逻辑

本文讲的是第二种——用 XML 写 AOP

虽然现在主流开发更倾向于注解+Java配置,但了解 XML 方式有助于:

  • 维护老项目
  • 深入理解 Spring AOP 底层机制
  • 在某些场景下更灵活控制(比如动态修改配置文件)

🧱 5.5 Schema-based AOP 支持

✅ 前提条件

要使用 <aop:config> 这些标签,必须先引入 Spring AOP 的命名空间(schema)

<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 https://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

然后才能使用 <aop:config><aop:aspect> 等标签。


🏗️ 5.5.1 声明一个 Aspect(切面)

在注解风格里,我们写一个类加上 @Aspect 就是一个切面;
而在 XML 风格中,普通 Java Bean + XML 配置 = 切面

示例代码

<aop:config><aop:aspect id="myAspect" ref="aBean">...</aop:aspect>
</aop:config><bean id="aBean" class="com.example.MyAspectBean"/>
  • ref="aBean" 表示这个切面的行为由 MyAspectBean 这个 bean 提供。
  • 所有通知方法(advice methods),比如 doAccessCheck(),都要在这个 bean 里实现。
  • 这个 bean 可以像其他任何 Spring Bean 一样被依赖注入!

📌 重点:没有 @Aspect 注解!切面是由 XML 定义的。


🔮 5.5.2 声明 Pointcut(切入点)

Pointcut 是“哪些方法需要被拦截”的规则表达式,语法与 AspectJ 相同。

① 全局定义(可在多个 aspect 中复用)

<aop:config><aop:pointcut id="businessService"expression="execution(* com.xyz.myapp.service.*.*(..))"/>
</aop:config>
  • id="businessService":给 pointcut 起名字,后面可以引用。
  • expression:标准的 AspectJ 切入点表达式,表示匹配 service 包下的所有公共方法。

② 在某个 aspect 内部定义

<aop:aspect id="myAspect" ref="aBean"><aop:pointcut id="serviceMethod"expression="execution(* com.xyz.service.*.*(..))"/>
</aop:aspect>

③ 使用已有的 @Aspect 类中的命名切入点

如果你已经有类似这样的类:

@Aspect
public class CommonPointcuts {@Pointcut("execution(* com.xyz.service.*.*(..))")public void businessService() {}
}

那么可以在 XML 中引用它:

<aop:pointcut id="myPC"expression="com.xyz.CommonPointcuts.businessService()"/>

⚠️ 注意限制

XML 定义的 pointcut 不能作为命名切入点用于组合新 pointcut(不像 @AspectJ 那样支持嵌套组合)。所以它的复用能力较弱。

💡 绑定 JoinPoint 上下文(如 this, target, args)

你可以从连接点提取参数传入 advice 方法:

<aop:pointcut id="serviceWithThis"expression="execution(* com.xyz.service.*.*(..)) and this(service)"/>
<aop:before method="monitor" pointcut-ref="serviceWithThis"/>

对应的方法:

public void monitor(Object service) { ... }

👉 this(service) 把代理对象绑定到变量 service,并自动传递给 advice 方法。

✅ XML 特殊字符处理

因为 && 在 XML 中会报错,可以用单词代替:

原符号XML 替代
&&and
`
!not

推荐写法:

expression="execution(* com.xyz.service.*.*(..)) and this(service)"

📢 5.5.3 声明 Advice(通知)

共有五种通知类型,与 @AspectJ 完全一致:

1. 前置通知(Before Advice)

<aop:before pointcut-ref="businessService" method="doAccessCheck"/>
  • 在目标方法执行前运行。
  • method="doAccessCheck" 指向 aspect bean 中的方法。

也可以内联 pointcut:

<aop:before pointcut="execution(* com.xyz.dao.*.*(..))"method="doAccessCheck"/>

2. 后置返回通知(After Returning)

正常返回后执行:

<aop:after-returning pointcut-ref="dataAccessOperation" returning="retVal" method="doLogResult"/>

对应方法需接收 retVal 参数:

public void doLogResult(Object retVal) { ... }

⚠️ 如果指定了 returning,只有当返回值类型匹配时才会触发。


3. 异常通知(After Throwing)

抛出异常后执行:

<aop:after-throwing pointcut-ref="dataAccessOperation" throwing="ex" method="doRecoveryActions"/>

方法签名:

public void doRecoveryActions(DataAccessException ex) { ... }

只对特定异常类型生效。


4. 最终通知(After Finally)

无论成功或失败都执行(类似 try-finally):

<aop:after pointcut-ref="dataAccessOperation" method="doReleaseLock"/>

常用于释放资源、解锁等操作。


5. 环绕通知(Around Advice)

最强大的通知类型,可控制是否继续执行原方法:

<aop:around pointcut-ref="businessService" method="doBasicProfiling"/>

实现方法:

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {System.out.println("开始计时");long start = System.currentTimeMillis();Object result = pjp.proceed(); // 执行原方法long elapsed = System.currentTimeMillis() - start;System.out.println("耗时:" + elapsed + "ms");return result;
}

✅ 第一个参数必须是 ProceedingJoinPoint


🧩 5.5.4 Introductions(引介 / 引入新接口)

类似于“让已有类额外实现某个接口”。

例如:让所有 Service 类都实现 UsageTracked 接口,记录调用次数。

XML 配置

<aop:aspect id="usageTracker" ref="usageTracking"><aop:declare-parentstypes-matching="com.xyz.myapp.service.*+"implement-interface="com.xyz.service.tracking.UsageTracked"default-impl="com.xyz.service.tracking.DefaultUsageTracked"/><aop:beforepointcut="execution(* com.xyz.myapp.service.*.*(..)) and this(usageTracked)"method="recordUsage"/>
</aop:aspect>
  • types-matching:哪些类要被增强?
  • implement-interface:新增实现哪个接口?
  • default-impl:该接口的默认实现类。

之后就可以把任意 service 强转为 UsageTracked

UsageTracked tracked = (UsageTracked) context.getBean("userService");
tracked.getUseCount();

🪄 5.5.5 实例化模型(Instantiation Models)

目前只支持 singleton(单例)模式

也就是说每个 <aop:aspect ref="..."> 对应的 bean 必须是单例的,不支持 per-this 或 per-target 等高级实例化模型。


📝 5.5.6 Advisors(顾问)

Advisor 是 Spring 特有的轻量级“切面”,通常只包含一个 advice 和一个 pointcut。

适合简单场景,比如事务管理。

示例:结合事务管理器使用

<aop:config><aop:pointcut id="businessService"expression="execution(* com.xyz.service.*.*(..))"/><aop:advisor pointcut-ref="businessService" advice-ref="txAdvice"/>
</aop:config><tx:advice id="txAdvice"><tx:attributes><tx:method name="*" propagation="REQUIRED"/></tx:attributes>
</tx:advice>
  • advice-ref 指向一个实现了 Spring Advice 接口的 bean(如 MethodInterceptor)。
  • 常见用途:事务、缓存、安全等通用横切逻辑。

🛠️ 5.5.7 完整示例:重试机制(并发锁失败自动重试)

场景需求

某些业务方法可能因数据库死锁失败(PessimisticLockingFailureException),但我们希望自动重试几次再抛异常。

这属于典型的横切关注点 → 适合用 AOP 实现。

Step 1:编写切面类(无注解)

public class ConcurrentOperationExecutor implements Ordered {private int maxRetries = 2;private int order = 1;public void setMaxRetries(int maxRetries) {this.maxRetries = maxRetries;}public int getOrder() {return order;}public void setOrder(int order) {this.order = order;}public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {int attempts = 0;PessimisticLockingFailureException lastException;do {attempts++;try {return pjp.proceed(); // 尝试执行} catch (PessimisticLockingFailureException e) {lastException = e;}} while (attempts <= maxRetries);throw lastException; // 重试完仍失败则抛出}
}

Step 2:XML 配置

<aop:config><aop:aspect id="concurrentRetry" ref="concurrentOperationExecutor"><aop:pointcut id="idempotentOp"expression="execution(* com.xyz.service.*.*(..))"/><aop:around pointcut-ref="idempotentOp"method="doConcurrentOperation"/></aop:aspect>
</aop:config><bean id="concurrentOperationExecutor"class="com.xyz.ConcurrentOperationExecutor"><property name="maxRetries" value="3"/><property name="order" value="100"/>
</bean>

✅ 效果

所有 service 方法如果抛出 PessimisticLockingFailureException,会最多重试 3 次。

🔐 更精细控制:仅对幂等方法重试

定义一个注解:

@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {}

标记幂等方法:

@Service
public class OrderService {@Idempotentpublic void cancelOrder(String id) { ... }
}

修改 pointcut:

<aop:pointcut id="idempotentOp"expression="execution(* com.xyz.service.*.*(..)) and @annotation(com.xyz.Idempotent)"/>

→ 只对加了 @Idempotent 的方法进行重试。


🧠 总结对比:Schema vs @AspectJ

功能Schema-based (XML)@AspectJ 注解
定义位置XML 文件Java 类
是否需要 @Aspect❌ 不需要✅ 需要
Pointcut 复用性较差(无法嵌套组合)强(可用方法调用)
类型安全性弱(字符串表达式)强(编译期检查)
参数绑定支持(按名称匹配)支持
调试难度高(分散在 XML 和 Java 中)低(集中在一个类)
适用场景老项目、事务配置、集中式策略新项目、复杂切面逻辑

✅ 学习建议

  • 新手入门:优先学习 @AspectJ 风格(更直观)。
  • 维护旧系统:必须掌握 schema-based 配置。
  • 最佳实践:避免混合使用 <aop:config>BeanNameAutoProxyCreator,否则可能导致代理失效。
  • 性能提示:大量使用 AOP 会影响启动速度和内存占用,合理设计切面粒度。

📚 结语

你看到的这部分文档非常全面,涵盖了 Spring AOP 的 XML 配置方式的所有关键特性。虽然现代开发中较少手写这些 XML,但它背后的原理(pointcut 表达式、advice 执行顺序、around 控制流程、introduction 扩展功能等)在各种框架(如 Spring Security、Spring Retry、Spring Cache)中都有广泛应用。

理解这一章,等于掌握了 Spring AOP 的底层运作机制

如果你想动手练习,我可以帮你生成完整的 Maven 项目结构 + 示例代码 👇

是否需要?

http://www.dtcms.com/a/532877.html

相关文章:

  • 什么人需要网站建设柳州网站开发公司
  • 做纯净系统的网站产品做国外网站有哪些
  • 商贸公司网站建设兴城泳装电子商务网站建设
  • 张祥前统一场论中的洛伦兹变换:多层次的应用与全新内涵
  • 网安面试题收集(4)
  • 高端上海网站设计公司价格wordpress 打赏
  • Yolo_lableimg_env
  • 【09】C语言中的格式输入函数scanf()详解
  • 鼠键共享工具
  • 个人网站备案 拍照装修网店
  • 投资,如何获得估值回归的收益?
  • 专业上海网站建设公司排名住房和城乡建设部网站杂志
  • 边界扫描测试原理 4 -- 保持状态
  • 国外服务器网站打开慢重庆公司起名
  • 个人怎样做旅游网站清新太和做网站
  • 电商系统设计:运费
  • Ceph分布式存储
  • 网站建设销售职责网站开发与运维收费明细
  • 破解空间网站十堰网站建设怎么做
  • 网站价值评估 php东莞住房和建设局网站
  • 11-14强制类型转换
  • redis中的数据类型
  • 2025年10月25日(星期六)骑行哈马者游记
  • 数据结构 —— 堆
  • 基于阿里云SDK的DDNS系统:架构设计与性能优化实战
  • 有哪些做调查问卷赚钱的网站自行创建网站的平台
  • 丰南建设网站wordpress网站前台打开慢
  • C4D域力场的修改层及遮罩详解
  • SAP SD交货单过账冲销接口分享
  • 如何在 Java 程序中检查是否可以连接到某个网站