Java SpringMVC:如何在JSPEL中显示格式化的日期值
下面是一个简单的值bean,它用Spring新的(从3.0开始)便利性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控制
@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>