Оглавление
Для нашего виртуального бакалейного магазина Thymes мы выбрали реализацию ITemplateResolver под названием ServletContextTemplateResolver, которая позволила получить шаблоны в качестве ресурсов из контекста сервлета.
Помимо предоставления возможности создавать собственный шаблонный резольвер, реализуя ITemplateResolver, Thymeleaf включает в себя четыре реализации из коробки:
Все предустановленные реализации ITemplateResolver позволяют использовать тот же набор параметров конфигурации, которые включают:
Пакеты интеграции Thymeleaf + Spring предлагают реализацию SpringResourceTemplateResolver, которая использует всю инфраструктуру Spring для доступа и чтения ресурсов в приложениях и которая является рекомендуемой реализацией в приложениях с поддержкой Spring.
Цепочка resolvers шаблонов
Кроме того, механизм шаблонов может указывать несколько искателей шаблонов, и в этом случае порядок может быть установлен между ними для поиска шаблона, так что, если первый не может найти шаблон, запрашивается второй и т. д.:
Когда применяется несколько шаблонов-резольверов, рекомендуется указывать patterns для каждого распознавателя шаблонов, чтобы Thymeleaf мог быстро отменить эти шаблонные resolvers, которые не предназначены для поиска шаблона, повышая производительность. Это не требование, а рекомендация:
Если эти resolvable шаблоны не указаны, мы будем полагаться на конкретные возможности каждой из созданных нами ITemplateResolver реализаций. Обратите внимание, что не все реализации могут определять существование шаблона перед его resolving и поэтому всегда могут рассматривать шаблон как resolvable и прерывать цепочку поиска (не позволяя другим resolvers проверять один и тот же шаблон).
Все реализации ITemplateResolver, входящие в состав ядра Thymeleaf, включают механизм, который позволит нам заставить resolvers действительно проверить, существует ли ресурс, прежде чем считать его найденным. Это флаг checkExistence, который работает как:
Этот флаг checkExistence заставляет resolver выполнять реальную проверку существования ресурса во время фазы поиска (и пусть следующий поиск осуществится в цепочке, если текущая проверка вернет false). Хотя это может показаться хорошим решением, в большинстве случаев это будет означать двойной доступ к самому ресурсу (один раз для проверки существования, в другое время для его чтения) и может быть проблемой производительности в некоторых сценариях, например, «удаленные» ресурсы шаблонов на основе URL-адресов — потенциальная проблема с производительностью, которая в любом случае может быть в значительной степени смягчена использованием кеша шаблона (в этом случае шаблоны будут искаться только при первом доступе к ним).
Мы явно не указали реализацию Message Resolver для нашего приложения Grocery, и, как было объяснено ранее, это означало, что используемой реализацией был объект org.thymeleaf.messageresolver.StandardMessageResolver.
StandardMessageResolver — это стандартная реализация интерфейса IMessageResolver, но мы могли бы создать наш собственный, если бы захотели, адаптированный к конкретным потребностям нашего приложения.
Пакеты интеграции Thymeleaf + Spring предлагают по умолчанию реализацию IMessageResolver, которая использует стандартный метод Spring для получения внешних сообщений, используя компоненты MessageSource, объявленные в контексте Spring Application Context.
Стандартный Resolver сообщений
Итак, как выглядит StandardMessageResolver сообщений, запрашиваемых по определенному шаблону?
Если имя шаблона «home», и оно находится в /WEB-INF/templates/home.html, а запрошенный язык — gl_ES, тогда этот распознаватель будет искать сообщения в следующих файлах в следующем порядке:
Обратитесь к документации JavaDoc класса StandardMessageResolver для более подробной информации о том, как работает полный механизм поиска сообщений.
Настройка искателя сообщений
Что делать, если мы хотим добавить средство преобразования сообщений (или больше) в шаблонный движок? Это легко:
И почему мы хотим иметь более одного искателя сообщений? По той же причине, что и искатель шаблонов: упорядочиватели сообщений упорядочены, и если первый не может найти конкретное сообщение, будет запрошен второй, затем третий и т. д.
Служба преобразования, которая позволяет нам выполнять операции преобразования и форматирования данных с помощью синтаксиса с двойной скобкой (${{...}}), на самом деле является элементом стандартного диалекта, а не самого механизма шаблонов Thymeleaf.
Таким образом, способ его настройки заключается в настройке пользовательской реализации интерфейса IStandardConversionService непосредственно в экземпляре StandardDialect, который настраивается в механизм шаблонов. Подробнее:
Обратите внимание, что пакеты thymeleaf-spring3 и thymeleaf-spring4 содержат SpringStandardDialect, и этот диалект уже предварительно настроен с внедрением IStandardConversionService, который интегрирует собственную инфраструктуру службы преобразования Spring в Thymeleaf.
Thymeleaf уделяет большое внимание регистрации событий и всегда пытается предоставить максимальный объем полезной информации через свой интерфейс ведения журнала.
Используемая библиотека протоколирования — slf4j, которая на самом деле выступает в качестве моста для любой реализации протоколирования, которую мы могли бы использовать в нашем приложении (например, log4j).
В классах Thymeleaf будут записываться данные TRACE, DEBUG и INFO, в зависимости от уровня детализации, который мы желаем, и, кроме общего ведения журнала, он будет использовать три специальных регистратора, связанных с классом TemplateEngine, которые мы можем настроить отдельно для разных целей:
Пример конфигурации для инфраструктуры регистрации Thymeleaf, использующей log4j, может быть:
15 Подробнее о конфигурации
15.1 Resolver шаблонов
Для нашего виртуального бакалейного магазина Thymes мы выбрали реализацию ITemplateResolver под названием ServletContextTemplateResolver, которая позволила получить шаблоны в качестве ресурсов из контекста сервлета.
Помимо предоставления возможности создавать собственный шаблонный резольвер, реализуя ITemplateResolver, Thymeleaf включает в себя четыре реализации из коробки:
- org.thymeleaf.templateresolver.ClassLoaderTemplateResolver, который находит шаблоны как ресурсы загрузчика классов, например:
return Thread.currentThread().getContextClassLoader().getResourceAsStream(template);
- org.thymeleaf.templateresolver.FileTemplateResolver, который находит шаблоны в виде файлов из файловой системы, например:
return new FileInputStream(new File(template));
- org.thymeleaf.templateresolver.UrlTemplateResolver, который находит шаблоны как URL-адреса (даже нелокальные), например:
return (new URL(template)).openStream();
- org.thymeleaf.templateresolver.StringTemplateResolver, который находит шаблоны напрямую, поскольку String указывается как шаблон (или имя шаблона, которое в этом случае, очевидно, намного больше, чем простое имя):
return new StringReader(templateName);
Все предустановленные реализации ITemplateResolver позволяют использовать тот же набор параметров конфигурации, которые включают:
- Префикс и суффикс (как уже было видно):
templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html");
- Шаблонные псевдонимы, позволяющие использовать имена шаблонов, которые не соответствуют именам файлов. Если существуют суффикс/префикс и псевдоним, псевдоним будет применяться до префикса/суффикса:
templateResolver.addTemplateAlias("adminHome","profiles/admin/home"); templateResolver.setTemplateAliases(aliasesMap);
- Кодировка, применяемая при чтении шаблонов:
templateResolver.setEncoding("UTF-8");
- Используемый режим шаблона:
// Default is HTML templateResolver.setTemplateMode("XML");
- Режим по умолчанию для кэша шаблонов и patterns для определения того, являются ли конкретные шаблоны кэшируемыми или нет:
// Default is true templateResolver.setCacheable(false); templateResolver.getCacheablePatternSpec().addPattern("/users/*");
- TTL в миллисекундах для пропарсенных записей кэша шаблонов, созданных в этом resolver шаблона. Если не задано, единственный способ удалить запись из кеша будет LRU (максимальный размер кеша превышен, и запись будет самой старой).
// По умолчанию нет TTL (только LRU удаляет записи) templateResolver.setCacheTTLMs(60000L);
Пакеты интеграции Thymeleaf + Spring предлагают реализацию SpringResourceTemplateResolver, которая использует всю инфраструктуру Spring для доступа и чтения ресурсов в приложениях и которая является рекомендуемой реализацией в приложениях с поддержкой Spring.
Цепочка resolvers шаблонов
Кроме того, механизм шаблонов может указывать несколько искателей шаблонов, и в этом случае порядок может быть установлен между ними для поиска шаблона, так что, если первый не может найти шаблон, запрашивается второй и т. д.:
ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver();
classLoaderTemplateResolver.setOrder(Integer.valueOf(1));
ServletContextTemplateResolver servletContextTemplateResolver =
new ServletContextTemplateResolver(servletContext);
servletContextTemplateResolver.setOrder(Integer.valueOf(2));
templateEngine.addTemplateResolver(classLoaderTemplateResolver);
templateEngine.addTemplateResolver(servletContextTemplateResolver);
Когда применяется несколько шаблонов-резольверов, рекомендуется указывать patterns для каждого распознавателя шаблонов, чтобы Thymeleaf мог быстро отменить эти шаблонные resolvers, которые не предназначены для поиска шаблона, повышая производительность. Это не требование, а рекомендация:
ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver();
classLoaderTemplateResolver.setOrder(Integer.valueOf(1));
// У этого загрузчика классов даже не будут запрошены классы, не соответствующие этим паттернам
classLoaderTemplateResolver.getResolvablePatternSpec().addPattern("/layout/*.html");
classLoaderTemplateResolver.getResolvablePatternSpec().addPattern("/menu/*.html");
ServletContextTemplateResolver servletContextTemplateResolver =
new ServletContextTemplateResolver(servletContext);
servletContextTemplateResolver.setOrder(Integer.valueOf(2));
Если эти resolvable шаблоны не указаны, мы будем полагаться на конкретные возможности каждой из созданных нами ITemplateResolver реализаций. Обратите внимание, что не все реализации могут определять существование шаблона перед его resolving и поэтому всегда могут рассматривать шаблон как resolvable и прерывать цепочку поиска (не позволяя другим resolvers проверять один и тот же шаблон).
Все реализации ITemplateResolver, входящие в состав ядра Thymeleaf, включают механизм, который позволит нам заставить resolvers действительно проверить, существует ли ресурс, прежде чем считать его найденным. Это флаг checkExistence, который работает как:
ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver();
classLoaderTemplateResolver.setOrder(Integer.valueOf(1));
classLoaderTempalteResolver.setCheckExistence(true);
Этот флаг checkExistence заставляет resolver выполнять реальную проверку существования ресурса во время фазы поиска (и пусть следующий поиск осуществится в цепочке, если текущая проверка вернет false). Хотя это может показаться хорошим решением, в большинстве случаев это будет означать двойной доступ к самому ресурсу (один раз для проверки существования, в другое время для его чтения) и может быть проблемой производительности в некоторых сценариях, например, «удаленные» ресурсы шаблонов на основе URL-адресов — потенциальная проблема с производительностью, которая в любом случае может быть в значительной степени смягчена использованием кеша шаблона (в этом случае шаблоны будут искаться только при первом доступе к ним).
15.2 Resolvers сообщений
Мы явно не указали реализацию Message Resolver для нашего приложения Grocery, и, как было объяснено ранее, это означало, что используемой реализацией был объект org.thymeleaf.messageresolver.StandardMessageResolver.
StandardMessageResolver — это стандартная реализация интерфейса IMessageResolver, но мы могли бы создать наш собственный, если бы захотели, адаптированный к конкретным потребностям нашего приложения.
Пакеты интеграции Thymeleaf + Spring предлагают по умолчанию реализацию IMessageResolver, которая использует стандартный метод Spring для получения внешних сообщений, используя компоненты MessageSource, объявленные в контексте Spring Application Context.
Стандартный Resolver сообщений
Итак, как выглядит StandardMessageResolver сообщений, запрашиваемых по определенному шаблону?
Если имя шаблона «home», и оно находится в /WEB-INF/templates/home.html, а запрошенный язык — gl_ES, тогда этот распознаватель будет искать сообщения в следующих файлах в следующем порядке:
/WEB-INF/templates/home_gl_ES.properties
/WEB-INF/templates/home_gl.properties
/WEB-INF/templates/home.properties
Обратитесь к документации JavaDoc класса StandardMessageResolver для более подробной информации о том, как работает полный механизм поиска сообщений.
Настройка искателя сообщений
Что делать, если мы хотим добавить средство преобразования сообщений (или больше) в шаблонный движок? Это легко:
// Для настройки одного
templateEngine.setMessageResolver(messageResolver);
// Для настройки более одного
templateEngine.addMessageResolver(messageResolver);
И почему мы хотим иметь более одного искателя сообщений? По той же причине, что и искатель шаблонов: упорядочиватели сообщений упорядочены, и если первый не может найти конкретное сообщение, будет запрошен второй, затем третий и т. д.
15.3 Услуги по преобразованию
Служба преобразования, которая позволяет нам выполнять операции преобразования и форматирования данных с помощью синтаксиса с двойной скобкой (${{...}}), на самом деле является элементом стандартного диалекта, а не самого механизма шаблонов Thymeleaf.
Таким образом, способ его настройки заключается в настройке пользовательской реализации интерфейса IStandardConversionService непосредственно в экземпляре StandardDialect, который настраивается в механизм шаблонов. Подробнее:
IStandardConversionService customConversionService = ...
StandardDialect dialect = new StandardDialect();
dialect.setConversionService(customConversionService);
templateEngine.setDialect(dialect);
Обратите внимание, что пакеты thymeleaf-spring3 и thymeleaf-spring4 содержат SpringStandardDialect, и этот диалект уже предварительно настроен с внедрением IStandardConversionService, который интегрирует собственную инфраструктуру службы преобразования Spring в Thymeleaf.
15.4 Logging/Логирование
Thymeleaf уделяет большое внимание регистрации событий и всегда пытается предоставить максимальный объем полезной информации через свой интерфейс ведения журнала.
Используемая библиотека протоколирования — slf4j, которая на самом деле выступает в качестве моста для любой реализации протоколирования, которую мы могли бы использовать в нашем приложении (например, log4j).
В классах Thymeleaf будут записываться данные TRACE, DEBUG и INFO, в зависимости от уровня детализации, который мы желаем, и, кроме общего ведения журнала, он будет использовать три специальных регистратора, связанных с классом TemplateEngine, которые мы можем настроить отдельно для разных целей:
- org.thymeleaf.TemplateEngine.CONFIG выведет подробную конфигурацию библиотеки во время инициализации
- org.thymeleaf.TemplateEngine.TIMER выведет информацию о времени, затраченном на обработку каждого шаблона (полезно для бенчмаркинга!)
- org.thymeleaf.TemplateEngine.cache является префиксом для набора регистраторов, которые выводят конкретную информацию о кэшах. Хотя имена кэш-регистраторов настраиваются пользователем и, следовательно, могут меняться, по умолчанию они:
org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHE
Пример конфигурации для инфраструктуры регистрации Thymeleaf, использующей log4j, может быть:
log4j.logger.org.thymeleaf=DEBUG
log4j.logger.org.thymeleaf.TemplateEngine.CONFIG=TRACE
log4j.logger.org.thymeleaf.TemplateEngine.TIMER=TRACE
log4j.logger.org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE=TRACE