Spring MVC:如何在JSP EL中显示格式化的日期值

时间:2014-12-17 00:42:20

标签: java spring jsp spring-mvc el

这里是一个简单的值bean,用Spring的新版本(3.0版本)方便@DateTimeFormat注释注释(据我所知,它取代了自定义PropertyEditor的3.0之前的需求根据{{​​3}}):

import java.time.LocalDate;
import org.springframework.format.annotation.DateTimeFormat;

public class Widget {
  private String name;

  @DateTimeFormat(pattern = "MM/dd/yyyy")
  private LocalDate created;

  // getters/setters excluded
}

将表单提交中的值与此窗口小部件结合使用时,日期格式可以正常运行。也就是说,只有MM/dd/yyyy格式的日期字符串才能成功转换为实际的LocalDate个对象。好的,我们在那里。

但是,我还希望能够使用JSP EL以相同的LocalDate格式在JSP视图中显示创建的MM/dd/yyyy属性(假设我的spring控制器添​​加了一个widget属性)到模型):

${widget.created}

不幸的是,这只会显示toString的默认LocalDate格式(格式为yyyy-MM-dd)。我知道如果我使用spring的表单标签,日期会根据需要显示:

<form:form commandName="widget">
  Widget created: <form:input path="created"/>
</form:form>

但我想在不使用spring form标签的情况下显示格式化的日期字符串。甚至是JSTL的fmt:formatDate标签。

来自Struts2,HttpServletRequest被包装在一个this SO question中,它启用了这样的EL表达式来实际查询OGNL值堆栈。所以我想知道spring是否提供类似的东西以允许转换器执行?

修改

我也意识到,当使用spring eval标记时,日期将根据@DateTimeFormat注释中定义的模式显示:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<spring:eval expression="widget.created"/>

有趣的是,当使用自定义PropertyEditor格式化日期时,此标记不会调用PropertyEditor的{​​{1}}方法,因此默认为{{1} } StrutsRequestWrapper。无论如何,我仍然想知道是否有办法实现日期格式化而不必使用标记 - 仅使用标准JSP EL。

4 个答案:

答案 0 :(得分:8)

您可以使用该标记为您提供这些格式,例如金钱,数据,时间等等。

您可以在JSP上添加引用: <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

并使用格式为: <fmt:formatDate pattern="yyyy-MM-dd" value="${now}" />

下面是参考:

http://www.tutorialspoint.com/jsp/jstl_format_formatdate_tag.htm

答案 1 :(得分:4)

准确地说Eduardo回答:

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<fmt:formatDate pattern="MM/dd/yyyy" value="${widget.created}" />

答案 2 :(得分:2)

我很高兴得知spring developers have decided not to integrate Unified EL (the expression language used in JSP 2.1+) with Spring EL陈述:

  

JSP和JSF在我们的发展重点方面都不具备强大的地位。

但是从引用的JIRA门票中获取灵感,我创建了一个自定义ELResolver,如果已解析的值为java.time.LocalDatejava.time.LocalDateTime,则会尝试提取@DateTimeFormat模式值,以格式化返回的String值。

这里是ELResolver(以及用于引导它的ServletContextListener):

    public class DateTimeFormatAwareElResolver extends ELResolver implements ServletContextListener {
      private final ThreadLocal<Boolean> isGetValueInProgress = new ThreadLocal<>();

      @Override
      public void contextInitialized(ServletContextEvent event) {
        JspFactory.getDefaultFactory().getJspApplicationContext(event.getServletContext()).addELResolver(this);
      }

      @Override
      public void contextDestroyed(ServletContextEvent sce) {}

      @Override
      public Object getValue(ELContext context, Object base, Object property) {
        try {
          if (Boolean.TRUE.equals(isGetValueInProgress.get())) {
            return null;
          }

          isGetValueInProgress.set(Boolean.TRUE);
          Object value = context.getELResolver().getValue(context, base, property);
          if (value != null && isFormattableDate(value)) {
            String pattern = getDateTimeFormatPatternOrNull(base, property.toString());
            if (pattern != null) {
              return format(value, DateTimeFormatter.ofPattern(pattern));
            }
          }
          return value;
        }
        finally {
          isGetValueInProgress.remove();
        }
      }

      private boolean isFormattableDate(Object value) {
        return value instanceof LocalDate || value instanceof LocalDateTime;
      }

      private String format(Object localDateOrLocalDateTime, DateTimeFormatter formatter) {
        if (localDateOrLocalDateTime instanceof LocalDate) {
          return ((LocalDate)localDateOrLocalDateTime).format(formatter);
        }
        return ((LocalDateTime)localDateOrLocalDateTime).format(formatter);
      }

      private String getDateTimeFormatPatternOrNull(Object base, String property) {
        DateTimeFormat dateTimeFormat = getDateTimeFormatAnnotation(base, property);
        if (dateTimeFormat != null) {
          return dateTimeFormat.pattern();
        }

        return null;
      }

      private DateTimeFormat getDateTimeFormatAnnotation(Object base, String property) {
        DateTimeFormat dtf = getDateTimeFormatFieldAnnotation(base, property);
        return dtf != null ? dtf : getDateTimeFormatMethodAnnotation(base, property);
      }

      private DateTimeFormat getDateTimeFormatFieldAnnotation(Object base, String property) {
        try {
          if (base != null && property != null) {
            Field field = base.getClass().getDeclaredField(property);
            return field.getAnnotation(DateTimeFormat.class);
          }
        }
        catch (NoSuchFieldException | SecurityException ignore) {
        }
        return null;
      }

      private DateTimeFormat getDateTimeFormatMethodAnnotation(Object base, String property) {
        try {
          if (base != null && property != null) {
            Method method = base.getClass().getMethod("get" + StringUtils.capitalize(property));
            return method.getAnnotation(DateTimeFormat.class);
          }
        }
        catch (NoSuchMethodException ignore) {
        }
        return null;
      }

      @Override
      public Class<?> getType(ELContext context, Object base, Object property) {
        return null;
      }

      @Override
      public void setValue(ELContext context, Object base, Object property, Object value) {
      }

      @Override
      public boolean isReadOnly(ELContext context, Object base, Object property) {
        return true;
      }

      @Override
      public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
        return null;
      }

      @Override
      public Class<?> getCommonPropertyType(ELContext context, Object base) {
        return null;
      }
    }

在web.xml中注册ELResolver

<listener>
  <listener-class>com.company.el.DateTimeFormatAwareElResolver</listener-class>
</listener>

现在当我的jsp中有${widget.created}时,显示的值将根据@DateTimeFormat注释进行格式化!

此外,如果jsp需要LocalDateLocalDateTime对象(而不仅仅是格式化的String表示),您仍然可以使用直接方法调用来访问对象本身,如:{{1 }}

答案 3 :(得分:1)

我也不想通过标签进行任何格式化。我意识到这可能不是您正在寻找的解决方案,并且正在寻找通过spring注释实现此目的的方法。然而,在过去,我使用了以下工作:

使用以下签名创建一个新的getter:

public String getCreatedDateDisplay

(如果您愿意,可以更改吸气剂的名称。)

在getter中,使用SimpleDateFormat等格式化程序根据需要格式化created日期属性。

然后您可以从JSP中调用以下内容

${widget.createDateDisplay}