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

@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,应将其更新为作为拦截的例外。这可能吗?

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

    }

最佳答案

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

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

根据共享的代码逻辑,将基于方法内的验证引发异常,并且在抛出异常之前,Spring AOP不会提供建议的句柄。

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


根据条件引发一个Bypassable异常,并且可以在异常实例创建期间自行设置字段bypassable。这将是最简单的方法。


以下是我提出的实现相同目标的Spring AOP方法。


@AfterThrowing可以如下设置可旁路。
可以模拟@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);
    }
}


希望这可以帮助

10-06 13:42