NumberFormat不适用于ModelAndView和HandlerInterceptorAdapter

时间:2011-12-05 00:43:48

标签: java spring spring-mvc

我创建了一个自定义HandlerInterceptorAdapter来覆盖postHandle方法:

public class AcmeInterceptor extends HandlerInterceptorAdapter {

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);

        AcmeController controller = (AcmeController) handler;

        controller.finalize(modelAndView);
    }
}

在AcmeModel中,我定义了一个用NumberFormat注释的字段:

public class AcmeModel {
    private BigDecimal cost = BigDecimal.valueOf(67890.6789);

    @NumberFormat(style = Style.CURRENCY)
    public BigDecimal getCost() {
        return cost;
    }
}

在acme.jsp中,我使用<spring:bind>输出格式化的值:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<spring:bind path="acmeModel.cost">
    Cost: <c:out value="${status.value}" />
</spring:bind>

现在,首先我尝试这样的控制器:

@Controller
public class AcmeController {

    @RequestMapping("/")
    public ModelAndView index() {
        ModelAndView modelAndView = new ModelAndView("WEB-INF/views/acme.jsp");
        modelAndView.addObject(new AcmeModel());
        return modelAndView;
    }

    public void finalize(ModelAndView modelAndView) {
    }
}

这是我得到的输出:

  

费用:67,890.68美元

这是令人费解的部分。如果我将addObject的呼叫移至finalize

的正文中
@Controller
public class AcmeController {

    @RequestMapping("/")
    public ModelAndView index() {
        ModelAndView modelAndView = new ModelAndView("WEB-INF/views/acme.jsp");
        //modelAndView.addObject(new AcmeModel());
        return modelAndView;
    }

    public void finalize(ModelAndView modelAndView) {
        modelAndView.addObject(new AcmeModel());
    }
}

然后输出变为:

  

费用:67890.6789

在处理程序方法中将对象添加到ModelAndView而不是影响<spring:bind>的普通控制器方法有什么区别?

编辑:这是servlet的bean定义。

<beans ...>     
    <mvc:annotation-driven />       
    <context:component-scan base-package="com.example" />       
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <bean id="acmeInterceptor" class="com.example.numberformat.AcmeInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

1 个答案:

答案 0 :(得分:1)

Spring通过BindingResult对象处理格式。在控制器中向模型添加值时,spring会对它们进行验证,创建BindingResult并将其添加到模型中。这个机制只适用于控制器,不适用于拦截器,可能是有意或无意我不确定,但绝对是这样的。如果要对拦截器中设置的值进行绑定,则需要进行绑定yourslef。像这样修改你的拦截器:

public class AcmeInterceptor extends HandlerInterceptorAdapter {

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {      

        AcmeController controller = (AcmeController) handler;

        AcmeModel acmeModel = controller.getAcmeModel();
        String key = "acmeModel";

        BeanPropertyBindingResult bpb = new BeanPropertyBindingResult(
                acmeModel, key);

        bpb.initConversion(controller.getBinder().getConversionService());

        modelAndView.addObject(key, acmeModel);
        modelAndView.addObject(BindingResult.MODEL_KEY_PREFIX + key, bpb);

    }

}

正如您所看到的,这段代码将额外的BindingResult对象添加到模型中。它需要掌握ConversionService。现在,它是通过从控制器获取它来完成的。 Controller可以使用像这样的@InitBinder注释来访问它

public class AcmeController {

    private WebDataBinder binder;

    @InitBinder
    protected void initBinder(WebDataBinder binder) {

        this.binder = binder;

    }

另一种选择是在拦截器中实现preHandle方法而不是postHandle。 PreHandle无法访问模型,但它可以访问请求,因此在preHandle中你可以将AcmeModel作为属性添加到reqiest然后在你的控制器中你可以得到值,将它添加到模型中,然后它将是Bound就像Spring的其他模型属性一样。