【问题标题】:add meta data to java throwable object将元数据添加到 java throwable 对象
【发布时间】:2020-08-12 10:51:18
【问题描述】:

在我的应用程序中,我执行一些业务逻辑,例如我有业务逻辑方法:

@Override
@ByPassable(exceptions = {"InvalidIdentityException"})
public void validate(Model model) {
    if (nonNull(model)) {
        final boolean test = isOk(model.getIdentity());
        if (test) {
            throw new InvalidIdentityException("Invalid bla bla");
        }
    }
}

以及自定义异常类:

public class InvalidIdentityException extends SomeException {

    public InvalidIdentityException (final String message) {
        super(message);
    }
}

方法上的@ByPassable 获取可以绕过的异常列表,因此在这种情况下会抛出InvalidIdentityException,并且在不久的将来重新执行此方法时它会变为bypassable

我为我的 Spring Boot 应用程序启动了一个具有一组可绕过异常的 bean:

public class Config {

    @Bean("bypassable-exceptions")
    public Set<String> getBypassableExceptions() {
        final Set<String> exceptions = new HashSet<>();
        new Reflections(new MethodAnnotationsScanner())
                .getMethodsAnnotatedWith(ByPassable.class).stream()
                .filter(method -> method.getAnnotation(ByPassable.class).enabled())
                .forEach(method -> {
                    final String[] exceptions = method.getAnnotation(ByPassable.class).exceptions();
                    exceptions.addAll(Arrays.asList(exceptions));
                });
        return exceptions;
    }
}

每当在方法中抛出 Bypassable 异常时,我的应用程序都会将 Throwable 对象作为 Json 保存在数据库中,但是我需要在该可抛出对象上添加一个额外的布尔属性 bypassable@BeforeThrowing 异常更新为拦截。这可能吗?

public class ExceptionAspect {

    @Pointcut("@annotation(com.services.aop.ByPassable)")
    public void byPassableExceptionMethods() {
    }

    @BeforeThrowing(pointcut = "byPassableExceptionMethods()", throwing = "exception")
    public void beforeThrowingAdviceForByPassableExceptionMethods(final JoinPoint jp,
                                                                 final Throwable exception) {

     // check against the set of bypassable exceptions and update a custom property on the exception 
        class so when Throwable is persisted it is persisted with this customer property e.g. bypassable 
         = true

    }

【问题讨论】:

  • if (test)true时是否需要拦截并设置属性?此属性不是 Exception 构造函数的一部分的任何具体原因?
  • 要求是拦截在抛出可绕过异常的方法上抛出的异常,并在 Exception 类上添加标志以表明它是可绕过的
  • @AfterThrowing 是建议类型,我相信你已经经历过了。如果要求在方法抛出异常时提供建议,@AfterThrowing 将捕获该异常。 @BeforeThrowing 的用法有点混乱,因为我无法理解这里的确切要求

标签: java spring exception spring-aop


【解决方案1】:

来自 Spring 参考文档:AOP Concepts 没有建议类型 @BeforeThrowing

在 Spring AOP 中,可以建议方法执行(连接点) - 在方法开始之前、在方法结束之后(有或无异常)或前后(在方法开始之前和方法结束之后)。这也意味着该方法中的逻辑在运行时无法更改,只能操作方法执行的输入或结果。

根据共享的代码逻辑,异常是基于方法内的验证抛出的,Spring AOP 在抛出异常之前不提供通知句柄。

话虽如此,以下是我能想到的实现相同目标的方法。

  1. Bypassable 异常会根据条件引发,并且可以在异常实例创建时间本身期间设置字段 bypassable。这将是最简单的方法。

以下是我想出的实现相同的 Spring AOP 方法。

  1. @AfterThrowing可以如下设置绕过。

  2. @BeforeThrowing 可以模拟。

注意:使用 Spring AOP 不能拦截内部调用。参考文档中的相关信息可以在section 下找到。

由于 Spring 的 AOP 框架基于代理的特性,内部调用 根据定义,目标对象不会被拦截。

因此,出于演示目的,示例代码自动连接自己的参考。抛出异常的方法可能会被移到另一个 bean 中,类似地被拦截。

对示例进行了以下更改。

具有公共基类的可绕过异常

public class BaseBypassableException extends RuntimeException {

    private boolean bypassable;

    public BaseBypassableException(String message) {
        super(message);
    }

    public boolean isBypassable() {
        return bypassable;
    }

    public void setBypassable(boolean bypassable) {
        this.bypassable = bypassable;
    }
}

可绕过的异常扩展自通用基类

public class InvalidIdentityException extends BaseBypassableException {

    public InvalidIdentityException(String message) {
        super(message);
    }
}

通知方法修改如下。 (示例有String 而不是Model

@Component
public class BypassableServiceImpl implements BypassableService {

    @Autowired
    BypassableService service;

    @Override
    @ByPassable(exceptions = {"InvalidIdentityException"})
    public void validate(String model) {
        if (null != model) {
            final boolean test = !("Ok".equals(model));
            if (test) {
                service.throwException(new InvalidIdentityException("Invalid bla bla"));
            }
        }
        System.out.println("Validate called : "+model);

    }

    @Override
    public void throwException(BaseBypassableException exception) {
        throw exception;
    }

}

方面建议这两种方法。 throwing 基于异常类型进行过滤,因此对于示例,我没有包含检查 bypassableExceptionNames 的逻辑,并且逻辑安全地假定异常类型为 BaseBypassableException。如果需要,可以修改逻辑以包含检查。

@Component
@Aspect
public class ExceptionAspect {

    @Autowired
    @Qualifier("bypassable-exceptions")
    Set<String> bypassableExceptionNames;

    @Pointcut("@annotation(com.services.aop.ByPassable)")
    public void byPassableExceptionMethods() {
    }

    @AfterThrowing(pointcut = "byPassableExceptionMethods()", throwing = "exception")
    public void afterThrowingAdviceForByPassableExceptionMethods(final JoinPoint jp,
            final BaseBypassableException exception) {
        System.out.println(jp.getSignature());
        System.out.println("Before " + exception.isBypassable());
        exception.setBypassable(true);
        System.out.println("After " + exception.isBypassable());
        System.out.println(exception);
    }

    @Before("execution(* com.services..*.*(..)) && args(exception)")
    public void beforeThrowingAdviceForByPassableExceptionMethods(final JoinPoint jp,
            final BaseBypassableException exception) {
        System.out.println(jp.getSignature());
        System.out.println("Before " + exception.isBypassable());
        exception.setBypassable(true);
        System.out.println("After " + exception.isBypassable());
        System.out.println(exception);
    }
}

希望对你有帮助

【讨论】:

  • 感谢您的回答。它进入@AfterThrowing 方面,只有throw new InvalidIdentityException("Invalid bla bla");。我们可以失去throwException()
  • 添加两者仅用于演示目的。如果这解决了您的问题,请将其标记为答案,以便可以关闭问题
猜你喜欢
  • 2010-10-09
  • 2013-01-03
  • 2020-04-05
  • 1970-01-01
  • 2017-02-24
  • 2021-09-16
  • 2022-08-19
  • 2013-12-13
  • 1970-01-01
相关资源
最近更新 更多