克服“无法构造InterfaceClass实例”而不提示父

时间:2017-01-03 21:24:51

标签: java spring spring-boot spring-annotations

我的控制器中有这个方法:

@RequestMapping(method = RequestMethod.POST)
InterfaceClass insert(@RequestBody InterfaceClass interfaceClass) {

    // Do something
}

我得到的错误非常直接且不言自明:

Can not construct instance of InterfaceClass: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information.

基本上,我需要告诉Spring我有InterfaceClassClassImpl的具体实现。

我试过了:

@JsonRootName("InterfaceClass")
public class ClassImpl implements InterfaceClass {
}

到任何程度。我无法使用@JsonTypeInfo ,因为父接口类InterfaceClass不应该知道ClassImpl并且它们位于不同的模块。我也尝试过:

使用摘要InterfaceClass实施AbstractClass并输入:

@JsonDeserialize(as = AbstractClass.class)

位于InterfaceClass之上。然后使用AbstractClass扩展ClassImpl。错误只会变成:

Can not construct instance of InterfaceClass: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information.

进一步尝试:

public class ControllerClass<E extends InterfaceClass> {

    @RequestMapping(method = RequestMethod.POST)
    InterfaceClass insert(@RequestBody E interfaceClass) {
        InterfaceClass object = (InterfaceClass) interfaceClass;
    }
}

,结果是:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to InterfaceClass

正如所料。

我真的希望Spring Boot能够处理组件发现,因为InterfaceClassAbstractClass只有一个具体实现,在我的类路径中是ClassImpl。也许我做错了什么?我怎样才能克服这个问题,而没有明确暗示InterfaceClass实现的位置(例如没有@JsonDeserialize等)?

1 个答案:

答案 0 :(得分:3)

解决方案1 ​​ - 动态注册子类型

您可以动态定义子类型。

<强> 1。在界面上,定义要用作标识符的JSON字段(@type):

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, 
              include = JsonTypeInfo.As.PROPERTY, property = "@type")
public interface InterfaceClass {
}

<强> 2。将“@type”字段添加到您的JSON有效负载

{
    ...
    "@type": "someName"
}

<强> 2。动态注册inteface的子类型:

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() {
        public void configure(ObjectMapper objectMapper) {
            objectMapper.registerSubtypes(ClassImpl.class);
            super.configure(objectMapper);
        };
    };
    return builder;
}

<强> 4。在具体类上指定“@type”名称(可选):

//Optional, otherwise uses the Simple class name (ie: 'ClassImpl')
@JsonTypeName("someName") 
public class ClassImpl implements InterfaceClass {
}

<强> 5。现在可以使用@RequestBody的界面:

@RequestMapping(method = RequestMethod.POST)
InterfaceClass insert(@RequestBody InterfaceClass interfaceClass) {
}

解决方案2 - 动态注册自定义反序列化器

如果无法(或不想要)添加@type字段,您还可以为您的界面定义自定义反序列化器,实际上会创建ClassImpl

<强> 1。定义自定义反序列化器:

class ClassImplJsonDeserializer extends JsonDeserializer<ClassImpl> {
    @Override
    public ClassImpl deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        return jp.readValuesAs(ClassImpl.class).next();
    }
}

<强> 2。动态设置自定义反序列化器:

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.deserializerByType(InterfaceClass.class, new ClassImplJsonDeserializer());
    return builder;
}

第3。从界面中删除@JsonTypeInfo:

public interface InterfaceClass {
}