Apache FreeMarker – это шаблонизатор для генерации любого вида текстового вывода от HTML до электронной почты и др. Spring Framework имеет встроенную интеграцию для использования Spring MVC с шаблонами FreeMarker.

Конфигурация представления

В следующем примере показано, как настроить FreeMarker в качестве технологии представления:

Java
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.freeMarker();
    }
    // Конфигурируем FreeMarker...
    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer() {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
        return configurer;
    }
}
Kotlin
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
    override fun configureViewResolvers(registry: ViewResolverRegistry) {
        registry.freeMarker()
    }
    // Конфигурируем FreeMarker...
    @Bean
    fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
        setTemplateLoaderPath("/WEB-INF/freemarker")
    }
}

В следующем примере показано, как сконфигурировать то же самое на XML:

<mvc:annotation-driven/>
<mvc:view-resolvers>
    <mvc:freemarker/>
</mvc:view-resolvers>
<!-- Конфигурируем FreeMarker... -->
<mvc:freemarker-configurer>
    <mvc:template-loader-path location="/WEB-INF/freemarker"/>
</mvc:freemarker-configurer>

Кроме того, можно объявить бин FreeMarkerConfigurer для полного контроля над всеми свойствами, как показано в следующем примере:

<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
</bean>

Ваши шаблоны должны храниться в директории, указанной FreeMarkerConfigurer, как показано в предыдущем примере. Учитывая предыдущую конфигурацию, если ваш контроллер возвращает имя представления welcome, преобразователь ищет шаблон /WEB-INF/freemarker/welcome.ftl.

Конфигурация FreeMarker

Можно передать "Settings" и "SharedVariable" обработчика FreeMarker непосредственно в объект Конфигурация обработчика FreeMarker (который управляется Spring), установив соответствующие свойства бина вFreeMarkerConfigurer. Для свойства freemarkerSettings требуется объект java.util.Properties, а для свойства freemarkerVariables - java.util.Map. В следующем примере показано, как использовать FreeMarkerConfigurer:

<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
    <property name="freemarkerVariables">
        <map>
            <entry key="xml_escape" value-ref="fmXmlEscape"/>
        </map>
    </property>
</bean>
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>

Подробности о настройках и переменных, применимых к объекту Конфигурация, см. в документации по FreeMarker.

Обработка форм

Spring предусматривает библиотеку тегов для использования в JSP, которая содержит, среди прочего, элемент <spring:bind/>. Этот элемент в первую очередь позволяет формам выводить на экран значения из базовых объектов формы и отображать результаты неудачных проверок из Validator на веб-уровне или бизнес-уровне. Spring также имеет поддержку той же функциональности во FreeMarker, с дополнительными удобными макросами для генерации самих элементов ввода формы.

Макросы привязки

Средства поддержки стандартного набора макросов находятся в файле spring-webmvc.jar для FreeMarker, поэтому они всегда доступны для соответствующим образом настроенного приложения.

Некоторые макросы, определенные в библиотеках шаблонизации в Spring, считаются внутренними (приватными), но в определениях макросов такого ограничения нет, что делает все макросы видимыми для вызывающего кода и пользовательских шаблонов. Следующие разделы посвящены только макросам, которые необходимо вызывать непосредственно из шаблонов. Если вам нужно просмотреть код макроса напрямую, файл называется spring.ftl и находится в пакете org.springframework.web.servlet.view.freemarker.

Простое привязывание

В ваших HTML-формах на базе шаблонов FreeMarker, которые действуют как представление формы для контроллера Spring MVC, можно использовать код, подобный тому, что представлен в следующем примере, для привязки к значениям полей и вывода на экран сообщений об ошибках для каждого поля ввода аналогично эквиваленту JSP. В следующем примере показано представление personForm:

<!-- Макросы FreeMarker должны быть импортированы в пространство имен.
    Мы настоятельно рекомендуем придерживаться параметра "spring". -->
<#import "/spring.ftl" as spring/>
<html>
    ...
    <form action="" method="POST">
        Name:
        <@spring.bind "personForm.name"/>
        <input type="text"
            name="${spring.status.expression}"
            value="${spring.status.value?html}"/><br />
        <#list spring.status.errorMessages as error> <b>${error}</b> <br /> </#list>
        <br />
        ...
        <input type="submit" value="submit"/>
    </form>
    ...
</html>

<@spring.bind> требует аргумент "path", который состоит из имени вашего объекта команды (это "command", если вы не изменили его в конфигурации контроллера), за которым следует точка и имя поля объекта команды, к которому вы хотите осуществить привязку. Также можно использовать вложенные поля, например command.address.street. Макрос bind предусматривает логику HTML-экранирования по умолчанию, заданную параметром defaultHtmlEscape контекста ServletContext в web.xml.

Альтернативная форма макроса <@spring.bindEscaped> принимает второй аргумент, который явно указывает, следует ли использовать HTML-экранироваание в сообщениях об ошибке определения статуса или в значениях. Можно установить значение в true или false в зависимости от необходимости. Дополнительные макросы для работы с формами упрощают использование HTML-экранирования, и вам стоит использовать эти макросы везде, где это возможно. Они описаны в следующем разделе.

Макросы ввода

Дополнительные вспомогательные макросы для FreeMarker упрощают привязку и генерацию форм (включая вывод на экран ошибок валидации). Нет необходимости использовать эти макросы для создания полей ввода формы, их можно смешивать и сочетать с помощью простого HTML или прямых вызовов макросов привязки Spring, о которых мы рассказывали ранее.

В следующей таблице доступных макросов приведены определения шаблона FreeMarker Template (FTL) и список параметров, которые принимает каждый из них:

Таблица 6. Таблица макроопределений
макрос Определение FTL

message (выводит строку из ресурсного локализационного пакета на основе параметра кода)

<@spring.message code/>

messageText (выводит строку из ресурсного локализационного пакета на основе параметра кода, возвращаясь к значению параметра по умолчанию)

<@spring.messageText code, text/>

url (префикс относительного URL-адреса с контекстным корнем приложения)

<@spring.url relativeUrl/>

formInput (стандартное поле ввода для сбора вводимой пользовательской информации)

<@spring.formInput path, attributes, fieldType/>

formHiddenInput (скрытое поле ввода для передачи информации, которую вводит не пользователь)

<@spring.formHiddenInput path, attributes/>

formPasswordInput (стандартное поле ввода для сбора паролей. Обратите внимание, что в полях этого типа никогда не заполняется никакое значение).

<@spring.formPasswordInput path, attributes/>

formTextarea (большое текстовое поле для сбора длинного текста произвольной формы)

<@spring.formTextarea path, attributes/>

formSingleSelect (выпадающее окно опций, позволяющее выбрать одно требуемое значение)

<@spring.formSingleSelect path, options, attributes/>

formMultiSelect (поле со списком опций, позволяющее пользователю выбрать 0 или более значений)

<@spring.formMultiSelect path, options, attributes/>

formRadioButtons (набор переключателей (радиокнопок), позволяющих сделать один выбор из имеющихся вариантов)

<@spring.formRadioButtons path, options separator, attributes/>

formCheckboxes (набор флажков, позволяющих выбрать 0 или более значений)

<@spring.formCheckboxes path, options, separator, attributes/>

formCheckbox (один флажок)

<@spring.formCheckbox path, attributes/>

showErrors (упрощает вывод на экран ошибок валидации для связанного поля)

<@spring.showErrors separator, classOrStyle/>

В шаблонах FreeMarker в formHiddenInput и formPasswordInput фактически нет нужды, так как вы можете использовать обычный макрос formInput, задав hidden или password в качестве значения параметра fieldType.

Параметры любого из вышеперечисленных макросов имеют понятный смысл:

  • path: имя поля для привязки (т.е. "command.name")

  • options: Map всех доступных значений, которые можно выбрать в поле ввода. Ключи Map представляют собой значения, которые передаются обратно из формы и привязываются к объекту команды. Объекты Map, хранящиеся наряду с ключами, являются метками, отображаемыми в форме для пользователя, и могут отличаться от соответствующих значений, возвращаемых формой. Обычно такая Map предоставляется контроллером в качестве справочных данных. Вы можете использовать любую реализацию Map в зависимости от требуемой логики работы. Для строго отсортированных Map можно использовать SortedMap (например, TreeMap) с подходящим Comparator, а для произвольных Map, которые должны возвращать значения в порядке вставки, используйте LinkedHashMap или LinkedMap из commons-collections.

  • separator: если несколько опций доступны в виде отдельных элементов (переключатели или флажки), то этот параметр представляет последовательность символов, используемых для разделения каждого из них в списке (например, <br>).

  • attributes: дополнительная строка произвольных тегов или текста для включения в сам HTML-тег. Эта строка побуквенно повторяется макросом. Например, в поле textarea можно передать атрибуты (например, 'rows="5" cols="60"') или информацию о стиле, например, 'style="border:1px solid silver"'.

  • classOrStyle: для макроса showErrors – имя класса CSS, который используется в элементе span, обертывающем каждую ошибку. Если информация не предоставлена (или значение пустое), ошибки оборачиваются в теги <b></b>.

В следующих разделах приведены примеры макросов.

Поля ввода

Макрос formInput принимает параметр path(command.name) и дополнительный параметр attributes (который в следующем примере пуст). Этот макрос, как и все другие макросы генерации форм, выполняет неявную привязку Spring к параметру "path". Привязка остается действительной до тех пор, пока не будет осуществлена новая привязка, поэтому макросу showErrors не нужно снова передавать параметр "пути" – он работает с полем, для которого привязка была создана в последний раз.

Макрос showErrors принимает параметр-разделитель (символы, которые используются для разделения нескольких ошибок в данном поле), а также принимает второй параметр – на этот раз имя класса или атрибут стиля. Обратите внимание, что FreeMarker может задавать значения по умолчанию для параметра "attributes". В следующем примере показано, как использовать макросы formInput и showErrors:

<@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/>

В следующем примере показан вывод фрагмента формы с генерацией поля имени и выводом ошибки валидации после того, как форма была отправлена без значения в поле. Валидация происходит через фреймворк Validation в Spring.

Создаваемый HTML похож на следующий пример:

Name:
<input type="text" name="name" value="">
<br>
    <b>required</b>
<br>
<br>

Макрос formTextarea работает так же, как и макрос formInput, и принимает тот же список параметров. Обычно второй параметр (attributes) используется для передачи информации о стиле или атрибутов rows и cols для textarea.

Поля выбора

Вы можете использовать четыре макроса для полей выбора, чтобы генерировать обычные входные данные выбора значений пользовательского интерфейса в ваших HTML-формах:

  • formSingleSelect

  • formMultiSelect

  • formRadioButtons

  • formCheckboxes

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

Следующий пример касается переключателей в FTL. Базовый объект формы задает для этого поля значение по умолчанию "London", поэтому валидация не требуется. Когда форма отображается, весь список городов для выбора предоставляется в качестве справочных данных в модели под именем "cityMap". В следующем листинге показан пример:

...
Town:
<@spring.formRadioButtons "command.address.town", cityMap, ""/><br><br>

В предыдущем листинге отображается строка переключателей, по одному для каждого значения в cityMap, и используется разделитель "". Никаких дополнительных атрибутов не присваивается (последний параметр макроса отсутствует). cityMap использует одну и ту же String для каждой пары ключ-значение в Map. Ключи Map – это то, что форма фактически отправляет в качестве параметров POST-запроса. Значения Map – это метки, которые видит пользователь. В предыдущем примере, учитывая список из трех известных городов и значение по умолчанию в базовом объекте формы, HTML выглядит следующим образом:

Town:
<input type="radio" name="address.town" value="London">London</input>
<input type="radio" name="address.town" value="Paris" checked="checked">Paris</input>
<input type="radio" name="address.town" value="New York">New York</input>

Если ваше приложение предполагает работу с городами по внутренним кодам (например), то можно создать Map кодов с соответствующими ключами, как показано в следующем примере:

Java
protected Map<String, ?> referenceData(HttpServletRequest request) throws Exception {
    Map<String, String> cityMap = new LinkedHashMap<>();
    cityMap.put("LDN", "London");
    cityMap.put("PRS", "Paris");
    cityMap.put("NYC", "New York");
    Map<String, Object> model = new HashMap<>();
    model.put("cityMap", cityMap);
    return model;
}
Kotlin
protected fun referenceData(request: HttpServletRequest): Map<String, *> {
    val cityMap = linkedMapOf(
            "LDN" to "London",
            "PRS" to "Paris",
            "NYC" to "New York"
    )
    return hashMapOf("cityMap" to cityMap)
}

Теперь код выводит данные, где значениями переключателя являются соответствующие коды, но пользователь по-прежнему видит более удобные названия городов, как показано ниже:

Town:
<input type="radio" name="address.town" value="LDN">London</input>
<input type="radio" name="address.town" value="PRS" checked="checked">Paris</input>
<input type="radio" name="address.town" value="NYC">New York</input>

HTML-экранирование

Использование по умолчанию макросов формы, описанных ранее, приводит к созданию HTML-элементов, которые соответствуют стандарту HTML 4.01 и используют значение по умолчанию для HTML-экранирования, определенное в вашем файле web.xml, которое используется средствами поддержки привязки Spring. Чтобы сделать элементы совместимыми с XHTML или переопределить значение HTML-экранирования по умолчанию, можно задать две переменные в вашем шаблоне (или в вашей модели, где их видят ваши шаблоны). Преимущество задания их в шаблонах заключается в том, что их можно изменить на другие значения позже в процессе обработки шаблона, чтобы обеспечить различную логику работы для разных полей вашей формы.

Чтобы переключиться в режим совместимости с XHTML для ваших тегов, задайте значение true для модели или контекстной переменной с именем xhtmlCompliant, как показано в следующем примере:

<#-- for FreeMarker -->
<#assign xhtmlCompliant = true>

После обработки этой директивы все элементы, созданные макросами Spring, теперь будут совместимы со стандартом XHTML.

Аналогичным образом можно задать HTML-экранирование для каждого поля, как показано в следующем примере:

<#-- until this point, default HTML escaping is used -->
<#assign htmlEscape = true>
<#-- next field will use HTML escaping -->
<@spring.formInput "command.name"/>
<#assign htmlEscape = false in spring>
<#-- all future fields will be bound with HTML-экранирование off -->