Servlet + Spring 搭建Thymeleaf模板引擎小案例

Servlet + Spring 搭建Thymeleaf模板引擎小案例

前言

  Thymeleaf模板引擎比较倾向于前端,了解过一点前端的人都会很快上手的。之所以不用JSP模板,是因为他还是原生的Java代码。换句话来说,就是Thymeleaf可以直接查看效果无需启动服务器,反之JSP就不行,必须依赖于服务器。这两种模板属于同一个方向,技术应用是要结合应用场景的。如果单纯从学习角度来看,学习哪个技术都没有错。但如果是要做产品,做项目,那技术选型就非常关键了。

正文

  • 项目目录:
    项目目录
  • pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>vip.wulang</groupId>
  <artifactId>thymeleaf</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <properties>
    <mysql.version>5.1.34</mysql.version>
    <spring.version>4.3.0.RELEASE</spring.version>
    <hibernate.version>5.1.0.Final</hibernate.version>
    <druid.version>1.1.10</druid.version>
    <jpa.version>1.10.4.RELEASE</jpa.version>
    <test.version>4.12</test.version>
    <common.version>3.3.2</common.version>
    <servlet.version>3.1.0</servlet.version>
    <fastjson.version>1.2.47</fastjson.version>
    <slf4j.version>1.7.25</slf4j.version>
    <aspectj.version>1.8.10</aspectj.version>
    <validation.version>6.0.13.Final</validation.version>
    <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
  </properties>

  <dependencies>
    <!-- Spring Framework start -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!-- Spring Framework end -->

    <!-- Druid start -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>${druid.version}</version>
    </dependency>
    <!-- Druid end -->

    <!-- JPA start -->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-jpa</artifactId>
      <version>${jpa.version}</version>
      <exclusions>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-beans</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-orm</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <!-- JPA end -->

    <!-- Hibernate start -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate.version}</version>
    </dependency>

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
    <!-- Hibernate end -->

    <!-- Mysql start -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>
    <!-- Mysql end -->

    <!-- Test start -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${test.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- Test end -->

    <!-- Common start -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>${common.version}</version>
    </dependency>
    <!-- Common end -->

    <!-- Servlet start -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>${servlet.version}</version>
      <scope>provided</scope>
    </dependency>
    <!-- Servlet end -->

    <!-- FastJson start -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>${fastjson.version}</version>
    </dependency>
    <!-- FastJson end -->

    <!-- Slf4j start -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>${slf4j.version}</version>
    </dependency>
    <!-- Slf4j end -->

    <!-- Aspectj start -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>${aspectj.version}</version>
    </dependency>
    <!-- Aspectj end -->

    <!-- Validation start -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>${validation.version}</version>
    </dependency>
    <!-- Validation end -->

    <!-- Thymeleaf start -->
    <dependency>
      <groupId>org.thymeleaf</groupId>
      <artifactId>thymeleaf-spring4</artifactId>
      <version>${thymeleaf.version}</version>
    </dependency>
    <dependency>
      <groupId>org.thymeleaf</groupId>
      <artifactId>thymeleaf</artifactId>
      <version>${thymeleaf.version}</version>
    </dependency>
    <!-- Thymeleaf end -->

  </dependencies>

</project>
  • web.xml文件,我是基于Java配置文件的方式,你们也可以使用其他方式,个人喜好
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  
  <context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
  </context-param>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>vip.wulang.config.MyApplicationContext</param-value>
  </context-param>
  
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
  <servlet>
    <servlet-name>IGTVGController</servlet-name>
    <servlet-class>vip.wulang.servlet.IGTVGController</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>IGTVGController</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  
</web-app>
  • MyApplicationContext.java,Spring容器
package vip.wulang.config;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.context.ServletContextAware;
import org.thymeleaf.ITemplateEngine;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.messageresolver.IMessageResolver;
import org.thymeleaf.messageresolver.StandardMessageResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;

import javax.servlet.ServletContext;
import java.io.IOException;
import java.util.Properties;

/** * @author CoolerWu on 2018/11/20. * @version 1.0 */
@Configuration
@ComponentScan("vip.wulang")
public class MyApplicationContext implements ApplicationContextAware, ServletContextAware {

    private static ApplicationContext applicationContext;
    private static ServletContext servletContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        MyApplicationContext.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public void setServletContext(ServletContext servletContext) {
        MyApplicationContext.servletContext = servletContext;
    }

    public static ServletContext getServletContext() {
        return servletContext;
    }

    @Bean
    public ITemplateResolver templateResolver() {
        ServletContextTemplateResolver servletContextTemplateResolver
                = new ServletContextTemplateResolver(servletContext);

        servletContextTemplateResolver.setTemplateMode(TemplateMode.HTML);
        servletContextTemplateResolver.setPrefix("/WEB-INF/templates/");
        servletContextTemplateResolver.setSuffix(".html");
        servletContextTemplateResolver.setCacheTTLMs(3600000L);
        servletContextTemplateResolver.setCacheable(true);

        return servletContextTemplateResolver;
    }

    @Bean
    public IMessageResolver messageResolver() throws IOException {
        ClassPathResource classPathResource = new ClassPathResource("./home.properties");
        Properties properties = new Properties();

        properties.load(classPathResource.getInputStream());

        StandardMessageResolver standardMessageResolver = new StandardMessageResolver();

        standardMessageResolver.setDefaultMessages(properties);

        return standardMessageResolver;
    }

    @Bean
    public ITemplateEngine templateEngine(ITemplateResolver templateResolver, IMessageResolver messageResolver) {
        TemplateEngine templateEngine = new TemplateEngine();

        templateEngine.setTemplateResolver(templateResolver);
        templateEngine.setMessageResolver(messageResolver);

        return templateEngine;
    }

}

  首先,笔者实现了两个接口 ApplicationContextAware、ServletContextAware,Spring启动时会自动注入 ApplicationContext、ServletContext。其次从官方文档来看,我们先要配置 ITemplateResolver Bean,其实就是模板解析器,ServletContextTemplateResolver 是该接口的实现类,并在其中设置前缀、后缀、模板类型、是否缓存、缓存多久等信息。再配置 IMessageResolver Bean,就是消息解析器,这个也可以不用配置。默认/WEB-INF/templates/home.html在同一文件夹中找到属性文件中的消息,并使用与模板相同的名称,例如:

  1. /WEB-INF/templates/home_en.properties 用于英文文本。
  2. /WEB-INF/templates/home_es.properties 西班牙语文本。
  3. /WEB-INF/templates/home_pt_BR.properties 用于葡萄牙语(巴西)语言文本。
  4. /WEB-INF/templates/home.properties 对于默认文本(如果区域设置不匹配)。

而笔者则是设置的是在根目录中,"./home.properties"。然后就到了最重要的一个环节了,ITemplateEngine 这个接口,就是模板引擎,里面包含了之前了模板解析器、消息解析器等。至少需要配置模板解析器。以后要进行页面跳转也是通过模板引擎,我们可以当成模板引擎就是操作按钮,其他的解析器都是已经在模板引擎装配好了。

  • IGTVGController.java 配置,继承了 GenericServlet
package vip.wulang.servlet;

import org.thymeleaf.ITemplateEngine;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import vip.wulang.config.MyApplicationContext;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/** * @author CoolerWu on 2018/11/20. * @version 1.0 */
public class IGTVGController extends GenericServlet {

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        TemplateEngine templateEngine =
                ((TemplateEngine) MyApplicationContext.getApplicationContext().getBean(ITemplateEngine.class));

        WebContext webContext =
                new WebContext(request, response, MyApplicationContext.getServletContext(), request.getLocale());

        templateEngine.process("home", webContext, response.getWriter());
    }
}

  官方文档介绍,Thymeleaf有一个上下文,这个上下文就是接口 IContext,而 IWebContext 扩展了该基础接口,WebContext 就是实现类。

WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());

这四个构造函数参数中只有三个是必需的,因为如果没有指定系统,将使用系统的默认语言环境(尽管在实际应用程序中不应该发生这种情况)。准备好上下文对象后,现在我们可以告诉模板引擎使用上下文处理模板(通过其名称),并将响应编写器传递给它,以便可以将响应写入它:

templateEngine.process("home", webContext, response.getWriter());
  • home.properties
home.welcome=Bienvenido a nuestra tienda de comestibles!!!!
  • home.html
<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>

<body>

    <p th:text="#{home.welcome}">Welcome to our grocery store!</p>

</body>

</html>

  已经搭建完成了,不过需要注意的是:

<html xmlns:th="http://www.thymeleaf.org">

就是增加一个约束空间。只适用于html模板,也可以不可以不写约束语句,即:

<!DOCTYPE html>

<html>

<head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>

<body>

    <p data-th-text="#{home.welcome}">Welcome to our grocery store!</p>

</body>

</html>

这种语句通用于 Thymeleaf 的所有模板。看个人喜好。不启动服务器打开:
不启动服务器
启动服务器打开:
启动服务器

结束语

  更多详情,了解官方文档:https://www.thymeleaf.org/