Reputation: 21152
As a good practice in a Spring MVC application, the Web configuration should pick up only the "front-end" components, such as @Controller
or @RestController
.
Every other bean should be picked up by the Root application context.
I've defined the Web configuration as follow (keep in mind I don't need the @EnableMvc
annotation, as it extends WebMvcConfigurationSupport
)
@Configuration
@ComponentScan(
basePackages = { ... },
useDefaultFilters = false,
includeFilters = @Filter({
Controller.class,
ControllerAdvice.class}))
And the Root configuration as follows.
@Configuration
@ComponentScan(
basePackages = { ... },
excludeFilters = @Filter({
Controller.class,
ControllerAdvice.class}))
I've defined two @RestControllerAdvice
classes, the first one catches all generic Exception
(s), the second one catches a more specific ServiceException
.
When throwing a ServiceException
, the specific adviser is never called, instead only the generic one is picked. Base packages are the same in both configuration classes.
Do I need to specify also RestControllerAdvice
on the exclude and include filters? Or am I missing something else?
Edit:
Both @RestControllerAdvice
are without basePackeges
or any specific criteria.
And the ServiceException
one is actually found and registered.
If I move the exception handler to the working handler than it is called.
This is how I got it working. If I move the ServiceException
handler in a separate class it is no more invoked.
@RestControllerAdvice
public class GlobalRestControllerAdviser extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleBindException(
final BindException ex,
final HttpHeaders headers,
final HttpStatus status,
final WebRequest request) {
return new ResponseEntity<Object>(
buildPresentableError(ex.getAllErrors().get(0)),
HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(ServiceException.class)
protected Response<?> handleServiceException(final ServiceException e) {
...
}
@ExceptionHandler(Exception.class)
protected ResponseEntity<Object> handleGenericException(final Exception ex) {
...
}
}
Seems like the most generic ExceptionHandler
is overriding the more specific one.
Upvotes: 4
Views: 14966
Reputation: 21152
Basically @ControllerAdvice
annotated classes are ordered, which means that if Spring internals find an @ExceptionHandler
which accepts the thrown exception, it will use that one and stop.
When having multiple classes it is possible to set the bean priority with @Order
(for example). Annotating the ServiceException
containing class with @Order
made it working.
Also, based on this feature request https://jira.spring.io/browse/SPR-8881 we can specify multiple classes on one @Filter
annotation, no need to split them in multiple @Filter
(s).
Upvotes: 0
Reputation: 44486
Almost there, use the FilterType type
and separate the filters.
@Configuration
@ComponentScan(
basePackages = { ... },
excludeFilters = {
@ComponentScan.Filter(type=FilterType.ANNOTATION, value=Controller.class),
@ComponentScan.Filter(type=FilterType.ANNOTATION, value=ControllerAdvice.class)
}
)
Alternatively, I suggest you create a custom annotation (ex. @FrontEnd
) and apply the filter to it.
Upvotes: 5