You can blame the JacksonFeature
JacksonFeature
registers default exception mappers for Jackson exceptions if the JacksonJaxbJsonProvider
is not registered. And that's exactly what you don't want.
See the relevant parts of the source code:
// Register Jackson.
if (!config.isRegistered(JacksonJaxbJsonProvider.class)) {
// add the default Jackson exception mappers
context.register(JsonParseExceptionMapper.class);
context.register(JsonMappingExceptionMapper.class);
...
}
Spring Boot registers the JacksonFeature
The Spring Boot's JerseyAutoConfiguration
class will register the JacksonFeature
if it's on the classpath. See the relevant parts of the source code:
@ConditionalOnClass(JacksonFeature.class)
@ConditionalOnSingleCandidate(ObjectMapper.class)
@Configuration
static class JacksonResourceConfigCustomizer {
...
@Bean
public ResourceConfigCustomizer resourceConfigCustomizer(
final ObjectMapper objectMapper) {
addJaxbAnnotationIntrospectorIfPresent(objectMapper);
return (ResourceConfig config) -> {
config.register(JacksonFeature.class);
config.register(new ObjectMapperContextResolver(objectMapper),
ContextResolver.class);
};
}
...
}
Workaround
As a workaround, you can register the JacksonJaxbJsonProvider
and then register your custom exception mappers (or just annotate them with @Provider
to get automatically discovered by Jersey):
@Component
public class OrderServiceResourceConfig extends ResourceConfig {
public OrderServiceResourceConfig() {
packages("com.rmn.gfc.common.providers");
register(JacksonJaxbJsonProvider.class);
// Register other providers
}
}
See what the documentation says about JacksonJaxbJsonProvider
:
JSON content type provider automatically configured to use both Jackson and JAXB annotations (in that order of priority). Otherwise functionally same as JacksonJsonProvider
.
Alternative solution
Alternatively you can get rid of the jersey-media-json-jackson
artifact and use jackson-jaxrs-json-provider
instead. With this, you will get rid of JacksonFeature
and then you can register your own exception mappers.
It was mentioned in this answer.
What seems to be the correct solution
See the following quote from the JAX-RS 2.1 specification:
4.1.3 Priorities
Application-supplied providers enable developers to extend and customize the JAX-RS runtime. Therefore, an application-supplied provider MUST always be preferred over a pre-packaged one if a single one is required.
Application-supplied providers may be annotated with @Priority
. If two or more providers are candidates for a certain task, the one with the highest priority is chosen: the highest priority is defined to be the one
with the lowest value in this case. That is, @Priority(1)
is higher than @Priority(10)
. If two or more providers are eligible and have identical priorities, one is chosen in an implementation dependent manner.
The default priority for all application-supplied providers is javax.ws.rs.Priorities.USER
. The general rule about priorities is different for filters and interceptors since these providers are collected
into chains.
As pointed in Kysil Ivan's answer, write your own exception mapper and give it a high priority, such as 1
. If you use auto discovery, just annotate it with @Provider
and @Priority
.
@Provider
@Priority(1)
public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
...
}
If you manually register your provider, you can give your provider a binding priority:
@ApplicationPath("/")
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(JsonParseExceptionMapper.class, 1);
}
}