Как настроить MappingJackson2HttpMessageConverter, зарегистрированный spring -hateoas
Мне нравится использовать spring -hateoas в моем проекте и настраивать его с помощью @EnableHypermediaSupport
.
Проблема заключается в том, что эта магия аннотации конфигурации регистрирует свой собственный MappingJackson2HttpMessageConverter
, и мой собственный настраиваемый конвертер будет проигнорирован.
Справочная информация. Я добавил некоторые модули Джексона (например, JodaModule
) в мой проект, и я хочу, чтобы они регистрировались с помощью objectMapper.findAndRegisterModules();
. Это делается путем переопределения configureMessageConverters(List<HttpMessageConverter<?>> converters)
в WebMvcConfigurationSupport
или WebMvcConfigurer
.
Моя текущая конфигурация выглядит следующим образом:
@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);
}
}
Есть ли способ настроить MappingJackson2HttpMessageConverter
или ObjectMapper
, который используется spring -hateoas?
Ответы
Ответ 1
Я нашел уродливое решение для своей проблемы:
Я использую BeanPostProcessor и много волшебства отражения, чтобы заменить внутренний ConversionService Spring HATEOAS 'своим собственным, который ранее был добавлен в контекст Spring. Таким образом, я уверен, что Spring HATEOAS использует точно такой же ConversionService, как Spring MVC.
/**
* 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="http://stackoverflow.com/questions/22240155/converter-from-pathvariable-domainobject-to-string-using-controllerlinkbuilde">
* 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;
}
}
Ответ 2
Я должен был сделать то же самое. С HATEOAS.16 мне удалось выполнить эту работу... но действительно очень уродливо.
Ключом было то, что в HypermediaSupportBeanDefinitionRegistrar часть, которая регистрирует конвертер HAL, проверяет, есть ли уже конвертер HAL, прежде чем он попытается добавить еще один. Поэтому я просто добавил конвертер HAL в свой WebMVCConfig:: configureMessageConverters самостоятельно.
Что-то вроде:
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 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 для вас... но она работает пока.
Ответ 3
Я использую следующий подход
@Configuration
@EnableHypermediaSupport(type = HypermediaType.HAL)
public class MvcConfig {
@Bean
public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {