【问题标题】:How to do spring request parameter conversionspring请求参数转换怎么做
【发布时间】:2011-07-26 19:43:43
【问题描述】:

在基于 Spring 3 的 web (portlet) 应用程序中,我有一个控制器,其方法如下:

@RenderMapping
public ModelAndView handleRenderRequest(...,@RequestParam MyClass myObject)
{
    ...
}

现在我想知道:我如何告诉spring如何将请求参数转换为MyClass。我找到了关于属性编辑器和 Converter 接口的信息,并且似乎暗示 Converter 是属性编辑器的继承者,但似乎没有人喜欢明确说明它。

我实现了 String 到 MyClass 转换的转换器接口。但是我该如何告诉 Spring 呢?我尽可能使用基于注释的配置,所以我检查了 spring 是否会自动从我的类路径中检测到 Converter,但它没有。

所以认为手册中的 Configuring a ConversionService 部分想告诉我,我必须将以下内容添加到我的 applicationContext.xml 中:

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="some.package.MyConverter"/>
        </list>
    </property>
</bean>

有点静止:

org.springframework.beans.ConversionNotSupportedException: 失败 转换价值 [...]

那么我错过了什么?有没有办法,只配置一个包,让 spring 扫描这个包中的转换器并自动注册它们?并说在某种方法中我想使用与所有其他方法不同的转换器。例如,我想检查一个具有 Luhn-Checksum 的整数并删除校验和,我该怎么做? @RequestParam(converter=some.package.MyConverter.class) 之类的东西会很棒。

编辑

好的,我刚刚在文档中发现:

在客户端环境中工作时使用 Formatter SPI, 比如web应用,需要解析和打印本地化字段 价值观

所以我想这意味着我应该使用 Formatter SPI,这是属性编辑器和转换器旁边的第三种可能性(我认为我真的可以使用比较表或类似的东西)。我也实现了 Parser 接口,并尝试使用以下方法注册我的转换器:

<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="some.package.SortOrderEnumConverterSpring"/>
        </set>
    </property>
</bean> 

如您所见,我使用“set”而不是“list”来指定转换器。我在 FormattingConversionServiceFactoryBean.setConverters 方法中设置了一个调试断点,该方法在使用 list 时不会触发,但在使用 set 时会触发。

另外我加了

<mvc:annotation-driven conversion-service="conversionService"/>

以及我的 applicationContext 的 mvc 前缀的命名空间。但我仍然得到不支持转换的异常。

我还尝试回到转换器方法,并在我的 applicationContext.xml 文件中将转换器的参数列表从列表更改为设置,但这也没有改变任何内容。

编辑2

正如 digitaljoel 指出的,可以使用 initBinder 方法为每个控制器设置不同的转换器。我将此应用于我的控制器:

@Autowired
private ConversionService conversionService;

@InitBinder
public void initBinder(WebDataBinder binder)
{
    binder.setConversionService(conversionService);
}

在我的 applicationContext.xml 中:

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="some.package.with.MyConverter"/>
        </set>
    </property>
</bean>

突然之间,转换工作正常:-)。但是我对必须将其应用于我的每个控制器并不十分满意。必须有一种方法可以在我的 applicationContext 中为每个人设置它,不是吗?很高兴知道我可以在需要时覆盖默认值(毕竟我要求这样做),但我仍然想设置默认值。

那么格式化程序的东西呢。我不应该使用它而不是 Converter 吗?

【问题讨论】:

    标签: spring spring-mvc


    【解决方案1】:

    Spring Portlet MVC 3.0 不支持

    <mvc:annotation-driven conversion-service="conversionService"/>
    

    访问https://jira.springsource.org/browse/SPR-6817了解更多信息。

    但是您可以将其添加到您的通用应用程序上下文中

    <bean
        class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="webBindingInitializer">
            <bean
                class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
                <property name="conversionService">
                    <list>
                        <ref bean="conversionService" />
                    </list>
                </property>
            </bean>
        </property>
    </bean>
    

    这样你就不需要为每个控制器添加@InitBinder

    当然

    <bean id="conversionService"
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <!-- converter implementations here -->
            </list>
        </property>
    </bean>
    

    【讨论】:

      【解决方案2】:

      您是正确的,Converter(和 ConverterFactory)是属性编辑器的继承者。您的问题可能是您不接受适当的类型作为转换器的参数,但是如果没有看到转换器代码,这很难说。如果您期待 Long 或 Integer,您可能实际上是从 Spring 获得一个 String 并且需要先自己执行该键转换。

      至于配置,我相信您需要在 xml.xml 中的 bean 配置中列出所有转换器。如果您使用 @Component 注释您的转换器实现,您可能可以通过 bean 名称而不是完全限定路径来引用它,但我只尝试过 ConverterFactory,而不是 Converter。

      最后,在特定转换器上,您似乎可以在控制器级别配置转换服务(请参阅 Javi 在 Setting up a mixed configuration for annotation-based Spring MVC controllers 上的回答),然后您可以放置​​该方法(以及其他需要该控制器的方法)到使用辅助转换服务的控制器中,您应该能够使用 @Resource 注释按名称注入。

      【讨论】:

      • 有价值的提示让我更进一步。基于您的答案的新发现现在在我上面的问题中......
      • 格式转换服务确实结合了两个独立的关注点。一种用于格式化(例如日期格式),一种用于转换。我不确定为什么它们会结合在一起。其次,看起来 mvc 命名空间在 portlet 中不起作用,只能在 servlet 中起作用。请参阅回复jira.springsource.org/browse/SPR-6817 的 cmets,这将解释为什么您必须指定 @initbinder 并且 mvc:annotation-drive 没有为您做任何事情。
      • 好的,这就是我在 applicationContext.xml 中需要的东西来让事情正常工作:pastebin.com/P7DapZyz。不幸的是,如果我这样做了,那么我就不能再使用 autowire/initBinder 了,它会抛出一个 IllegalStateException,因为它已经配置好了。但最好在此处进行删减并对此提出单独的关注/问题...我建议您编辑您的问题,包括 XML,我会接受它。
      • 由于我不确定 xml 在做什么,我不打算将它添加到我的答案中。也许您应该编辑您的问题并附加它,或者使用 xml 回答您自己的问题,并解释正在发生的事情以及为什么它不再允许 autowire/initBinder 工作。很高兴我的回答至少可以帮助你重新振作起来。
      【解决方案3】:

      实现一个 WebArgumentResolver:

      public class MyArgumentResolver implements WebArgumentResolver
      {
          @Override
          public Object resolveArgument(MethodParameter methodParameter,
                  NativeWebRequest webRequest) throws Exception
          {
              Class<?> paramType = methodParameter.getParameterType();
              if (paramType == MyClass.class)
              {
                  String parameterName = methodParameter.getParameterName();
                  String stringParameter = webRequest.getParameter(parameterName);
                  return convert(stringParameter);
              }
              return UNRESOLVED;
          }
      }
      

      并在你的 applicationContext.xml 中注册它:

      <bean class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter">
          <property name="customArgumentResolver">
              <bean class="com.dshs.eakte.util.MyArgumentResolver" />
          </property>
      </bean>
      

      这很有效,甚至具有允许基于多个方法参数的参数转换的优点。

      【讨论】:

      • 好的,这是一种可能,不幸的是,它不像全局定义一个转换器那么简单,因为它适用于某些内置的数据类型(数字,...)并使用它,因为它看起来就像我只能指定其中一个 WebArgumentResolvers
      • 可能@ModelAttribute 选项比上面的更容易。
      【解决方案4】:

      为了实现与您正在做的类似的事情,我发现this blog entry 很有用。

      【讨论】:

      • 你应该在这里发布答案,以防万一有一天链接离线。
      【解决方案5】:

      我认为你需要使用类似的东西

      public ModelAndView handleRenderRequest(...,@ModelAttribute("myObject") MyClass myObject)
      

      【讨论】:

      • 我曾经想要这样:stackoverflow.com/questions/8818744/…。但是这个问题有点不同
      • 不同之处在于@ModelAttribute 针对的是组合对象。但我想要一个类似于 URL http://.../...?date=1970-1-1 的东西,它映射到 renderMethod handleRenderRequest(MyOwnDateClass data)。 (此处使用“日期”只是为了简单说明,实际上我是在转换不同的类型)
      猜你喜欢
      • 1970-01-01
      • 2019-03-04
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      • 2016-08-09
      • 1970-01-01
      相关资源
      最近更新 更多