🍊
翻译橙
🍊返回主站🤖参与贡献
  • hello,这里是翻译橙
  • spring boot参考文档
    • 1. 法律
    • 2. 寻求帮助
    • 3. 文档概述
    • 4. 入门
    • 5. 升级Spring Boot
    • 6. 使用 Spring Boot 进行开发
      • 6.1. 构建系统
      • 6.2. 构建你的代码
      • 6.3. 配置类
      • 6.4. 自动配置
      • 6.5. Spring Bean 和依赖注入
      • 6.6. 使用@SpringBootApplication注解
      • 6.7. 运行您的应用程序
      • 6.8. 开发者工具
      • 6.9. 打包您的生产应用程序
      • 6.10. 接下来读什么
    • 7.核心特性
      • 7.1. SpringApplication
      • 7.2. 外部化配置
      • 7.3.Profile配置
      • 7.4.日志记录
      • 7.5.国际化
      • 7.6 面向切面的编程
      • 7.7. JSON
      • 7.8. 任务执行与调度
      • 7.9. 单元测试
        • 7.9.1. 测试范围依赖
        • 7.9.2. 测试 Spring 应用程序
        • 7.9.3. 测试 Spring Boot 应用程序
        • 7.9.4. 测试容器
        • 7.9.5. 测试工具
      • 7.10. Docker Compose 支持
      • 7.11. 测试容器支持
      • 7.12. 创建您自己的自动配置
      • 7.13. Kotlin 支持
      • 7.14 SSL
      • 7.15.接下来要读什么
    • 8. 网络
      • 8.1. Servlet Web 应用程序
        • 8.1.1. “Spring Web MVC 框架”
        • 8.1.2. JAX-RS 和Jersey
        • 8.1.3. 嵌入式 Servlet 容器支持
      • 8.2 反应式网络应用程序
        • 8.2.1. “Spring WebFlux 框架”
        • 8.2.2. 嵌入式反应式服务器支持
        • 8.2.3. 反应式服务器资源配置
      • 8.3. 优雅关机
      • 8.4. spring安全
        • 8.4.1. MVC安全
        • 8.4.2. WebFlux 安全
        • 8.4.3. OAuth2
        • 8.4.4. SAML 2.0
      • 8.5. spring 会话
      • 8.6.GraphQL
      • 8.7. Spring HATEOAS
      • 8.8.接下来读什么
    • 9. 数据
      • 9.1. SQL数据库
      • 9.2. 使用 NoSQL 技术
      • 9.3. 接下来读什么
    • 10. 消息
      • 10.1. JMS
      • 10.2. AMQP
      • 10.3. Apache Kafka 支持
      • 10.4. Apache Pulsar 支持
      • 10.5. RSocket
      • 10.6. Spring Integration
      • 10.7. WebSockets
      • 10.8. What to Read Next
    • 11. IO
      • 11.1. 缓存
      • 11.2. Hazelcast
      • 11.3. Quartz 调度程序
      • 11.4. 发送电子邮件
      • 11.5. 验证
      • 11.6. 调用 REST 服务
      • 11.7. web services
      • 11.8. 使用 JTA 进行分布式事务
      • 11.9. 接下来读什么
    • 12. 容器镜像
  • Spring核心功能
    • 1.IOC容器和Bean简介
      • 1.2. 容器概述
      • 1.3. Bean概述
      • 1.4. 依赖项
        • 1.4.1. 依赖注入
        • 1.4.2. 详细的依赖关系和配置
        • 1.4.3. 使用depends-on
        • 1.4.4. 延迟初始化的 Bean
        • 1.4.5. 自动装配协作者
        • 1.4.6. 方法注入
    • 2. Resources
      • 2.1. 介绍
      • 2.2. Resource接口
      • 2.3. 内置Resource实现
      • 2.4. ResourceLoader接口
      • 2.5. ResourcePatternResolver接口
      • 2.6. ResourceLoaderAware接口
      • 2.7. 资源作为依赖
      • 2.8. 应用程序上下文和资源路径
    • 3. 验证、数据绑定和类型转换
      • 3.1. 使用 Spring 的 Validator 接口进行验证
      • 3.2. 将代码解析为错误消息
      • 3.3. Bean 操作和BeanWrapper
      • 3.4. spring类型转换
      • 3.5. spring字段格式
      • 3.6. 配置全局日期和时间格式
      • 3.7. Java Bean 验证
    • 4. SpEL表达式
    • 5. Spring 面向切面编程
      • 5.1. AOP 概念
      • 5.2. Spring AOP 的能力和目标
      • 5.3. AOP 代理
      • 5.4. @AspectJ 支持
        • 5.4.1. 启用@AspectJ 支持
        • 5.4.2. 声明一个切面
        • 5.4.3. 声明切入点
        • 5.4.4. 声明切点
        • 5.4.5. 切面说明
        • 5.4.6. 切面实例化模型
        • 5.4.7. AOP 示例
      • 5.5. 基于模式的 AOP 支持
      • 5.6. 选择要使用的 AOP 声明样式
      • 5.7. 混合切面类型
      • 5.8. 代理机制
      • 5.9. @AspectJ 代理的程序化创建
      • 5.10. 在 Spring 应用程序中使用 AspectJ
      • 5.11.更多资源
    • 6. Spring AOP API
      • 6.1. Spring中的切入点API
      • 6.2. Spring 中的 Advice API
      • 6.3. Spring 中的 Advisor API
      • 6.4. 使用ProxyFactoryBean创建 AOP 代理
      • 6.5. 简洁的代理定义
      • 6.6. 以编程方式创建 AOP 代理ProxyFactory
      • 6.7. 操作切面对象
      • 6.8. 使用“自动代理”工具
      • 6.9. 使用TargetSource实现
      • 6.10. 定义新的切面类型
    • 7. 空指针安全
    • 8. 数据缓冲器和编解码器
    • 9. 日志
    • 10. 附录
      • 10.1. XML 模式
      • 10.2. 自定义XML Schema
        • 10.2.1. 创作 Schema
        • 10.2.2. 编码一个NamespaceHandler
        • 10.2.3. 使用BeanDefinitionParser
        • 10.2.4. 注册处理程序和模式
        • 10.2.5. 在 Spring XML 配置中使用自定义扩展
        • 10.2.6. 更详细的例子
      • 10.3. 应用程序启动步骤
  • 使用redis实现分布式锁
  • Java 安全标准算法名称
  • JDK 9 JEP
  • JDK 10 JEP
  • 人件
    • 《人件》
    • 第一部分 管理人力资源
      • 01 此时此刻,一个项目正在走向失败
      • 02 干酪汉堡,做一个,卖一个
      • 03 维也纳在等你
      • 04 质量——如果时间允许
      • 05 再谈帕金森定律
      • 06 苦杏素
    • 第二部分 办公环境
      • 07 家具警察
      • 08 “朝九晚五在这里啥也完成不了。”
      • 09 在空间上省钱
      • 间奏曲:生产效率度量和不明飞行物
      • 10 大脑时问与身体时间
      • 11 电话
      • 12 门的回归
      • 13 采取保护步骤
    • 第三部分 正确的人
      • 14 霍恩布洛尔因素
      • 15 谈谈领导力
      • 16 雇一名杂耍演员
      • 17 与他人良好合作
      • 18 童年的终结
      • 19 在这儿很开心
      • 20 人力资本
    • 第四部分 高效团队养成
      • 21 整体大于部分之和
      • 22 黑衣团队
      • 23 团队自毁
      • 24 再谈团队自毁
      • 25 竞争
      • 26 一顿意面晚餐
      • 27 敞开和服
      • 28 团队形成的化学反应
    • 第五部分 沃土
      • 29 自我愈复系统
      • 30 与风险共舞
      • 3l 会议、独白和交流
      • 32 终极管理罪恶得主是……
      • 33 “邪恶”电邮
      • 34 让改变成为可能
      • 35 组织型学习
      • 36 构建社区
    • 第六部分 快乐地工作
      • 37 混乱与秩序
      • 38 自由电子
      • 39 霍尔加·丹斯克
由 GitBook 提供支持
在本页

这有帮助吗?

在GitHub上编辑
  1. Spring核心功能
  2. 5. Spring 面向切面编程

5.5. 基于模式的 AOP 支持

上一页5.4.7. AOP 示例下一页5.6. 选择要使用的 AOP 声明样式

最后更新于1年前

这有帮助吗?

如果您更喜欢基于 XML 的格式,Spring 还支持使用aop命名空间标签定义切面。支持与使用 @AspectJ 样式时完全相同的切入点表达式和通知类型。因此,在本节中,我们将重点放在该语法上,并请读者参考上一节中的讨论(),以了解编写切入点表达式和通知参数的绑定。

要使用本节中描述的 aop 命名空间标签,您需要导入 spring-aop架构,如中所述。 有关如何在aop命名空间中导入标签的信息,请参阅。

在您的 Spring 配置中,所有切面和顾问元素都必须放在一个<aop:config>元素中(您可以在应用程序上下文配置中拥有多个<aop:config>元素)。一个<aop:config>元素可以包含切入点、顾问和切面元素(请注意,这些元素必须按此顺序声明)。

<aop:config>配置风格大量使用了 Spring 的 机制。如果您已经通过使用BeanNameAutoProxyCreator或类似的方式使用显式自动代理,这可能会导致问题(例如未编织通知) 。推荐的使用模式是仅使用<aop:config>样式或仅使用AutoProxyCreator样式并且从不混合使用它们。

5.5.1. 声明一个切面

当您使用模式支持时,切面是在 Spring 应用程序上下文中定义为 bean 的常规 Java 对象。在对象的字段和方法中捕获状态和行为,在 XML 中捕获切入点和通知信息。

您可以使用<aop:aspect>元素声明切面,并使用属性ref引用支持 bean ,如以下示例所示:

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        ...
    </aop:aspect>
</aop:config>

<bean id="aBean" class="...">
    ...
</bean>

支持切面的 bean(在这种情况下是aBean)当然可以像任何其他 Spring bean 一样进行配置和依赖注入。

5.5.2. 声明切入点

您可以在<aop:config>元素内声明一个命名切入点,让切入点定义在多个切面和顾问之间共享。

表示服务层中任何业务服务执行的切入点可以定义如下:

<aop:config>

    <aop:pointcut id="businessService"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>

</aop:config>
<aop:config>

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

</aop:config>

然后在切面内声明切入点与声明顶级切入点非常相似,如以下示例所示:

<aop:config>

    <aop:aspect id="myAspect" ref="aBean">

        <aop:pointcut id="businessService"
            expression="execution(* com.xyz.myapp.service.*.*(..))"/>

        ...
    </aop:aspect>

</aop:config>

与@AspectJ 切面非常相似,使用基于模式的定义样式声明的切入点可以收集连接点上下文。例如,以下切入点收集this对象作为连接点上下文并将其传递给通知:

<aop:config>

    <aop:aspect id="myAspect" ref="aBean">

        <aop:pointcut id="businessService"
            expression="execution(* com.xyz.myapp.service.*.*(..)) &amp;&amp; this(service)"/>

        <aop:before pointcut-ref="businessService" method="monitor"/>

        ...
    </aop:aspect>

</aop:config>

必须通过包含匹配名称的参数来声明通知以接收收集的连接点上下文,如下所示:

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

组合切入点子表达式时,&&在 XML 文档中很尴尬,因此您可以分别使用and、or和not关键字来代替&&、 ||和!。例如,前面的切入点可以更好地写成如下:

<aop:config>

    <aop:aspect id="myAspect" ref="aBean">

        <aop:pointcut id="businessService"
            expression="execution(* com.xyz.myapp.service.*.*(..)) and this(service)"/>

        <aop:before pointcut-ref="businessService" method="monitor"/>

        ...
    </aop:aspect>
</aop:config>

请注意,以这种方式定义的切入点由它们的 XML 引用,id不能用作命名切入点来形成复合切入点。因此,基于模式的定义风格中的命名切入点支持比@AspectJ 风格提供的更有限。

5.5.3. 声明通知

基于模式的 AOP 支持使用与 @AspectJ 样式相同的五种通知,并且它们具有完全相同的语义。

前置通知

Before 通知在匹配的方法执行之前运行。 通过在 <aop:aspect> 内声明使用<aop:before>元素,如以下示例所示:

<aop:aspect id="beforeExample" ref="aBean">

    <aop:before
        pointcut-ref="dataAccessOperation"
        method="doAccessCheck"/>

    ...

</aop:aspect>

这里,dataAccessOperation是在顶层 ( <aop:config>) 级别定义的切入点的id 。要改为内联定义切入点,请将pointcut-ref属性替换为pointcut属性,如下所示:

<aop:aspect id="beforeExample" ref="aBean">

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

    ...
</aop:aspect>

正如我们在讨论@AspectJ 样式时所指出的,使用命名切入点可以显着提高代码的可读性。

method属性标识提供通知正文的方法 (doAccessCheck )。必须为包含通知的切面元素引用的 bean 定义此方法。在执行数据访问操作(切入点表达式匹配的方法执行连接点)之前,将调用切面 bean 上的doAccessCheck方法。

返回通知

当匹配的方法执行正常完成时,返回通知运行后。它在 <aop:aspect>内部声明的方式与之前的通知相同。以下示例显示了如何声明它:

<aop:aspect id="afterReturningExample" ref="aBean">

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

    ...
</aop:aspect>

与@AspectJ 样式一样,您可以在通知正文中获取返回值。为此,请使用returning属性指定应将返回值传递到的参数名称,如以下示例所示:

<aop:aspect id="afterReturningExample" ref="aBean">

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

    ...
</aop:aspect>

该doAccessCheck方法必须声明一个名为retVal 的参数。此参数的类型以与@AfterReturning描述相同的方式约束匹配。例如,您可以如下声明方法签名:

public void doAccessCheck(Object retVal) {...

异常通知

当匹配的方法执行通过抛出异常退出时,抛出通知运行后。它通过在 <aop:aspect>内声明使用after-throwing元素,如以下示例所示:

<aop:aspect id="afterThrowingExample" ref="aBean">

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

    ...
</aop:aspect>

与@AspectJ 风格一样,您可以在通知正文中获取抛出的异常。为此,请使用throwing属性指定应将异常传递到的参数的名称,如以下示例所示:

<aop:aspect id="afterThrowingExample" ref="aBean">

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

    ...
</aop:aspect>

doRecoveryActions方法必须声明一个名为dataAccessEx 的参数。此参数的类型以与@AfterThrowing描述相同的方式约束匹配 。例如,方法签名可以声明如下:

public void doRecoveryActions(DataAccessException dataAccessEx) {...

(最终)通知之后

无论匹配的方法执行如何退出,(最终)通知都会运行。您可以使用after元素来声明它,如以下示例所示:

<aop:aspect id="afterFinallyExample" ref="aBean">

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

    ...
</aop:aspect>

环绕通知

最后一种通知是环绕通知。环绕通知“围绕”匹配方法的执行。它有机会在方法运行之前和之后进行工作,并确定该方法何时、如何以及是否真正开始运行。如果您需要以线程安全的方式在方法执行之前和之后共享状态(例如,启动和停止计时器),则通常使用环绕通知。

始终使用满足您要求的最不强大的通知形式。例如,如果之前的通知足以满足您的需求,请不要使用环绕通知。

以下示例展示了如何在 XML 中声明环绕通知:

<aop:aspect id="aroundExample" ref="aBean">

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

    ...
</aop:aspect>

通知的实现doBasicProfiling可以与@AspectJ 示例中的完全相同(当然,要减去注解),如以下示例所示:

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed();
    // stop stopwatch
    return retVal;
}

通知参数

<aop:before
    pointcut="com.xyz.lib.Pointcuts.anyPublicMethod() and @annotation(auditable)"
    method="audit"
    arg-names="auditable"/>

该arg-names属性接受以逗号分隔的参数名称列表。

以下基于 XSD 的方法稍微复杂一些的示例显示了一些与许多强类型参数结合使用的环绕通知:

package x.y.service;

public interface PersonService {

    Person getPerson(String personName, int age);
}

public class DefaultPersonService implements PersonService {

    public Person getPerson(String name, int age) {
        return new Person(name, age);
    }
}

接下来是切面。请注意,profile(..)方法接受许多强类型参数,其中第一个参数恰好是用于继续进行方法调用的连接点。此参数的存在表明 profile(..)将用作around通知,如以下示例所示:

package x.y;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;

public class SimpleProfiler {

    public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable {
        StopWatch clock = new StopWatch("Profiling for '" + name + "' and '" + age + "'");
        try {
            clock.start(call.toShortString());
            return call.proceed();
        } finally {
            clock.stop();
            System.out.println(clock.prettyPrint());
        }
    }
}

最后,以下示例 XML 配置会影响对特定连接点的上述通知的执行:

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

    <!-- this is the object that will be proxied by Spring's AOP infrastructure -->
    <bean id="personService" class="x.y.service.DefaultPersonService"/>

    <!-- this is the actual advice itself -->
    <bean id="profiler" class="x.y.SimpleProfiler"/>

    <aop:config>
        <aop:aspect ref="profiler">

            <aop:pointcut id="theExecutionOfSomePersonServiceMethod"
                expression="execution(* x.y.service.PersonService.getPerson(String,int))
                and args(name, age)"/>

            <aop:around pointcut-ref="theExecutionOfSomePersonServiceMethod"
                method="profile"/>

        </aop:aspect>
    </aop:config>

</beans>

考虑以下驱动程序脚本:

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import x.y.service.PersonService;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        BeanFactory ctx = new ClassPathXmlApplicationContext("x/y/plain.xml");
        PersonService person = (PersonService) ctx.getBean("personService");
        person.getPerson("Pengo", 12);
    }
}

使用这样的 Boot 类,我们将在标准输出中获得类似于以下内容的输出:

StopWatch 'Profiling for 'Pengo' and '12': running time (millis) = 0
-----------------------------------------
ms     %     Task name
-----------------------------------------
00000  ?  execution(getFoo)

通知优先级

与同一个 @Aspect 类中定义的通知方法的优先级规则相反,当同一个<aop:aspect> 元素中定义的两条通知都需要在同一连接点运行时,优先级由以下顺序决定:其中通知元素在封闭的 <aop:aspect> 元素中声明,优先级从最高到最低。

例如,给定在同一个 <aop:aspect> 元素中定义的应用于同一连接点的 around 通知和 before 通知,为了确保 around 通知比 before 通知具有更高的优先级, 元素必须在 <aop:before> 元素之前声明。

5.5.4. 切面说明

切面说明(在 AspectJ 中称为类型间声明)让切面声明通知对象实现给定接口并代表这些对象提供该接口的实现。

您可以通过在 aop:aspect 内使用 aop:declare-parents 元素进行切面说明。您可以使用 aop:declare-parents 元素来声明匹配类型具有新的父级(因此得名)。例如,给定一个名为UsageTracked 的接口以及名为DefaultUsageTracked 的该接口的实现,以下方面声明服务接口的所有实现者也实现UsageTracked 接口。 (例如,为了通过 JMX 公开统计信息。)

<aop:aspect id="usageTrackerAspect" ref="usageTracking">

    <aop:declare-parents
        types-matching="com.xzy.myapp.service.*+"
        implement-interface="com.xyz.myapp.service.tracking.UsageTracked"
        default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/>

    <aop:before
        pointcut="com.xyz.myapp.CommonPointcuts.businessService()
            and this(usageTracked)"
            method="recordUsage"/>

</aop:aspect>

支持usageTrackingbean 的类将包含以下方法:

public void recordUsage(UsageTracked usageTracked) {
    usageTracked.incrementUseCount();
}

要实现的接口由implement-interface属性决定。该types-matching属性的值是一个 AspectJ 类型模式。任何匹配类型的 bean 都会实现该UsageTracked接口。请注意,在前面示例的之前通知中,服务 bean 可以直接用作UsageTracked接口的实现。要以编程方式访问 bean,您可以编写以下代码:

UsageTracked usageTracked = (UsageTracked) context.getBean("myService");

5.5.5. 切面实例化模型

唯一受支持的模式定义切面的实例化模型是单例模型。未来版本可能支持其他实例化模型。

5.5.6. 顾问

Spring 通过<aop:advisor>元素支持顾问概念。您最常看到它与事务通知一起使用,后者在 Spring 中也有自己的命名空间支持。以下示例显示了一个顾问:

<aop:config>

    <aop:pointcut id="businessService"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>

    <aop:advisor
        pointcut-ref="businessService"
        advice-ref="tx-advice"/>

</aop:config>

<tx:advice id="tx-advice">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

除了pointcut-ref前面示例中使用的属性,您还可以使用该 pointcut属性内联定义切入点表达式。

要定义顾问的优先级以便通知可以参与排序,请使用order属性来定义顾问的Ordered值。

5.5.7。AOP 模式示例

由于并发问题(例如,死锁失败者),业务服务的执行有时会失败。如果该操作被重试,则很可能在下一次尝试时成功。对于在这种情况下适合重试的业务服务(不需要返回给用户解决冲突的幂等操作),我们希望透明地重试操作以避免客户端看到 PessimisticLockingFailureException. 这是一个明确跨越服务层中多个服务的要求,因此非常适合通过切面实现。

因为我们要重试操作,所以我们需要使用around通知,以便我们可以多次调用proceed。下面的清单显示了基本的切面实现(这是一个使用模式支持的常规 Java 类):

public class ConcurrentOperationExecutor implements Ordered {

    private static final int DEFAULT_MAX_RETRIES = 2;

    private int maxRetries = DEFAULT_MAX_RETRIES;
    private int order = 1;

    public void setMaxRetries(int maxRetries) {
        this.maxRetries = maxRetries;
    }

    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
        int numAttempts = 0;
        PessimisticLockingFailureException lockFailureException;
        do {
            numAttempts++;
            try {
                return pjp.proceed();
            }
            catch(PessimisticLockingFailureException ex) {
                lockFailureException = ex;
            }
        } while(numAttempts <= this.maxRetries);
        throw lockFailureException;
    }
}

请注意,切面实现了Ordered接口,以便我们可以将切面的优先级设置为高于事务通知(我们希望每次重试时都有一个新事务)。maxRetries和order属性都是由 Spring 配置的。主要动作doConcurrentOperation发生在around 通知方法中。我们尝试继续。如果我们以 PessimisticLockingFailureException失败,我们会再试一次,除非我们已经用尽了所有的重试尝试。

此类与@AspectJ 示例中使用的类相同,但删除了注解。

对应的Spring配置如下:

<aop:config>

    <aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">

        <aop:pointcut id="idempotentOperation"
            expression="execution(* com.xyz.myapp.service.*.*(..))"/>

        <aop:around
            pointcut-ref="idempotentOperation"
            method="doConcurrentOperation"/>

    </aop:aspect>

</aop:config>

<bean id="concurrentOperationExecutor"
    class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
        <property name="maxRetries" value="3"/>
        <property name="order" value="100"/>
</bean>

请注意,我们暂时假设所有业务服务都是幂等的。如果不是这种情况,我们可以通过引入注解并使用Idempotent注解来注解服务操作的实现,来细化切面,使其仅重试真正的幂等操作,如以下示例所示:

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

对仅重试幂等操作切面的更改涉及改进切入点表达式,以便仅@Idempotent操作匹配,如下所示:

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

请注意,切入点表达式本身使用与中描述的相同的 AspectJ 切入点表达式语言。如果您使用基于模式的声明样式,您可以在切入点表达式中引用类型 (@Aspects) 中定义的命名切入点。定义上述切入点的另一种方法如下:

假设您有中描述的CommonPointcuts切面。

您可以使用aop:around元素声明环绕通知。通知方法应该声明Object为它的返回类型,并且方法的第一个参数必须是 ProceedingJoinPoint。在通知方法的主体中,您必须调用ProceedingJoinPoint的 proceed()以使底层方法运行。不带参数调用proceed()将导致调用者的原始参数在调用时提供给底层方法。对于高级用例,该proceed()方法有一个重载变体,它接受参数数组 ( Object[])。调用时,数组中的值将用作底层方法的参数。有关.

基于模式的声明风格以与@AspectJ 支持相同的方式支持完全类型化的通知——通过按名称匹配切入点参数与通知方法参数。有关详细信息,请参阅。如果您希望为通知方法显式指定参数名称(不依赖于前面描述的检测策略),您可以使用 通知元素的arg-names属性来实现,该属性的处理方式与 通知注解中的argNames属性相同(如中所述)。以下示例显示如何在 XML 中指定参数名称:

当多条通知需要在同一个连接点(执行方法)运行时,排序规则如中所述。切面之间的优先级通过元素order中的属性<aop:aspect>或通过将@Order注解添加到支持切面的 bean 或通过让 bean 实现Ordered接口来确定。

“顾问”的概念来自 Spring 中定义的 AOP 支持,在 AspectJ 中没有直接的等价物。顾问就像一个独立的小切面,只有一条通知。通知本身由 bean 表示,并且必须实现 描述的通知接口之一。顾问可以利用 AspectJ 切入点表达式。

本节展示了 中的并发锁定失败重试示例在使用模式支持重写时的外观。

@AspectJ 支持
基于 XML 架构的配置
AOP 模式
自动代理
@AspectJ 支持
共享通用切入点定义
proceed使用
通知参数
确定参数名称
Advice Ordering
Spring 中的 Advice Types 中
一个 AOP 示例