【问题标题】:In Spring: Using Interface as RequestBody and returning the right class在 Spring:使用接口作为 RequestBody 并返回正确的类
【发布时间】:2014-05-27 07:45:18
【问题描述】:

我正在尝试在 Spring 控制器中使用接口,但无法实例化正确的类。

我有以下方法:

@RequestMapping(value="/settings/features/{feature}", method=RequestMethod.POST, produces = "application/json; charset=utf-8")
@ResponseBody
public ResponseEntityWrapper setFeatureSettings(
        HttpServletRequest request,
        @PathVariable Feature feature,
        @RequestBody IFeatureSettings featureSettings,
        HttpServletResponse response
        ) {
           ...
        }

我有几个实现 IFeatureSettings 的类,我需要为输入实例化正确的类。我可以通过查看特征(它是一个枚举)来知道哪个是正确的类。因此,对于功能 A,我将拥有 AFeatureSettingsImpl,对于功能 B,我将拥有 BFeatureSettingsImpl。

理想情况下,有一些方法可以在中间说:这一次,用数据填充类 AFeatureSettingsImpl 并将其作为接口实现返回。

我一直在研究 HandlerMethodArgumentResolver,这听起来是正确的方法,但我不知道该怎么做。我需要访问 feature 参数,希望在我得到解决代码之前解决这个问题(或者我需要自己解决它)。

【问题讨论】:

    标签: java spring interface controller


    【解决方案1】:

    编写另一个以featurebodyObject 作为参数的方法怎么样?

    类似

    private final IFeatureSettings getFeatureImpl(Feature feature, Object bodyObject) {
    switch(feature) {
    case a: return new AFeatureSettingsImpl;
    ...
       }
    }
    

    顺便说一句,我真的不知道你想要的东西是否存在。

    【讨论】:

    • 感谢您的回答,但问题不在于选择正确的实现类,而是让 Spring 从输入中自动创建正确的类。这是一道春季题。
    【解决方案2】:

    简单的解决方案是在方法签名中使用Map 而不是IFeatureSettings。然后根据您的 Feature 鉴别器,您可以手动实例化和填充 bean。使用Apache BeanUtils 看起来像这样:

    @RequestMapping(value="/settings/features/{feature}", method=RequestMethod.POST, produces = "application/json; charset=utf-8")
    @ResponseBody
    public ResponseEntityWrapper setFeatureSettings(
        HttpServletRequest request,
        @PathVariable Feature feature,
        @RequestBody Map featureSettings,
        HttpServletResponse response) {
            IFeatureSettings bean = createBean(feature); // instantiating the right bean based on the feature discriminator
            BeanUtils.populate(bean, featureSettings);
            ...
    }
    

    如果您想让它更通用,请查看 HttpMessageConverters。 Spring 默认使用MappingJacksonHttpMessageConverter 将json 与@RequestBody 绑定。您必须使用自定义逻辑扩展此转换器。请记住,在这种情况下,特征鉴别器不能是独立的参数,因为您需要在转换器中访问它。您可以放入 HTTP 标头以使其工作。转换器的readInternal 方法看起来像:

    @Override
    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        Class<?> actualClass = lookupByFeature(inputMessage.getHeaders().get("feature")); // returns the actual Class type based on the feature found in the header
        return super.readInternal(actualClass, inputMessage); // and now the original implementation can use the actual type instead of the interface
    }
    

    【讨论】:

    • 添加地图作为参数并不能解决我的问题,因为 Spring 不知道如何自己填充它,因此没有理由将该地图作为方法参数的一部分。正如您所建议的,我当然可以根据不同的功能手动实例化类,但我宁愿构建一个更通用的解决方案,它也适用于其他情况。使用消息转换器是另一种选择(尽管不是那么好),这意味着我将不得不再次手动绑定自己,并且必须以某种方式再次手动访问功能参数。
    • 这不对。 Spring 能够使用您的 json 中的键值对填充映射。如果您想要更通用的解决方案,则需要使用上述转换器。您无法避免自己编写绑定逻辑。 Spring 怎么会知道你的 enum->bean 实现映射?
    • 问题是Spring的输入(实际上是jackson)是一个对象,没有关于对象类型的提示,并且服务器端的接口没有说明可以是哪个实现类使用(它只是一个接口,Imp部分未知)。那么杰克逊如何知道选择哪个实现类来将它得到的值映射到一个真实的对象呢?
    • 不是Jackson,而是Spring的MappingJacksonHttpMessageConverter!这是您需要扩展和覆盖 readInternal 方法以便能够根据特征鉴别器选择正确的 bean 实现的方法。这就是鉴别器必须转到 HTTP 标头的原因。我将用更多细节更新我的答案以使其清楚。
    • 这个答案看起来是个不错的方向,我试试看,谢谢!我会更新成功/失败。
    猜你喜欢
    • 1970-01-01
    • 2019-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-09
    • 1970-01-01
    • 2011-06-27
    相关资源
    最近更新 更多