Статьи

Интеграция Thymeleaf с Spring (часть 1)

1. Введение

Эта статья посвящена тому, как Thymeleaf может быть интегрирован в среду Spring. Это позволит нашему веб-приложению MVC воспользоваться механизмом шаблонов Thymeleaf HTML5 без потери каких-либо функций Spring. Уровень данных использует Spring Data для взаимодействия с базой данных mongoDB.

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

  • Вставка нового гостя: синхронный запрос, показывающий, как Thymeleaf интегрируется с компонентами поддержки формы Spring.
  • Список гостей: асинхронный запрос, показывающий, как обрабатывать рендеринг фрагментов с помощью AJAX.

Этот урок ожидает, что вы знаете основы Thymeleaf. Если нет, вы должны сначала прочитать эту статью .

Вот пример потока приложения:

hotelFlow

Этот пример основан на версиях Thymeleaf 2.1 и Spring 4.

  • Исходный код можно найти на github .

2.Configuration

В этом руководстве используется подход JavaConfig для настройки необходимых компонентов. Это означает, что файлы конфигурации xml больше не нужны.

web.xml

Поскольку мы хотим использовать JavaConfig, нам нужно указать AnnotationConfigWebApplicationContext в качестве класса, который будет настраивать контейнер Spring. Если мы не укажем его, он будет использовать XmlWebApplicationContext по умолчанию.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!-- Bootstrap the root context -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
 
<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext -->
<context-param>
    <param-name>contextClass</param-name>
    <param-value>
        org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
</context-param>
 
<!-- @Configuration classes or package -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>xpadro.thymeleaf.configuration.WebAppConfiguration</param-value>
</context-param>
 
<!-- Spring servlet -->
<servlet>
    <servlet-name>springServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>springServlet</servlet-name>
    <url-pattern>/spring/*</url-pattern>
</servlet-mapping>

Конфигурация пружины

Моя конфигурация разделена на два класса: интеграция тимили-пружина (класс WebAppConfiguration ) и конфигурация mongoDB (класс MongoDBConfiguration ).

WebAppConfiguration.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@EnableWebMvc
@Configuration
@ComponentScan("xpadro.thymeleaf")
@Import(MongoDBConfiguration.class)
public class WebAppConfiguration extends WebMvcConfigurerAdapter {
    @Bean
    @Description("Thymeleaf template resolver serving HTML 5")
    public ServletContextTemplateResolver templateResolver() {
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
        templateResolver.setPrefix("/WEB-INF/html/");
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode("HTML5");
         
        return templateResolver;
    }
     
    @Bean
    @Description("Thymeleaf template engine with Spring integration")
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver());
         
        return templateEngine;
    }
     
    @Bean
    @Description("Thymeleaf view resolver")
    public ThymeleafViewResolver viewResolver() {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine());
         
        return viewResolver;
    }
     
    @Bean
    @Description("Spring message resolver")
    public ResourceBundleMessageSource messageSource() { 
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); 
        messageSource.setBasename("i18n/messages"); 
         
        return messageSource; 
    }
     
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/WEB-INF/resources/");
    }
}

Вещи, на которые стоит обратить внимание, глядя на приведенный выше код:

  • @EnableWebMvc : Это позволяет аннотации Spring MVC, такие как @RequestMapping. Это будет то же самое, что и пространство имен xml <mvc: annotation-driven />
  • @ComponentScan («xpadro.thymeleaf») : активирует сканирование компонентов в пакете и подпакетах xpadro.thymeleaf. Классы, аннотированные @Component и связанные аннотации, будут зарегистрированы как бины.
  • Мы регистрируем три компонента, которые необходимы для настройки Thymeleaf и его интеграции с платформой Spring.
    • распознаватель шаблонов: разрешает имена шаблонов и делегирует их в распознаватель ресурсов контекста сервлета.
    • шаблонизатор: интегрируется с Spring Framework, устанавливая специфический для Spring диалект в качестве диалекта по умолчанию.
    • распознаватель представлений: реализация Thymeleaf интерфейса преобразователя представлений Spring MVC для разрешения представлений Thymeleaf.

MongoDBConfiguration.java

01
02
03
04
05
06
07
08
09
10
11
12
13
@Configuration
@EnableMongoRepositories("xpadro.thymeleaf.repository")
public class MongoDBConfiguration extends AbstractMongoConfiguration {
    @Override
    protected String getDatabaseName() {
        return "hotel-db";
    }
     
    @Override
    public Mongo mongo() throws Exception {
        return new Mongo();
    }
}

Этот класс расширяет AbstracMongoConfiguration , который определяет компоненты mongoFactory и mongoTemplate.

@EnableMongoRepositories будет сканировать указанный пакет, чтобы найти интерфейсы, расширяющие MongoRepository. Затем он создаст бин для каждого. Мы увидим это позже, в разделе уровня доступа к данным.

3.Thymeleaf — Spring MVC Integration

HotelController

Контроллер отвечает за доступ к сервисному уровню, создает модель представления из результата и возвращает представление. С конфигурацией, которую мы установили в предыдущем разделе, теперь контроллеры MVC смогут возвращать идентификатор представления, который будет разрешен как представление Thymeleaf.

Ниже мы видим фрагмент контроллера, где он обрабатывает начальный запрос (http: // localhost: 8080 / th-spring -gration / spring / home):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Controller
public class HotelController {
    @Autowired
    private HotelService hotelService;
     
    @ModelAttribute("guest")
    public Guest prepareGuestModel() {
        return new Guest();
    }
     
    @ModelAttribute("hotelData")
    public HotelData prepareHotelDataModel() {
        return hotelService.getHotelData();
    }
     
    @RequestMapping(value = "/home", method = RequestMethod.GET)
    public String showHome(Model model) {
        prepareHotelDataModel();
        prepareGuestModel();
         
        return "home";
    }
     
    ...
}

Типичный контроллер MVC, который возвращает «домашний» идентификатор представления. Средство распознавания шаблонов Thymeleaf будет искать шаблон с именем «home.html», который находится в папке / WEB-INF / html /, как указано в конфигурации. Кроме того, атрибут представления с именем hotelData будет открыт для представления Thymeleaf, содержащего информацию об отеле, которая должна отображаться в начальном представлении.

Этот фрагмент домашнего представления показывает, как он получает доступ к некоторым свойствам атрибута представления с помощью Spring Expression Language (Spring EL):

1
2
<span th:text="${hotelData.name}">Hotel name</span><br />
<span th:text="${hotelData.address}">Hotel address</span><br />

Еще одна приятная особенность — Thymeleaf сможет разрешать свойства сообщений, управляемых Spring, которые были настроены через интерфейс MessageSource.

1
<h3 th:text="#{hotel.information}">Hotel Information</h3>

Обработка ошибок

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

Поскольку у нас только один контроллер, нет необходимости использовать @ControllerAdvice . Вместо этого мы будем использовать аннотированный метод @ExceptionHandler. Вы можете заметить, что мы возвращаем интернационализированное сообщение как сообщение об ошибке:

01
02
03
04
05
06
07
08
09
10
@ExceptionHandler({GuestFoundException.class})
public ModelAndView handleDatabaseError(GuestFoundException e) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("home");
    modelAndView.addObject("errorMessage", "error.user.exist");
    modelAndView.addObject("guest", prepareGuestModel());
    modelAndView.addObject("hotelData", prepareHotelDataModel());
     
    return modelAndView;
}

Thymeleaf разрешит атрибут представления с помощью $ {}, а затем разрешит сообщение # {}:

1
<span class="messageContainer" th:unless="${#strings.isEmpty(errorMessage)}" th:text="#{${errorMessage}}"></span>

Атрибут th: исключением, если Thymeleaf будет отображать элемент span, только если было возвращено сообщение об ошибке.

hotelError

4. Уровень обслуживания

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Service("hotelServiceImpl")
public class HotelServiceImpl implements HotelService {
    @Autowired
    HotelRepository hotelRepository;
     
    @Override
    public List<Guest> getGuestsList() {
        return hotelRepository.findAll();
    }
     
    @Override
    public List<Guest> getGuestsList(String surname) {
        return hotelRepository.findGuestsBySurname(surname);
    }
     
    @Override
    public void insertNewGuest(Guest newGuest) {
        if (hotelRepository.exists(newGuest.getId())) {
            throw new GuestFoundException();
        }
         
        hotelRepository.save(newGuest);
    }
}

5. Уровень доступа к данным

HotelRepository расширяет класс Spring Data MongoRepository .

1
2
3
4
public interface HotelRepository extends MongoRepository<Guest, Long> {
    @Query("{ 'surname' : ?0 }")
    List<Guest> findGuestsBySurname(String surname);
}

Это просто интерфейс, мы не будем его реализовывать. Если вы помните класс конфигурации, мы добавили следующую аннотацию:

1
@EnableMongoRepositories("xpadro.thymeleaf.repository")

Поскольку это пакет, в котором находится хранилище, Spring создаст bean-компонент и внедрит в него mongoTemplate. Расширение этого интерфейса предоставляет нам общие операции CRUD. Если вам нужны дополнительные операции, вы можете добавить их с аннотацией @Query (см. Код выше).

6. Заключение

Мы настроили Thymeleaf для разрешения представлений в управляемом веб-приложении Spring. Это позволяет представлению получить доступ к Spring Expression Language и разрешению сообщений. В следующей части этого руководства будет показано, как формы связаны с компонентами поддержки формы Spring и как мы можем перезагрузить фрагменты, отправив запрос AJAX.