我也一直遇到同样的问题很长一段时间了,并通过以下方式解决了它。@Florian走在正确的轨道上,多亏了他的建议,我找到了一种使转换自动工作的方法。需要以下几个部分:
- 一种转换服务,用于实现从 URI 到实体的转换(利用随框架提供的 UriToEntityConverter)
- 一个反序列化程序,用于检测何时适合调用转换器(我们不想弄乱默认的SDR行为)
- 自定义 Jackson 模块,可将所有内容推送到 SDR
对于第 1 点,实现可以缩小到以下内容
import org.springframework.context.ApplicationContext;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.repository.support.DomainClassConverter;
import org.springframework.data.rest.core.UriToEntityConverter;
import org.springframework.format.support.DefaultFormattingConversionService;
public class UriToEntityConversionService extends DefaultFormattingConversionService {
private UriToEntityConverter converter;
public UriToEntityConversionService(ApplicationContext applicationContext, PersistentEntities entities) {
new DomainClassConverter<>(this).setApplicationContext(applicationContext);
converter = new UriToEntityConverter(entities, this);
addConverter(converter);
}
public UriToEntityConverter getConverter() {
return converter;
}
}
对于第2点,这是我的解决方案
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator;
import your.domain.RootEntity; // <-- replace this with the import of the root class (or marker interface) of your domain
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.rest.core.UriToEntityConverter;
import org.springframework.util.Assert;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;
public class RootEntityFromUriDeserializer extends BeanDeserializerModifier {
private final UriToEntityConverter converter;
private final PersistentEntities repositories;
public RootEntityFromUriDeserializer(PersistentEntities repositories, UriToEntityConverter converter) {
Assert.notNull(repositories, "Repositories must not be null!");
Assert.notNull(converter, "UriToEntityConverter must not be null!");
this.repositories = repositories;
this.converter = converter;
}
@Override
public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc, BeanDeserializerBuilder builder) {
PersistentEntity<?, ?> entity = repositories.getPersistentEntity(beanDesc.getBeanClass());
boolean deserializingARootEntity = entity != null && RootEntity.class.isAssignableFrom(entity.getType());
if (deserializingARootEntity) {
replaceValueInstantiator(builder, entity);
}
return builder;
}
private void replaceValueInstantiator(BeanDeserializerBuilder builder, PersistentEntity<?, ?> entity) {
ValueInstantiator currentValueInstantiator = builder.getValueInstantiator();
if (currentValueInstantiator instanceof StdValueInstantiator) {
EntityFromUriInstantiator entityFromUriInstantiator =
new EntityFromUriInstantiator((StdValueInstantiator) currentValueInstantiator, entity.getType(), converter);
builder.setValueInstantiator(entityFromUriInstantiator);
}
}
private class EntityFromUriInstantiator extends StdValueInstantiator {
private final Class entityType;
private final UriToEntityConverter converter;
private EntityFromUriInstantiator(StdValueInstantiator src, Class entityType, UriToEntityConverter converter) {
super(src);
this.entityType = entityType;
this.converter = converter;
}
@Override
public Object createFromString(DeserializationContext ctxt, String value) throws IOException {
URI uri;
try {
uri = new URI(value);
} catch (URISyntaxException e) {
return super.createFromString(ctxt, value);
}
return converter.convert(uri, TypeDescriptor.valueOf(URI.class), TypeDescriptor.valueOf(entityType));
}
}
}
然后对于第3点,在自定义存储库RestConfigurerAdapter中,
public class MyRepositoryRestConfigurer extends RepositoryRestConfigurerAdapter {
@Override
public void configureJacksonObjectMapper(ObjectMapper objectMapper) {
objectMapper.registerModule(new SimpleModule("URIDeserializationModule"){
@Override
public void setupModule(SetupContext context) {
UriToEntityConverter converter = conversionService.getConverter();
RootEntityFromUriDeserializer rootEntityFromUriDeserializer = new RootEntityFromUriDeserializer(persistentEntities, converter);
context.addBeanDeserializerModifier(rootEntityFromUriDeserializer);
}
});
}
}
这对我来说很顺利,并且不会干扰框架的任何转换(我们有许多自定义终结点)。在第 2 点中,目的是仅在以下情况下启用从 URI 的实例化:
- 要反序列化的实体是根实体(因此没有属性)
- 提供的字符串是实际的 URI(否则它只是回退到默认行为)