Jackson JsonParseExceptionMapper和JsonMappingExceptionMapper阴影自定义映射器

我的项目使用Spring Boot + Jersey 2。

我为JsonParseException创建了自定义Jackson映射器,但它没有被调用,而是使用标准的Jackson JsonParseExceptionMapper。

我的自定义映射器:

package com.rmn.gfc.common.providers; import ... @Provider public class JsonParseExceptionHandler implements ExceptionMapper { @Override public Response toResponse(JsonParseException exception) { return Response .status(Response.Status.BAD_REQUEST) // entity ... .build(); } } 

我像这样注册我的映射器:

 @Component public class OrderServiceResourceConfig extends ResourceConfig { public OrderServiceResourceConfig() { packages("com.rmn.gfc.common.providers"); } } 

我确信mapper注册很好,因为这个包中的其他自定义映射器正在工作,但是JsonParseException的一个被Jersey标准JsonParseExceptionMapper遮蔽。

我怎么能用我的实现覆盖标准的Jackson JsonParseExceptionMapper?

我发现解决方案对我来说很好。
在自定义映射器上使用javax.annotation.Priority使其覆盖Jackson默认映射器,例如:

 @Provider @Priority(1) public class JsonParseExceptionHandler implements ExceptionMapper { // ... } 

或者,如果您通过ResourceConfig注册JAX-RS组件,您可以指定如下优先级:

 public class MyResourceConfig extends ResourceConfig { public MyResourceConfig() { register(JsonMappingExceptionHandler.class, 1); register(JsonParseExceptionHandler.class, 1); // ... } } 

较低的数字是最高优先级。
javax.ws.rs.Priorities有一些预定义的优先级常量。

你可以责怪JacksonFeature

如果JacksonJaxbJsonProvider未注册, JacksonJaxbJsonProvider会为Jackson例外注册默认的exception映射器。 这正是你不想要的。

查看源代码的相关部分:

 // Register Jackson. if (!config.isRegistered(JacksonJaxbJsonProvider.class)) { // add the default Jackson exception mappers context.register(JsonParseExceptionMapper.class); context.register(JsonMappingExceptionMapper.class); ... } 

Spring Boot注册了JacksonFeature

Spring Boot的JerseyAutoConfiguration类将注册JacksonFeature如果它在类路径上。 查看源代码的相关部分:

 @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); }; } ... } 

解决方法

作为一种解决方法,您可以注册JacksonJaxbJsonProvider然后注册您的自定义exception映射器(或者只是使用@Provider注释它们以便泽西自动发现):

 @Component public class OrderServiceResourceConfig extends ResourceConfig { public OrderServiceResourceConfig() { packages("com.rmn.gfc.common.providers"); register(JacksonJaxbJsonProvider.class); // Register other providers } } 

查看文档中有关JacksonJaxbJsonProvider

JSON内容类型提供程序自动配置为使用Jackson和JAXB注释(按优先级顺序)。 其他function与JacksonJsonProvider相同。

替代解决方案

或者你可以摆脱jersey-media-json-jackson神器并改用jackson-jaxrs-json-provider 。 有了这个,你将摆脱JacksonFeature ,然后你可以注册自己的exception映射器。

在这个答案中提到了。

什么似乎是正确的解决方案

请参阅JAX-RS 2.1规范中的以下引用:

4.1.3优先事项

应用程序提供的提供程序使开发人员能够扩展和自定义JAX-RS运行时。 因此,应用程序提供的提供程序必须始终优先于预先打包的提供程序,如果需要单个提供程序。

应用程序提供的提供程序可以使用@Priority进行注释。 如果两个或多个提供者是某个任务的候选者,则选择具有最高优先级的提供者:在这种情况下,最高优先级被定义为具有最低值的提供者。 也就是说, @Priority(1)高于@Priority(10) 。 如果两个或更多提供者符合条件且具有相同的优先级,则以依赖于实现的方式选择一个。

所有应用程序提供的提供程序的默认优先级是javax.ws.rs.Priorities.USER 。 关于优先级的一般规则对于filter和拦截器是不同的,因为这些提供者被收集到链中。

正如Kysil Ivan的回答所指出的那样,编写自己的exception映射器并赋予它高优先级,例如1 。 如果您使用自动发现,只需使用@Provider@Priority注释@Priority

 @Provider @Priority(1) public class JsonParseExceptionMapper implements ExceptionMapper { ... } 

如果您手动注册提供商,则可以为您的提供商提供绑定优先权 :

 @ApplicationPath("/") public class MyResourceConfig extends ResourceConfig { public MyResourceConfig() { register(JsonParseExceptionMapper.class, 1); } }