【问题标题】:Can I control how spring controller method arguments are instantiated?我可以控制如何实例化弹簧控制器方法参数吗?
【发布时间】:2013-05-17 11:45:23
【问题描述】:

考虑一下 spring 项目中的以下接口/对象层次结构:

public interface MyInterface {
    //method defenitions
}

@Component
@Scope(SCOPE_PROTOTYPE)
public class MyClass implements MyInterface {
   //method implementations
}

我在控制器方法中使用MyClass,它是从请求正文中读取的:

@RequestMapping(method = POST, value = "/posturi", consumes = "application/json")
public void createEntity(@RequestBody MyClass myClass) {
    //handle request
}

jackson 库用于读取 json 数据并将其转换为 java 对象。

我想将控制器方法中的参数类型从MyClass更改为MyInterface。这似乎不起作用,因为无法使用 new 运算符实例化接口。但它可以这样创建:

MyInterface instance = applicationContext.getBean(MyInterface.class);

是否可以让 spring/jackson 以这种方式实例化对象?我想这样做,这样我的控制器就不需要知道使用了什么实现。

【问题讨论】:

    标签: java spring spring-mvc jackson


    【解决方案1】:

    转换器应该可以。请参阅文档http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/validation.html。问题是,你怎么知道转换器返回哪个类?而是重新考虑您的设计以在输入中使用 POJO。

    【讨论】:

    • 重点是不知道实例将是什么。只要它实现了 MyInterface 就可以了。
    • 查看转换器的文档,我看不出如何在不知道 MyClass 的情况下使用它们将 MyInterface 的实例注入控制器方法中。
    • 应该是这样的:class StringToMyInterface implements Converter<String, MyInterface> { public MyInterface convert(String source) { MyInterface i = new MyClass(); // which implements MyIterface return i; } } 你总是要返回类的实例
    【解决方案2】:

    我现在已经解决了这个问题,这个概念很简单,但实现起来可能有点棘手。据我了解,您可以使用@RequestBody 注释任何类型,只要您提供可以从http 请求转换为所需类型的HttpMessageConverter。 所以解决办法是:

    1. 实现HttpMessageConverter
    2. 配置 spring 以便使用您的 HttpMessageConverter

    第二部分可能有点棘手。这是因为 spring 添加了一堆默认的 HttpMessageConverter 可以处理常见的类型,如字符串、整数、日期,我希望这些继续像往常一样工作。另一个问题是,如果jackson在路径上,spring还会添加一个MappingJackson2HttpMessageConverter用于通用json处理,例如转换为具体对象、地图等。 Spring 将使用它发现的第一个 HttpMessageConverter 声称能够转换为您的类型。 MappingJackson2HttpMessageConverter 声称可以为我的对象这样做,但它不能,因此它失败并且请求失败。这可能被认为是一个错误......

    我想要的链条是:

    1. 弹簧默认HttpMessageConverters。
    2. 我自己的HttpMessageConverter
    3. MappingJackson2HttpMessageConverter

    我找到了两种方法来实现这一点。首先,您可以通过 xml 显式声明它。

    <mvc:annotation-driven>
        <mvc:message-converters>
            <!-- All converters in specific order here -->
        </mvc:message-converters>
    </mvc:annotation-driven>
    

    这样做的缺点是,如果默认的HttpMessageConverter 链在以后的版本中发生变化,它不会因您的配置而改变。

    另一种方法是在MappingJackson2HttpMessageConverter 之前以编程方式插入您自己的HttpMessageConverter

    @Configuration
    public class MyConfiguration {
    
        @Autowired
        private RequestMappingHandlerAdapter adapter;
    
        @Autowired
        private MyHttpMessageConverter myHttpMessageConverter;
    
        @PostConstruct
        private void modify() {
            List<HttpMessageConverter<?>> messageConverters = adapter.getMessageConverters();
            int insertLocation = messageConverters.size() - 1;
            for (int i = 0; i < messageConverters.size(); i++) {
                Object messageConverter = messageConverters.get(i);
                if (messageConverter instanceof MappingJackson2HttpMessageConverter) {
                    insertLocation = i;
                }
            }
            messageConverters.add(insertLocation, myHttpMessageConverter);
        }
    }
    

    第二种选择将继续使用“默认配置”,即使它在以后的版本中发生更改。我认为它有点hacky,一点也不优雅,但我认为它是一个有效的解决方案的原因是MappingJackson2HttpMessageConverter 声称能够转换为它无法转换的类型似乎存在缺陷。而且你不能明确地将HttpMessageConverter 添加到链中的特定位置。

    现在我选择第二个选项,但如何做取决于你...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-04-04
      • 1970-01-01
      • 1970-01-01
      • 2014-10-28
      • 2013-01-11
      • 2013-06-17
      • 1970-01-01
      相关资源
      最近更新 更多