baymon opened SPR-11775 and commented

I want to use @validated on a @Service bean in combination with @Async methods. But the order property of my MethodValidationPostProcessor has no effect to the execution order of the validation interceptor. So a validation exception is always thrown within the new thread and never given to the caller.

I don't see any (working) way to configure the execution order of the method interceptors.


Affects: 4.0.4

Reference URL: https://github.com/spring-projects/spring-framework-issues/pull/77

Issue Links: - #13635 Spring @Async should have some way of specifying UncaughtExceptionHandler - #19878 MethodValidationPostProcessor not respecting aspect order

Referenced from: commits https://github.com/spring-projects/spring-framework/commit/cee53e9330b1677921b466e255751dd28d0b4207

0 votes, 5 watchers

Comment From: spring-projects-issues

Stéphane Nicoll commented

See also this SO thread

Comment From: spring-projects-issues

Stéphane Nicoll commented

Jürgen, I have merged the repro project (see here).

This issue made me thought of #13635 so I updated the project to master with a simple AsyncUncaughtExceptionHandler. I can see that two ConstraintViolationException are indeed processed by the callback.

So it looks like it "works" but the exception is swallowed since the method is invoked asynchronously. Thoughts?

Comment From: spring-projects-issues

Juergen Hoeller commented

The root of the problem is AsyncAnnotationBeanPostProcessor's "beforeExistingAdvisors" flag which defaults to true, in contrast to MethodValidationPostProcessor which regularly adds its advisor to the end of the chain.

So if you set AsyncAnnotationBeanPostProcessor's "beforeExistingAdvisors" to false, regular post-processor ordering will have effect - and MethodValidationPostProcessor has a chance of coming first. I've added unit tests to verify that.

We should revisit what we can do here to make those options more obvious, or ideally to even allow for 'natural' ordering without any special setup.

Juergen

Comment From: spring-projects-issues

baymon commented

The AsyncAnnotationBeanPostProcessor is created by org.springframework.scheduling.annotation.ProxyAsyncConfiguration.asyncAdvisor(). How to get this bean and the set the beforeExistingAdvisors property. Is there a best practice?

If I implement the interface InitializingBean in my configuration and add the following, I get an "unresolvable circular reference" while context initialization.

@Autowired
protected AsyncAnnotationBeanPostProcessor asyncAnnotationBeanPostProcessor;

@Override
public void afterPropertiesSet() throws Exception {
    this.asyncAnnotationBeanPostProcessor.setBeforeExistingAdvisors(false);
}

Comment From: spring-projects-issues

Philipp Foermer commented

I have got a similar problem where I would like to define the method validation aspect ordering via the advisor order priority.

For spring 4.2+ it would be nice, if the MethodValidationPostProcessor supports to define the advisor ordering per builtin, like done in the following code snippet:

    @Setter
    public static class OrderedMethodValidationPostProcessor extends MethodValidationPostProcessor {

        private static final long serialVersionUID = 1L;

        private int methodValidationAspectOrder = Integer.MIN_VALUE;

        @Override
        public void afterPropertiesSet() {
            super.afterPropertiesSet();
            DefaultPointcutAdvisor advisor = (DefaultPointcutAdvisor) this.advisor;
            advisor.setOrder(methodValidationAspectOrder);
        }
    }

Best regards, Philipp Förmer

Comment From: snicoll

I am wondering if that's something we could specify on @EnableAsync, a boolean flag that would explain a bit more high-level what the outcome is going to be. WDYT @jhoeller?

Comment From: jhoeller

On review, I'm not convinced we should be making a different async-vs-validation order configurable at a first-class level. After all, regular programmatic assertions against incoming parameters are also only executing in the async thread in this case, and we generally recommend such programmatic assertions over declarative validation for such invocation parameter purposes.

If validation (whether programmatic or declarative) is expected to happen in the caller's thread, the invocation could also be split into two methods: one that validates/sanitizes the input in an entry-point facade and then delegates to another component/method for the actual async execution.