如何配置由Spring HATEOAS注册的MappingJackson2HttpMessageConverter

16

我喜欢在我的项目中使用spring-hateoas,并使用@EnableHypermediaSupport进行配置。 问题是,这个神奇的配置注释会注册它自己的MappingJackson2HttpMessageConverter,我的自定义转换器将被忽略。

背景:我向我的项目添加了一些Jackson模块(例如JodaModule),并希望使用objectMapper.findAndRegisterModules();进行注册。这可以通过覆盖WebMvcConfigurationSupportWebMvcConfigurer中的configureMessageConverters(List<HttpMessageConverter<?>> converters)方法来完成。

我的当前配置如下:

@Configuration
@EnableHypermediaSupport(type = HAL)
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

  @Override
  protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.getObjectMapper().findAndRegisterModules();

    converters.add(converter);
  }
}

有没有一种方法可以自定义由Spring HATEOAS使用的MappingJackson2HttpMessageConverterObjectMapper ?


你是指自定义MappingJackson2HttpMessageConverter吗?覆盖它的行为?如果你想要覆盖ObjectMapper,你可以调用MappingJackson2HttpMessageConverter.getObjectMapper()并配置你喜欢的ObjectMapper。 - hutingung
我肯定很感兴趣知道如何做到这一点。我认为在使用Spring HATEOAS时,每个Spring应用程序上下文初始化至少3(也许4或更多)个不同的ObjectMapper实例。 我无法配置它们中的任何一个; 我唯一能够做到的就是通过注释目标类来完成,但这对于全局首选项来说有点痛苦。 - Jonathan W
3个回答

1
我必须做同样的事情。使用HATEOAS .16,我能够让它工作...但非常丑陋。关键在于HypermediaSupportBeanDefinitionRegistrar中注册HAL转换器的部分会检查是否已经有一个HAL转换器,然后再尝试添加另一个。所以我只需在WebMVCConfig :: configureMessageConverters中自己添加一个HAL转换器即可。类似于:
private static final String DELEGATING_REL_PROVIDER_BEAN_NAME = "_relProvider";
private static final String LINK_DISCOVERER_REGISTRY_BEAN_NAME = "_linkDiscovererRegistry";
private static final String HAL_OBJECT_MAPPER_BEAN_NAME = "_halObjectMapper";

@Autowired
private ListableBeanFactory beanFactory;

private static CurieProvider getCurieProvider(BeanFactory factory) {

    try {
        return factory.getBean(CurieProvider.class);
    } catch (NoSuchBeanDefinitionException e) {
        return null;
    }
}

@Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        List<HttpMessageConverter<?>> baseConverters = new ArrayList<HttpMessageConverter<?>>();
        super.configureMessageConverters(baseConverters);

        //Need to override some behaviour in the HAL Serializer...so let's make our own
        CurieProvider curieProvider = getCurieProvider(beanFactory);
        RelProvider relProvider = beanFactory.getBean(DELEGATING_REL_PROVIDER_BEAN_NAME, RelProvider.class);
        ObjectMapper halObjectMapper = beanFactory.getBean(HAL_OBJECT_MAPPER_BEAN_NAME, ObjectMapper.class);

        halObjectMapper.registerModule(new Jackson2HalModule());
        halObjectMapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider));

        MappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(
            ResourceSupport.class);
        halConverter.setSupportedMediaTypes(Arrays.asList(HAL_JSON));
        halConverter.setObjectMapper(halObjectMapper);

        converters.add(halConverter);
    }

这显然依赖于具体实现,使用了实现细节,并且无法让您修改@EnableHyperMediaSupport为您构建的那个...但目前它可以工作。

0
我找到了一个丑陋的解决方案:
我使用了BeanPostProcessor和大量的反射技巧,将Spring HATEOAS内部的ConversionService替换为我自己的ConversionService。在此之前,我已经将我的ConversionService添加到了Spring上下文中。这样一来,我就可以确保Spring HATEOAS使用与Spring MVC完全相同的ConversionService。
/**
 * This is a HACK to work around a not yet implemented feature. At the moment Spring Hateoas uses a
 * {@link ConversionService}, which is hold in a private static final field and hence cannot be accessed to add more
 * Converters<br/>
 *
 * <ul>
 *   <li><a href="https://github.com/spring-projects/spring-hateoas/issues/118">Spring Hateoas Issue</a></li>
 *   <li><a
 *     href="https://dev59.com/fH3aa4cB1Zd3GeqPblJu">
 *     Solution on Stackoverflow</a></li>
 * </ul>
 */
public static class HateoasConversionServicePostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
        if (bean instanceof ConversionService) {
            try {
                Class<?> clazz = Class.forName(
                        "org.springframework.hateoas.mvc.AnnotatedParametersParameterAccessor$BoundMethodParameter");
                Field field = clazz.getDeclaredField("CONVERSION_SERVICE");
                field.setAccessible(true);

                Field modifiersField = Field.class.getDeclaredField("modifiers");
                modifiersField.setAccessible(true);
                modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

                field.set(null, bean);

                modifiersField.setInt(field, field.getModifiers() & Modifier.FINAL);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
        return bean;
    }
}

与Chris的解决方案相比,这种解决方案的优点在于完整的ConversionService可能包含比MappingJackson2HttpMessageConverter更有用的转换器。HATEOAS不仅使用其ConversionService将结果映射到JSON,还将用作链接构建器API中参数的对象转换为URL路径变量。 - André Menneken

0
我使用以下方法。
@Configuration
@EnableHypermediaSupport(type = HypermediaType.HAL)
public class MvcConfig {    

  @Bean
  public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {

似乎完全不起作用...spring-hateoas 0.16.0.RELEASE仍然无法使用注册的模块转换我的资源/实体。:( - Hendy Irawan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接