🍊
翻译橙
🍊返回主站🤖参与贡献
  • 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 面向切面编程
  3. 5.4. @AspectJ 支持

5.4.4. 声明切点

Advice 与切入点表达式相关联,并在切入点匹配的方法执行之前、之后或周围运行。切入点表达式可以是对命名切入点的简单引用,也可以是就地声明的切入点表达式。

前置通知

您可以使用注解@Before在切面声明之前的通知:

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

@Aspect
public class BeforeExample {

    @Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }
}

如果我们使用就地切入点表达式,我们可以将前面的示例重写为以下示例:

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

@Aspect
public class BeforeExample {

    @Before("execution(* com.xyz.myapp.dao.*.*(..))")
    public void doAccessCheck() {
        // ...
    }
}

返回通知

返回通知后,当匹配的方法执行正常返回时运行。您可以使用@AfterReturning注解来声明它:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

    @AfterReturning("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }
}

你可以有多个通知声明(以及其他成员),都在同一个切面。我们在这些示例中只展示了一个通知声明,以集中每个通知的效果。

有时,您需要在通知正文中访问返回的实际值。您可以使用@AfterReturning绑定返回值的形式来获取该访问权限,如以下示例所示:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

    @AfterReturning(
        pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
        returning="retVal")
    public void doAccessCheck(Object retVal) {
        // ...
    }
}

属性returning中使用的名称必须与通知方法中的参数名称相对应。当方法执行返回时,返回值作为相应的参数值传递给通知方法。returning子句还将匹配限制为仅返回指定类型的值的那些方法执行(在这种情况下,Object匹配任何返回值)。

请注意,在返回通知后使用时,不可能返回完全不同的参考。

异常通知

当匹配的方法执行通过抛出异常退出时,抛出通知运行后。您可以使用@AfterThrowing注解来声明它,如以下示例所示:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

    @AfterThrowing("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doRecoveryActions() {
        // ...
    }
}

通常,您希望通知仅在引发给定类型的异常时运行,并且您还经常需要访问通知正文中引发的异常。您可以使用throwing属性来限制匹配(如果需要 -否则用Throwable 作异常类型)并将抛出的异常绑定到通知参数。以下示例显示了如何执行此操作:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

    @AfterThrowing(
        pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
        throwing="ex")
    public void doRecoveryActions(DataAccessException ex) {
        // ...
    }
}

属性throwing中使用的名称必须与通知方法中的参数名称相对应。当通过抛出异常退出方法执行时,异常将作为相应的参数值传递给通知方法。throwing子句还将匹配限制为仅抛出指定类型的异常(在本例中为DataAccessException )的那些方法执行。

请注意,@AfterThrowing这并不表示一般的异常处理回调。具体来说,@AfterThrowing通知方法只应该从连接点(用户声明的目标方法)本身接收异常,而不是从伴随的 @After/@AfterReturning方法接收异常。

(最终)后置通知

当匹配的方法执行退出时(最终)通知运行。它是通过使用@After注解来声明的。After 通知必须准备好处理正常和异常返回条件。它通常用于释放资源和类似目的。下面的例子展示了如何使用 after finally 通知:

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

@Aspect
public class AfterFinallyExample {

    @After("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doReleaseLock() {
        // ...
    }
}

请注意,@AfterAspectJ 中的通知被定义为“在 finally 通知之后”,类似于 try-catch 语句中的 finally 块。它将在连接点(用户声明的目标方法)抛出的任何结果、正常返回或异常时调用,与之相反,@AfterReturning它仅适用于成功的正常返回。

环绕通知

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

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

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

around 通知返回的值是方法调用者看到的返回值。例如,一个简单的缓存切面可以从缓存中返回一个值,如果它有一个值,或者调用proceed()(并返回该值)如果它没有。请注意,proceed 可能会在环绕通知的主体内调用一次、多次或根本不调用。所有这些都是合法的。

如果您将环绕通知方法的返回类型声明为void,将始终返回给调用者null ,有效地忽略任何调用proceed()的结果。因此,使用环绕通知方法声明返回类型为Object. 通知方法通常应该返回调用proceed()返回的值,即使底层方法具有void返回类型。但是,根据用例,通知可以选择返回缓存值、包装值或其他值。

下面的例子展示了如何使用环绕通知:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class AroundExample {

    @Around("com.xyz.myapp.CommonPointcuts.businessService()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        // start stopwatch
        Object retVal = pjp.proceed();
        // stop stopwatch
        return retVal;
    }
}

通知参数

Spring 提供完全类型化的通知,这意味着您可以在通知签名中声明所需的参数(正如我们之前在返回和抛出示例中看到的那样),而不是一直使用数组Object[]。我们将在本节后面看到如何使参数和其他上下文值可用于通知主体。首先,我们看一下如何编写通用通知,以了解通知当前通知的方法。

访问当前JoinPoint

任何通知方法都可以声明类型为 的参数作为其第一个参数 org.aspectj.lang.JoinPoint。请注意,使用环绕通知来声明的第一个参数类型是ProceedingJoinPoint,它是JoinPoint 的子类。

该JoinPoint接口提供了许多有用的方法:

  • getArgs():返回方法参数。

  • getThis():返回代理对象。

  • getTarget():返回目标对象。

  • getSignature():返回所通知方法的描述。

  • toString():打印所通知方法的有用描述。

将参数传递给 Advice

我们已经看到了如何绑定返回值或异常值(在返回和抛出通知之后使用)。要使参数值可用于通知正文,您可以使用args. 如果在args表达式中使用参数名称代替类型名称,则在调用通知时相应参数的值将作为参数值传递。一个例子应该更清楚地说明这一点。假设您要通知执行以Account 对象为第一个参数的 DAO 操作,并且您需要访问通知正文中的帐户。您可以编写以下内容:

@Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation() && args(account,..)")
public void validateAccount(Account account) {
    // ...
}

args(account,..)切入点表达式的部分有两个目的。首先,它将匹配限制为只匹配那些方法至少有一个参数的方法执行,并且传递给该参数的参数是Account. 其次,它通过参数使实际Account对象可用于通知account 。

另一种写法是声明一个切入点, 当它匹配一个连接点时“提供”Account对象值,然后从通知中引用命名的切入点。这将如下所示:

@Pointcut("com.xyz.myapp.CommonPointcuts.dataAccessOperation() && args(account,..)")
private void accountDataAccessOperation(Account account) {}

@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
    // ...
}

有关详细信息,请参阅 AspectJ 编程指南。

代理对象 ( this)、目标对象 ( target) 和注解 ( @within、 @target、@annotation和@args) 都可以以类似的方式绑定。接下来的两个示例显示了如何匹配带有@Auditable 注解的方法的执行并提取审计代码:

这两个示例中的第一个显示了@Auditable注解的定义:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
    AuditCode value();
}

这两个示例中的第二个显示了与@Auditable方法执行相匹配的通知:

@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
public void audit(Auditable auditable) {
    AuditCode code = auditable.value();
    // ...
}

通知参数和泛型

Spring AOP 可以处理类声明和方法参数中使用的泛型。假设你有一个像下面这样的泛型:

public interface Sample<T> {
    void sampleGenericMethod(T param);
    void sampleGenericCollectionMethod(Collection<T> param);
}

您可以通过将通知参数绑定到要拦截方法的参数类型来将方法类型的拦截限制为某些参数类型:

@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
    // Advice implementation
}

这种方法不适用于泛型集合。所以你不能定义一个切入点如下:

@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) {
    // Advice implementation
}

为了完成这项工作,我们必须检查集合的每个元素,这是不合理的,因为我们也无法决定如何处理null一般的值。要实现类似的效果,您必须键入参数Collection<?>并手动检查元素的类型。

确定参数名称

通知调用中的参数绑定依赖于切入点表达式中使用的名称与通知和切入点方法签名中声明的参数名称的匹配。参数名称不能通过 Java 反射获得,因此 Spring AOP 使用以下策略来确定参数名称:

  • 如果用户已明确指定参数名称,则使用指定的参数名称。通知和切入点注解都有一个可选argNames属性,您可以使用它来指定带注解的方法的参数名称。这些参数名称在运行时可用。以下示例显示了如何使用该argNames属性:

@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
        argNames="bean,auditable")
public void audit(Object bean, Auditable auditable) {
    AuditCode code = auditable.value();
    // ... use code and bean
}

如果第一个参数是JoinPoint、ProceedingJoinPoint或 JoinPoint.StaticPart类型,则可以在argNames属性值中省略参数名称。例如,如果您修改前面的通知以接收连接点对象,则argNames属性不需要包含它:

@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
        argNames="bean,auditable")
public void audit(JoinPoint jp, Object bean, Auditable auditable) {
    AuditCode code = auditable.value();
    // ... use code, bean, and jp
}

对JoinPoint, ProceedingJoinPoint和JoinPoint.StaticPart类型的第一个参数的特殊处理对于不收集任何其他连接点上下文的通知实例特别方便。在这种情况下,您可以省略该argNames属性。例如,以下通知不需要声明argNames属性:

@Before("com.xyz.lib.Pointcuts.anyPublicMethod()")
public void audit(JoinPoint jp) {
    // ... use jp
}
  • 使用argNames属性有点笨拙,所以如果没有指定argNames属性,Spring AOP 会查看类的调试信息并尝试从局部变量表中确定参数名称。只要使用调试信息(至少)编译了类,就会出现此信息。使用此标志进行编译的后果是:(1)您的代码更容易理解(逆向工程),(2)类文件大小稍微大一点(通常无关紧要),(3)优化以删除未使用的本地您的编译器未应用变量。换句话说,打开此标志进行构建应该不会遇到任何困难。

    如果 AspectJ 编译器 ( ajc) 已经编译了 @AspectJ 切面,即使没有调试信息,您也不需要添加argNames属性,因为编译器会保留所需的信息。

  • 如果在没有必要调试信息的情况下编译了代码,Spring AOP 会尝试推断绑定变量与参数的配对(例如,如果切入点表达式中只绑定了一个变量,并且advice 方法只接受一个参数,则配对很明显)。如果给定可用信息,变量的绑定不明确,则抛出AmbiguousBindingException。

  • 如果上述所有策略均失败,则抛出 IllegalArgumentException。

参数处理

我们之前提到过,我们将描述如何编写一个带有在 Spring AOP 和 AspectJ 中一致工作的参数的proceed调用。解决方案是确保通知签名按顺序绑定每个方法参数。以下示例显示了如何执行此操作:

@Around("execution(List<Account> find*(..)) && " +
        "com.xyz.myapp.CommonPointcuts.inDataAccessLayer() && " +
        "args(accountHolderNamePattern)")
public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
        String accountHolderNamePattern) throws Throwable {
    String newPattern = preProcess(accountHolderNamePattern);
    return pjp.proceed(new Object[] {newPattern});
}

在许多情况下,无论如何都要执行此绑定(如前面的示例中所示)。

通知优先级

当多条通知都想在同一个连接点运行时会发生什么?Spring AOP 遵循与 AspectJ 相同的优先级规则来确定通知执行的顺序。最高优先级的通知首先“在进入的路上”运行(因此,给定两条之前的通知,优先级最高的一条首先运行)。从连接点“退出”时,优先级最高的通知最后运行(因此,给定两条后通知,具有最高优先级的一条将运行第二个)。

当不同方面定义的两条通知都需要在同一个连接点运行时,除非您另外指定,否则执行顺序是未定义的。您可以通过指定优先级来控制执行顺序。这是通过在方面类中实现 org.springframework.core.Ordered 接口或使用 @Order 注释对其进行注释,以正常的 Spring 方式完成的。给定两个方面,从 Ordered.getOrder() (或注释值)返回较低值的方面具有较高的优先级。

特定方面的每个不同通知类型在概念上都意味着直接应用于连接点。因此,@AfterThrowing 通知方法不应从随附的 @After/@AfterReturning 方法接收异常。

从 Spring Framework 5.2.7 开始,需要在同一连接点运行的同一 @Aspect 类中定义的通知方法将根据其通知类型按以下顺序分配优先级,从最高优先级到最低优先级:@Around、@Before 、@After、@AfterReturning、@AfterThrowing。但请注意,@After 通知方法将在同一方面中的任何 @AfterReturning 或 @AfterThrowing 通知方法之后有效地调用,遵循 AspectJ 的 @After 的“after finally 通知”语义。

当在同一个 @Aspect 类中定义的两个相同类型的通知(例如,两个 @After 通知方法)都需要在同一连接点运行时,顺序是未定义的(因为无法检索源)通过 javac 编译类的反射来声明代码顺序)。考虑将此类通知方法折叠为每个 @Aspect 类中每个连接点的一个通知方法,或者将通知片段重构为单独的 @Aspect 类,您可以通过 Ordered 或 @Order 在方面级别订购这些类。

上一页5.4.3. 声明切入点下一页5.4.5. 切面说明

最后更新于1年前

这有帮助吗?

使用proceed 调用Object[]时的行为与 AspectJ 编译器编译的proceed 焕荣通知的行为略有不同。对于使用传统 AspectJ 语言编写的环绕通知,传递给proceed的参数 数量必须与传递给环绕通知的参数数量相匹配(而不是底层连接点采用的参数数量),并且传递给给定继续进行的值参数位置替换值绑定到的实体的连接点处的原始值(如果现在没有意义,请不要担心)。Spring 采用的方法更简单,更符合其基于代理的、仅执行的语义。如果您编译为 Spring 编写的@AspectJ切面和与 AspectJ 编译器和编织器一起使用proceed参数,您只需要注意这种差异 。有一种方法可以编写跨 Spring AOP 和 AspectJ 100% 兼容的切面,这将在 的部分中讨论。

有关更多详细信息,请参阅。

下一节有关通知参数
javadoc