Java SpringMVC:如何在JSPEL中显示格式化的日期值

Java SpringMVC:如何在JSPEL中显示格式化的日期值,java,spring,jsp,spring-mvc,el,Java,Spring,Jsp,Spring Mvc,El,下面是一个简单的值bean,它用Spring新的(从3.0开始)便利性@DateTimeFormat注释进行了注释(据我所知,它取代了3.0以前对自定义属性编辑器的需求,如所示): 当将表单提交的值提交到此小部件时,日期格式可以完美地工作。也就是说,只有MM/dd/yyyy格式的日期字符串才能成功转换为实际的LocalDate对象。太好了,我们已经走到一半了 但是,我也希望能够使用JSP EL以相同的MM/dd/yyyy格式在JSP视图中显示创建的LocalDate属性(假设我的spring控制

下面是一个简单的值bean,它用Spring新的(从3.0开始)便利性
@DateTimeFormat
注释进行了注释(据我所知,它取代了3.0以前对自定义
属性编辑器的需求,如所示):

当将表单提交的值提交到此小部件时,日期格式可以完美地工作。也就是说,只有
MM/dd/yyyy
格式的日期字符串才能成功转换为实际的
LocalDate
对象。太好了,我们已经走到一半了

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

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

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

有趣的是,当使用自定义的
PropertyEditor
格式化日期时,此标记不会调用
PropertyEditor
getAsText
方法,因此默认为
DateFormat.SHORT
as。无论如何,我仍然想知道是否有一种方法可以实现日期格式化,而不必使用标记——只使用标准JSP EL。

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

创建具有以下签名的新getter:

public String getCreatedDateDisplay
(如果愿意,可以更改getter的名称。)

在getter中,使用SimpleDataFormat等格式化程序根据需要格式化
创建的
日期属性

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

${widget.createDateDisplay}

您可以使用标记为您提供这些类型的格式,例如金钱、数据、时间和许多其他格式

您可以在JSP上添加参考:

并将格式设置为:

以下为参考资料:


爱德华多的准确回答:

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

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

我沮丧地得知:

就我们的开发重点而言,JSP和JSF都不再占据强势地位

但是从引用的JIRA票据中得到启发,我创建了一个定制,如果解析的值是
java.time.LocalDate
java.time.LocalDateTime
,则将尝试提取
@DateTimeFormat
模式值,以便格式化返回的
字符串

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

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


此外,如果jsp需要
LocalDate
LocalDateTime
对象(而不仅仅是格式化的字符串表示),您仍然可以使用直接方法调用来访问对象本身,如:
${widget.getCreated()}

在移交给视图之前,考虑在控制器中格式化日期?这是一个有效的替代方案。谢谢你的回答。但你是对的——我真的希望我可以使用Spring的
@DateTimeFormat
,在视图中声明性地获取我需要的内容,而不必在bean中(强制地)完成工作。但是,我们在同一个页面上,因为
${widget.createDateDisplay}
的最终结果非常好(特别是在处理较新的
java.time.LocalDate
时,它与JSTL的fmt taglib相比没有那么好),我希望能够不使用JSTL的
fmt:formatDate
标记直接输出值。(在任何情况下,当使用
LocalDate
date对象时,此标记都不起作用。)为什么不使用JSTL?在您的解决方案中,
taglib不适用于
java.time.LocalDate
,是否需要isGetValueInProgress,如果需要,为什么?不管怎样,在旧版本中找到了一条注释,让我找到了这个目的<代码>//此冲突解决程序位于原始冲突解决程序链中。要防止//无限递归,请设置一个标志,以防止此冲突解决程序在//链中轮到它时//再次调用原始冲突解决程序链。
public String getCreatedDateDisplay
${widget.createDateDisplay}
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<fmt:formatDate pattern="MM/dd/yyyy" value="${widget.created}" />
    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;
      }
    }
<listener>
  <listener-class>com.company.el.DateTimeFormatAwareElResolver</listener-class>
</listener>