2014-12-17 2 views
14

Вот простой компонент компонента, аннотированный с помощью новой (начиная с версии 3.0) функции Spring. @DateTimeFormat аннотация (что, как я понимаю, заменяет потребность в pre-3.0 для пользовательских PropertyEditor s по this SO question):Spring MVC: как отображать отформатированные значения даты в JSP EL

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 объектов. Отлично, мы на полпути.

Однако, я также хотел бы также иметь возможность отобразить созданный LocalDate свойство в представлении JSP в том же формате MM/dd/yyyy с использованием JSP EL как так (при условии, моя весна контроллер добавлен атрибут виджета к модели):

${widget.created} 

К сожалению, это будет отображаться только по умолчанию toString формат LocalDate (в формате yyyy-MM-dd). Я понимаю, что форма, если я использую Спринг помечает дата отображается по желанию:

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

Но я хотел бы, чтобы просто отобразить отформатированную строку даты без использования пружинного теги формы. Или даже тег JSTL fmt:formatDate.

Начиная с Struts2, HttpServletRequest был обернут в StrutsRequestWrapper, который позволил выражениям EL, как это, фактически опросить стек значений OGNL. Поэтому мне интересно, добавила ли пружина что-то похожее на это, чтобы позволить конвертерам исполнять?

EDIT

Я также понимаю, что при использовании eval тега весны дата будет отображаться в соответствии шаблон, определенный в @DateTimeFormat аннотации:

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

Интересно, что при использовании пользовательских PropertyEditor для форматирования date, этот тег НЕ ПРИНИМАЕТ этот метод и поэтому по умолчанию равен DateFormat.SHORT как described in the docs. В любом случае, мне все равно хотелось бы знать, есть ли способ добиться форматирования даты без использования тега - только с использованием стандартного JSP EL.

+0

рассмотреть форматирование даты в контроллере перед передачей на просмотр? – Septem

ответ

0

я был подавленным, чтобы узнать, что 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.LocalDate или java.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; 
     } 
    } 

Зарегистрируйте ELResolver в web.xml:

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

И теперь, когда у меня есть ${widget.created} в моем JSP, отображаемое значение будет отформатировано в соответствии с аннотацией @DateTimeFormat!

Кроме того, если LocalDate или LocalDateTime объект необходим СПЯ (а не только форматированный представление String), вы можете получить доступ к самому объекту с помощью прямого вызова метода, как: ${widget.getCreated()}

+0

В вашем решении требуется ** isGetValueInProgress **, и если да, то почему? – Pawel

+0

nevermind, нашел комментарий в старом [github repo] (https://github.com/pukkaone/webappenhance/blob/master/src/main/java/com/github/pukkaone/jsp/EscapeXmlELResolver.java), который привел меня к цели. '// Этот резольвер находится в исходной цепочке распознавателей. Чтобы предотвратить // бесконечную рекурсию, установите флаг, чтобы предотвратить этот преобразователь от // повторное включение исходной цепи распознавателя, когда происходит его поворот в цепочке //. – Pawel

1

Я также предпочитаю не форматировать с помощью тегов. Я понимаю, что это не может быть решением, которое вы ищете, и ищете способ сделать это с помощью весенних аннотаций. Тем не менее, в прошлом я использовал следующие работы вокруг:

Создать новый поглотитель со следующей подписью: (. Вы можете изменить имя получателя, если вы предпочитаете)

public String getCreatedDateDisplay 

Внутри получателя форматируйте атрибут даты created по желанию с помощью форматирования, такого как SimpleDateFormat.

Тогда вы можете вызвать следующие из вашей JSP

${widget.createDateDisplay} 
+0

Это действительная альтернатива. Спасибо за ответ. Но вы правы - я действительно надеюсь, что могу использовать '@ DateTimeFormat Spring 'и декларативно получить то, что мне нужно в представлении, а не выполнять работу (обязательно) в компоненте. Тем не менее, мы находимся на одной странице, потому что конечный результат '$ {widget.createDateDisplay}' очень хорош (особенно при работе с более новым 'java.time.LocalDate', который не играет так же хорошо с fmt taglib JSTL). –

7

Вы можете использовать тег, чтобы предоставить вам эти виды formattings, такие как деньги, данные, время, и многие другие.

Вы можете добавить на вас 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

'<% @ taglib prefix =" fmt "uri =" http://java.sun.com/jsp/jstl/fmt "%>' taglib не работает с 'java.time.LocalDate' –

4

точным Eduardo ответ:

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

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

Как я упоминал в этом вопросе я надеялся, что сможет напрямую вывести значение без использования тега 'fmt: formatDate' JSTL. (И в любом случае этот тег не работает при использовании объектов даты «LocalDate».) –

+0

Почему вы не хотите использовать JSTL? –

Смежные вопросы