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

时间:2017-08-03 08:13:57

标签: java spring-boot jersey

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

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

我的自定义映射器:

 package com.rmn.gfc.common.providers;
 import ...

 @Provider
 public class JsonParseExceptionHandler implements ExceptionMapper<JsonParseException> { 
    @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?

2 个答案:

答案 0 :(得分:3)

你可以归咎于JacksonFeature

如果JacksonFeature未注册,则

JacksonJaxbJsonProvider会为Jackson例外注册默认的异常映射器。这正是你不想要的。

请参阅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注册JacksonFeature

Spring Boot的JerseyAutoConfiguration类将注册JacksonFeature,如果它在类路径上。请参阅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);
        };
    }

    ...
}

解决方法

作为一种变通方法,您可以注册JacksonJaxbJsonProvider然后注册自定义异常映射器(或者仅使用@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注释(按优先级顺序)。其他功能与JacksonJsonProvider相同。

替代解决方案

或者,您可以删除jersey-media-json-jackson工件并改为使用jackson-jaxrs-json-provider。有了这个,你将摆脱JacksonFeature,然后你可以注册自己的异常映射器。

这是answer中提到的。

似乎是正确的解决方案

请参阅JAX-RS 2.1 specification

中的以下引用
  

4.1.3优先事项

     

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

     

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

     

所有应用程序提供的提供程序的默认优先级为javax.ws.rs.Priorities.USER。关于优先级的一般规则对于过滤器和拦截器是不同的,因为收集了这些提供者   进入连锁店。

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

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

如果您手动注册提供商,可以give your provider a binding priority

@ApplicationPath("/")
public class MyResourceConfig extends ResourceConfig {

    public MyResourceConfig() {
        register(JsonParseExceptionMapper.class, 1);
    }
}

答案 1 :(得分:3)

我找到了对我来说没问题的解决方案 在自定义映射器上使用javax.annotation.Priority使其覆盖杰克逊默认映射器,例如:

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

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

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

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