为什么 Spring Around advice 可以吞噬或阻止目标方法抛出的异常的传播?

Why a Spring Around advice can swallow, or halt the propagation of, an exception thown by the target method?

我正在学习 Spring 核心认证,根据我的学习,我对这个问题有以下疑问 material:

Which of the following statments is NOT true about advice types and exception handling?

  • If a Before advice throws an exception, the target method will not be called.

  • An Around advice can swallow, or halt the propagation of, an exception thown by the target method.

  • An AfterReturning advice type can swallow, or halt the propagation of, an exception thown by the target method.

现在我知道上一题的正确答案是最后一道(我有答案了)但是为什么呢?

所以它问我哪个陈述不正确所以这意味着前两个陈述是正确的。

我正在尝试通过一些具体示例来分析前 3 个案例,但我不确定我的推理是否正确。

1) 建议前:

我可以有这样的东西:

@Aspect
public class PropertyChangeTracker {
    private Logger logger = Logger.getLogger(getClass());

    @Before(“execution(void check*(*))”)
    public void trackCheck() {
        logger.info(“Property about to check…”);
   }
}

因此,每次执行 checkSomething(oneArgument) 方法时,都会执行已实施的建议(将日志行创建到 .log 文件中)。如果在执行此方法期间抛出异常,则不执行建议。

我认为这很清楚

2) AROUND ADVICE,我知道这是Around advice

的时序图

我有以下此类建议的示例:

@Around("execution(* com.journaldev.spring.model.Employee.getName())")
public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
    System.out.println("Before invoking getName() method");
    Object value = null;
    try {
        value = proceedingJoinPoint.proceed();
    } catch (Throwable e) {
        e.printStackTrace();
    }
    System.out.println("After invoking getName() method. Return value="+value);
    return value;
}

阅读官方文档发现:

Around advice: Advice that surrounds a join point such as a method invocation. This is the most powerful kind of advice. Around advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.

所以在我看来,Around advice是用来切断jointpoint执行前后的方法执行的。我们可以用它来控制建议的方法是否执行。我们还可以检查返回值并更改它。这是最有力的建议,需要正确应用。

所以在前面的例子中,我认为它执行了两次:第一次执行 getName() 方法之前,第二次执行 getName()方法被执行。

但是

value = proceedingJoinPoint.proceed();

我认为它是联合点前后执行的分界点,在这种情况下我认为 proceed() 方法说 getName() 方法必须执行,其结果将被放入 value 字段。是对的还是我遗漏了什么?

所以回到原来的说法我可以说

An Around advice can swallow, or halt the propagation of, an exception thown by the target method.

为什么我可以说这是真的?具体是什么意思?

使用 @Around 就像编写自己的代码并调用方法一样,不同之处在于您使用 proceedingJoinPoint.proceed()。与普通方法调用一样,您可以选择不执行它(将其包装在条件中)或捕获它抛出的错误(将调用包装在 try 块中)。

来自spring documentation

Around advice is declared using the @Around annotation. The first parameter of the advice method must be of type ProceedingJoinPoint. Within the body of the advice, calling proceed() on the ProceedingJoinPoint causes the underlying method to execute. The proceed method may also be called passing in an Object[] - the values in the array will be used as the arguments to the method execution when it proceeds.

因此,具体来说:value = proceedingJoinPoint.proceed(); 导致调用底层方法并将其 return 值分配给 value

An Around advice can swallow, or halt the propagation of, an exception thown by the target method.

这是通过以下方式实现的:

try {
   proceedingJoinPoint.proceed();
} catch (Throwable e) {
    // ignore or handle
}

So in the previous example I think that it is performed twice time: the first one before that the getName() method is executed and a second one after that the getName() method is executed.

如果我理解你在这里的想法,那你就错了。通知只被调用一次,里面的代码负责调用实际的通知方法getName()。这就是 proceedingJoinPoint.proceed() 所做的。

一个典型的场景 - 您拦截方法调用,执行一些检查,调用方法(或不调用,可能取决于检查的结果),然后 return 结果。在您发布的示例中, proceed()try - catch 块包围,这意味着您可以捕获 getName() 抛出的异常并用它做任何您想做的事情。这就解释了为什么你问题中的第二句话是正确的。