扩展异常方面以获得特定于应用程序的实现

Extending a exception aspect in order to have application specific implementation

在我的 spring 引导应用程序中,我一直在使用一个外部公共库来处理异常。外部库定义了一个方面,如下所示:

@Aspect
@Order(0)
public class InternalExceptionAspect {

  public InternalExceptionAspect() {
  }

  @Pointcut("@within(org.springframework.stereotype.Service)")
  public void applicationServicePointcut() {
  }

  @AfterThrowing(
    pointcut = "applicationServicePointcut()",
    throwing = "e"
  )
  public void translate(JoinPoint joinPoint, Throwable e) {
    String resourceId = this.getResourceId();
    if (e instanceof BadInputException) {
      BadInputException inputException = (BadInputException)e;
      throw new BadRequestAlertException(inputException.getErrorCode().getDefaultMessage(), inputException.getMessage(), inputException.getErrorCode().getHttpStatusCode(), resourceId, inputException.getErrorCode().getCode());
    } else if (!(e instanceof BadServerStateException) && !(e instanceof InternalException)) {
      String message;
      if (e instanceof JDBCException) {
        ...
        throw new BadServerStateException(message, resourceId, "20");
      } else {
        ...
        throw new BadServerStateException(message, resourceId, "10");
      }
    } else {
      InternalException serverStateException = (InternalException)e;
      throw new BadServerStateException(serverStateException.getErrorCode().getDefaultMessage(), serverStateException.getMessage(), resourceId, serverStateException.getErrorCode().getHttpStatusCode(), serverStateException.getErrorCode().getCode(), serverStateException.getErrorCode().getErrorType().name());
    }
  }

  String getResourceId() {
    RequestHeaders requestHeaders = RequestResponseContext.getRequestHeaders();
    return requestHeaders.getResourceId();
  }
}

在这里我想介绍另一个 else if 块,以便为我的应用程序处理 DuplicateKeyException

问题是,上面的代码作为公共库的一部分,正被多个其他应用程序使用。但是,我想做的更改只适用于我的应用程序。

我一直在考虑在我的应用程序中继承方面 class 如下所示:

 @Aspect
 @Order(0)
 public class MyInternalExceptionAspect extends InternalExceptionAspect {
  public MyInternalExceptionAspect() {
  }

  @Pointcut("@within(org.springframework.stereotype.Service)")
  public void applicationServicePointcut() {
  }

  @AfterThrowing(
    pointcut = "applicationServicePointcut()",
    throwing = "e"
  )
  public void translate(JoinPoint joinPoint, Throwable e) { 
        if(e instanceof DuplicateKeyException) {
            ...
         }
         super.translate(joinpoint, e);
     }
}

但是,我不确定这样做是否正确。任何人都可以在这里帮助实现这一目标的最佳方法是什么?谢谢

您不能使用 class MyInternalExceptionAspect extends InternalExceptionAspect 扩展具体方面。它会在 Spring AOP:

中引起异常
...AopConfigException:
  [...MyInternalExceptionAspect] cannot extend concrete aspect
  [...InternalExceptionAspect]

只有抽象的方面才能扩展。

但是您可以简单地创建一个没有继承的新方面,并确保它的优先级低于原始方面。

为什么要降低优先级?

  • 根据 @Order javadoc,“较低的值具有较高的优先级”
  • 您希望在 原始方面可能将感兴趣的异常转换为其他东西之前,在您有机会处理之前启动您自己方面的 @AfterReturning 建议它。但是根据 Spring manual: " 最高优先级建议 运行 是第一个"在进来的路上"(所以,给定两条之前的建议,优先级最高的那个 运行s first)。从连接点“离开”时,最高优先级通知 运行s 最后(因此,给定两条后通知,具有最高优先级的通知将 运行 第二个)。.
  • 因此,你自己的方面应该有@Order(1),给予它比原来的方面低的优先级,但是让@AfterThrowing建议运行在原来的之前。对不起,反向逻辑,即使它是有道理的。你只需要知道它。

这是一个MCVE,以简化的方式模拟您的情况:

package de.scrum_master.spring.q69862121;

import org.springframework.stereotype.Service;

@Service
public class MyService {
  public void doSomething(Throwable throwable) throws Throwable {
    if (throwable != null)
      throw throwable;
  }
}
package de.scrum_master.spring.q69862121;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;

@SpringBootApplication
@Configuration
public class DemoApplication {
  private static final Logger log = LoggerFactory.getLogger(DemoApplication.class.getName());

  public static void main(String[] args) throws Throwable {
    try (ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args)) {
      doStuff(appContext);
    }
  }

  private static void doStuff(ConfigurableApplicationContext appContext) {
    MyService myService = appContext.getBean(MyService.class);
    List<Throwable> throwables = Arrays.asList(
      null,                                   // No exception -> no aspect should kick in
      new Exception("oops"),                  // Not covered by any aspects -> no translation
      new IllegalArgumentException("uh-oh"),  // Original aspect translates to RuntimeException
      new NullPointerException("null"),       // Custom aspect translates to RuntimeException
      new ArithmeticException("argh")         // Custom aspect translates to IllegalArgumentException,
                                              // then original aspect translates to RuntimeException
    );

    for (Throwable originalThrowable : throwables) {
      try {
        myService.doSomething(originalThrowable);
      }
      catch (Throwable translatedThrowable) {
        log.info(translatedThrowable.toString());
      }
    }
  }
}

如您所见,应用程序调用服务,第一次使用 null,没有引起任何异常,然后对于几种类型的异常,这些方面将被忽略或转换。

package de.scrum_master.spring.q69862121;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Order(0)
public class InternalExceptionAspect {
  private static final Logger log = LoggerFactory.getLogger(InternalExceptionAspect.class.getName());

  @AfterThrowing(pointcut = "@within(org.springframework.stereotype.Service)", throwing = "e")
  public void translate(JoinPoint joinPoint, Throwable e) {
    log.info(joinPoint + "  -> " + e);
    if (e instanceof IllegalArgumentException)
      throw new RuntimeException("Transformed by InternalExceptionAspect", e);
  }
}
package de.scrum_master.spring.q69862121;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Order(1)
public class MyInternalExceptionAspect {
  private static final Logger log = LoggerFactory.getLogger(MyInternalExceptionAspect.class.getName());

  @AfterThrowing(pointcut = "@within(org.springframework.stereotype.Service)", throwing = "e")
  public void translate(JoinPoint joinPoint, Throwable e) {
    log.info(joinPoint + "  -> " + e);
    if (e instanceof NullPointerException)
      throw new RuntimeException("Transformed by MyInternalExceptionAspect", e);
    if (e instanceof ArithmeticException)
      throw new IllegalArgumentException("Transformed by MyInternalExceptionAspect", e);
  }
}

控制台日志证明一切都按预期工作,也与调用顺序有关:

d.s.s.q.MyInternalExceptionAspect        : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable))  -> java.lang.Exception: oops
d.s.s.q69862121.InternalExceptionAspect  : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable))  -> java.lang.Exception: oops
d.s.spring.q69862121.DemoApplication     : java.lang.Exception: oops
d.s.s.q.MyInternalExceptionAspect        : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable))  -> java.lang.IllegalArgumentException: uh-oh
d.s.s.q69862121.InternalExceptionAspect  : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable))  -> java.lang.IllegalArgumentException: uh-oh
d.s.spring.q69862121.DemoApplication     : java.lang.RuntimeException: Transformed by InternalExceptionAspect
d.s.s.q.MyInternalExceptionAspect        : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable))  -> java.lang.NullPointerException: null
d.s.s.q69862121.InternalExceptionAspect  : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable))  -> java.lang.RuntimeException: Transformed by MyInternalExceptionAspect
d.s.spring.q69862121.DemoApplication     : java.lang.RuntimeException: Transformed by MyInternalExceptionAspect
d.s.s.q.MyInternalExceptionAspect        : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable))  -> java.lang.ArithmeticException: argh
d.s.s.q69862121.InternalExceptionAspect  : execution(void de.scrum_master.spring.q69862121.MyService.doSomething(Throwable))  -> java.lang.IllegalArgumentException: Transformed by MyInternalExceptionAspect
d.s.spring.q69862121.DemoApplication     : java.lang.RuntimeException: Transformed by InternalExceptionAspect