Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to provide a @ControllerAdvice conditional on bean?

I'm creating a framework that I'd like to reuse in all of my future projects. If the sub project does not define a @ControllerAdvice, I want my framework to automatically initialize a default Advice for exception handling.

public class ExHandler implements IAdvice {
    ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ResponseBody
    public ErrorDTO default(xception e) {
        return new ErrorDTO();
    }
}

I tried as follows, but does not work:

@Configuration
static class MyConfig {
    @ControllerAdvice
    @ConditionalOnMissingBean(IAdvice.class)
    static class AdviceExHandler extends ExHandler {

    }
}

In Sub project:

@ControllerAdvice
public class SubHandler extends ExHandler {
}

Result: It works. BUT: if the subproject does not define the ExHandler, the bean is not initialized at all! But why?

Sidenote: I'm trying to prevent mutliple ControllerAdvice because error handling depends on the order of methods inside the exception handler. Thus I don't want to mess the order by introducing multiple classes.

like image 703
membersound Avatar asked Oct 15 '25 09:10

membersound


2 Answers

You may use @ConditionalOnMissingBean(annotation = ControllerAdvice.class) to configure condition on missing bean with ControllerAdvice annotation.

@ControllerAdvice
public abstract class FrameworkAdvice {
...
}

And conditionally configure it:

@Configuration
@ConditionalOnMissingBean(annotation = ControllerAdvice.class)
public class FrameworkAdviceConfig {
    @Bean
    public FrameworkAdvice frameworkAdvice() {
        return new FrameworkAdvice() {
        };
    }
}

And if there is another controller advice in project, it will be used instead.

@ControllerAdvice
public class CustomAdvice {
...
}
like image 102
Igor Rybak Avatar answered Oct 16 '25 22:10

Igor Rybak


You could use it without wrappers. Just declare @ControllerAdvice annotated class as following in Kotlin:

@ControllerAdvice
@ConditionalOnMissingBean(annotation = [ControllerAdvice::class])
class ExceptionHandler {

    @ResponseStatus(HttpStatus.EXPECTATION_FAILED)
    @ResponseBody
    @ExceptionHandler
    fun handleProcessingException(e: Exception): ErrorDto {
        return ErrorDto()
    }
}

And just declare it in spring.factories file if you're doing it for starter:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  ru.raiffeisen.ecommerce.controller.ExceptionHandler

That's all. If there is no @ControllerAdvice annotated classes, then will be used class from configuration.

like image 36
Dracontis Avatar answered Oct 16 '25 21:10

Dracontis