Spring 系列 (2) - 在 Spring Boot 项目里使用 Thymeleaf、JQuery+Bootstrap 和国际化


Spring Boot 推荐使用 Thymeleaf 作为其视图模板引擎。Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎,它与 JSP、Velocity、FreeMaker 等模板引擎类似,也可以轻易地与 Spring MVC 等 Web 框架集成。
    
Thymeleaf 是新一代 Java 模板引擎,与 Velocity、FreeMarker 等传统 Java 模板引擎不同,Thymeleaf 支持 HTML 原型,其文件后缀为 “.html”,因此它可以直接被浏览器打开,此时浏览器会忽略未定义的 Thymeleaf 标签属性,展示 thymeleaf 模板的静态页面效果;当通过 Web 应用程序访问时,Thymeleaf 会动态地替换掉静态内容,使页面动态显示。

本文将结合实例讲讲如何在 Spring Boot 项目里使用 Thymeleaf、JQuery、Bootstrap 等。

Spring Boot: https://spring.io/projects/spring-boot/
Thymeleaf:https://www.thymeleaf.org/
JQuery: https://jquery.com/
Bootstrap: https://getbootstrap.com/

1. 开发环境

    Windows版本:Windows 10 Home (20H2)   
    IntelliJ IDEA (https://www.jetbrains.com/idea/download/):Community Edition for Windows 2020.1.4
    Apache Maven (https://maven.apache.org/):3.8.1

    注:Spring 开发环境的搭建,可以参考 “ ”。


2. 创建 Spring Boot 项目

    1) 运行 IDEA 创建项目
    
        点击菜单 New 创建 Project:
        
        New Project -> Project Type: Maven -> Project SDK: 1.8 -> Check "Create from archtype" -> select "org.apache.maven.archtypes:maven-archtype-quickstart" -> Next

            Name: SpringbootExample02
            GroupId: com.example
            ArtifactId: SpringbootExample02

        -> Finish

    2) 生成的项目目录结构

        |-- src
        |   |-- main
        |   |     |-- java
        |   |       |-- com
        |   |            |-- example
        |   |                   |-- App.java
        |   |-- test
        |        |-- java
        |               |-- com
        |                    |-- example
        |                           |-- AppTest.java
        |-- pom.xml

    3) Spring Boot Web 配置
    
        参考 “” 的 “3. 配置 Spring Boot Web” 部分,完成 Spring Boot Web 配置。

        (1) 配置后的 pom.xml

 1             <?xml version="1.0" encoding="UTF-8"?>
 2 
 3             <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4                 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5                 <modelVersion>4.0.0modelVersion>
 6 
 7                 <groupId>com.examplegroupId>
 8                 <artifactId>SpringbootExample02artifactId>
 9                 <version>1.0-SNAPSHOTversion>
10 
11                 <name>SpringbootExample02name>
12                 
13                 <url>http://www.example.comurl>
14 
15                 <properties>
16                     <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
17                     <maven.compiler.source>1.8maven.compiler.source>
18                     <maven.compiler.target>1.8maven.compiler.target>
19                 properties>
20 
21                 <packaging>jarpackaging>
22 
23                 <parent>
24                     <groupId>org.springframework.bootgroupId>
25                     <artifactId>spring-boot-starter-parentartifactId>
26                     <version>2.6.6version>
27                     <relativePath/> 
28                 parent>
29 
30                 <dependencies>
31                     <dependency>
32                         <groupId>junitgroupId>
33                         <artifactId>junitartifactId>
34                         <version>4.11version>
35                         <scope>testscope>
36                     dependency>
37                     <dependency>
38                         <groupId>org.springframework.bootgroupId>
39                         <artifactId>spring-boot-starter-webartifactId>
40                     dependency>
41                     
42                     <dependency>
43                         <groupId>org.springframework.bootgroupId>
44                         <artifactId>spring-boot-starter-tomcatartifactId>
45                         <scope>providedscope>
46                     dependency>
47                     <dependency>
48                         <groupId>org.springframework.bootgroupId>
49                         <artifactId>spring-boot-starter-testartifactId>
50                         <scope>testscope>
51                     dependency>
52                 dependencies>
53 
54                 <build>
55                     ...
56                 build>
57             project>


            在IDE中项目列表 -> SpringbootExample02 -> 点击鼠标右键 -> Maven -> Reload Project

        (2) 修改 src/main/java/com/example/App.java 文件

 1             package com.example;
 2 
 3             import org.springframework.boot.SpringApplication;
 4             import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 
 6             @SpringBootApplication
 7             public class App {
 8                 public static void main(String[] args) {
 9                     SpringApplication.run(App.class, args);
10                     System.out.println("Spring boot example02 project");
11                 }
12             }


        (3) 创建 src/main/java/com/example/ServletInitializer.java 文件

 1             package com.example;
 2 
 3             import org.springframework.boot.builder.SpringApplicationBuilder;
 4             import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
 5 
 6             public class ServletInitializer extends SpringBootServletInitializer {
 7 
 8                 @Override
 9                 protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
10                     return application.sources(App.class);
11                 }
12 
13             }


        (4) 创建 src/main/java/com/example/controller/IndexController.java 文件

 1             package com.example.controller;
 2 
 3             import org.springframework.stereotype.Controller;
 4             import org.springframework.web.bind.annotation.RequestMapping;
 5             import org.springframework.web.bind.annotation.ResponseBody;
 6 
 7             @Controller
 8             public class IndexController {
 9                 @ResponseBody
10                 @RequestMapping("/test")
11                 public String test() {
12                     return "Test Page";
13                 }
14             }


        (5) 创建 src/main/resources/application.properties 文件

1             spring.main.banner-mode=off
2 
3             # Web server
4             server.display-name=SpringbootExample02-Test
5             server.address=localhost
6             server.port=9090


        (6) 运行

            Edit Configurations

                Click "+" add new configuration -> Select "Maven"

                    Command line: clean spring-boot:run
                    Name: SpringbootExample02 [clean,spring-boot:run]

                -> Apply / OK

            Click Run "SpringbootExample02 [clean,spring-boot:run]"

                ...

                Spring boot example02 project       

            访问 http://localhost:9090/test

                Test Page


3. 导入 Thymeleaf 依赖包

    访问 http://www.mvnrepository.com/,查询 Thymeleaf

    修改 pom.xml:

 1         <project ... >
 2             ...
 3             <dependencies>
 4                 ...
 5 
 6                 
 7                 <dependency>
 8                     <groupId>org.springframework.bootgroupId>
 9                     <artifactId>spring-boot-starter-thymeleafartifactId>
10                 dependency>
11 
12                 ...
13             dependencies>
14 
15             ...
16         project>


        在IDE中项目列表 -> SpringbootExample02 -> 点击鼠标右键 -> Maven -> Reload Project


4. 配置静态资源(jQuery、Bootstrap、Images)

    本文使用 jQuery 3.6.0 和 Bootstrap 4.2.1,两者放到 src/main/resources/static/lib/ 目录下,图片放在 src/main/resources/static/images/ 目录下。
    
    目录结构如下

         static
            |- images
            |   |- bootstrap-solid.svg
            |
            |- lib
                |- jquery
                |     |- jquery-3.6.0.min.js
                |
                |- bootstrap-4.2.1-dist
                    |- css
                    |   |- bootstrap.min.css
                    |    ...
                    |   
                    |- js
                        |- bootstrap.min.js
                            ...

        注:bootstrap-solid.svg 的下载自 https://v4.bootcss.com/docs/4.6/assets/brand/bootstrap-solid.svg


5. 配置 Thymeleaf 模版

    Thymeleaf 模板的默认位置在 resources/templates 目录下,默认的后缀是 html,即只要将 HTML 页面放在 “classpath:/templates/” 下,Thymeleaf 就能自动进行渲染。

     1) 创建 src/main/resources/templates/common.html 文件

 1         <div th:fragment="header(var)">
 2             <meta charset="UTF-8">
 3             <title th:text="${var}">Titletitle>
 4             <link rel="stylesheet" th:href="@{/lib/bootstrap-4.2.1-dist/css/bootstrap.min.css}" href="/lib/bootstrap-4.2.1-dist/css/bootstrap.min.css">
 5             <script language="javascript" th:src="@{/lib/jquery/jquery-3.6.0.min.js}" src="/lib/jquery/jquery-3.6.0.min.js">script>
 6             <script language="javascript" th:src="@{/lib/bootstrap-4.2.1-dist/js/bootstrap.min.js}" src="/lib/bootstrap-4.2.1-dist/js/bootstrap.min.js">script>
 7         div>
 8 
 9         <div th:fragment="content-header" class="container" id="content-header-id">
10             <nav class="navbar navbar-light bg-light">
11                 <a class="navbar-brand" href="#">
12                     <img th:src="@{/images/bootstrap-solid.svg}" src="/images/bootstrap-solid.svg" width="30" height="30" class="d-inline-block align-top" alt="">
13                     Thymeleaf Demo
14                 a>
15             nav>
16         div>
17 
18         <div th:fragment="content-footer(var)" class="container" id="content-footer-id">
19             <p th:text="${var}">Content Footerp>
20         div>


    2) 创建 src/main/resources/templates/home.html 文件

 1         DOCTYPE html>
 2         <html lang="en" xmlns:th="http://www.thymeleaf.org">
 3         <head th:include="common::header(var='Home Page')">
 4         head>
 5         <body>
 6             <div th:replace="common::content-header">div>
 7 
 8             <div class="container" id="content" th:style="'min-height: 480px;'">
 9                 <p> p>
10                 <h3 th:text="'Welcome ' + ${name}">Welcomeh3>
11 
12                 <p> p>
13                 <p>Message: <span id="message">span>p>
14             div>
15 
16             <div th:replace="common::content-footer(var='Copyright © 2020')">div>
17 
18             <script type="text/javascript">
19                 $(document).ready(function(){
20                     console.log("jQuery is running");
21 
22                     $("#message").html("jQuery is running");
23                 });
24             script>
25         body>
26         html>


    3) 修改 src/main/java/com/example/controller/IndexController.java 文件

 1         package com.example.controller;
 2 
 3         import org.springframework.ui.Model;
 4         import org.springframework.stereotype.Controller;
 5         import org.springframework.web.bind.annotation.RequestMapping;
 6         import org.springframework.web.bind.annotation.ResponseBody;
 7 
 8         @Controller
 9         public class IndexController {
10             @ResponseBody
11             @RequestMapping("/test")
12             public String test() {
13                 return "Test Page";
14             }
15 
16             @RequestMapping("/home")
17             public String home(Model model) {
18                 model.addAttribute("name", "admin");
19                 return "home";
20             }
21         }


    访问 http://localhost:9090/home
 
        [Logo] Thymeleaf Demo
        
        Welcome admin
        
        Message: jQuery is running

        Copyright ? 2020


6. 国际化

    1) 编写国际化资源文件

        在 src/main/resources 下创建一个 i18n 的目录,并在该目录中按照国际化资源文件命名格式分别创建以下三个文件:

            home.properties:无语言设置时生效
            home_en_US.properties:英语时生效
            home_zh_CN.properties:中文时生效

        以上国际化资源文件创建完成后,IDEA 会自动识别它们,并转换成 Resource Bundle 模式。

        打开 home.properties,点击窗口底部 Resource Bundle 标签,然后点击 “+” 号,创建属性。
        
            修改后的 home.properties 文件

                home.title=Welcome(default)
                home.message=Message(default)
                home.status_on=jQuery is running(default)
                home.status_off=jQuery is off (default)

            修改后的 home_en_US.properties 文件

                home.title=Welcome
                home.message=Message
                home.status_on=jQuery is running
                home.status_off=jQuery is off

            修改后的 home_zh_CN.properties 文件

                home.title=欢迎
                home.message=信息
                home.status_on=jQuery 正在运行
                home.status_off=jQuery 没有生效

    2) 修改 src/main/resources/application.properties 文件,添加如下内容:

        spring.messages.basename=i18n.home
        spring.messages.encoding=UTF-8

    3) 修改 src/main/resources/templates/home.html 文件

 1         DOCTYPE html>
 2         <html lang="en" xmlns:th="http://www.thymeleaf.org">
 3         <head th:include="common::header(var='Home Page')">
 4         head>
 5         <body>
 6             <div th:replace="common::content-header">div>
 7 
 8             <div class="container" id="content" th:style="'min-height: 480px;'">
 9                 <p> p>
10                 <h3 th:text="#{home.title} + ' ' + ${name}">Welcomeh3>
11 
12                 <p> p>
13                 <p><span th:text="#{home.message}">Messagespan>: 
14                     <span id="message" th:text="#{home.status_off}">span>p>
15             div>
16 
17             <div th:replace="common::content-footer(var='Copyright © 2020')">div>
18 
19             <script type="text/javascript" th:inline="javascript">
20                 $(document).ready(function(){
21                     console.log("jQuery is running");
22 
23                     $("#message").html([[#{home.status_on}]]);
24                 });
25             script>
26         body>
27         html>


    访问 http://localhost:9090/home


    注:Spring Boot 根据浏览器放出的请求头的 Accept-Language 来自动选择资源文件,比如 Accept-Language: zh-CN 映射到 home_zh_CN.properties,Accept-Language: en-US 映射到 home_en_US.properties,其它值会默认映射到 home.properties。


6. 使用 spring-boot-maven-plugin 插件运行打包

    1) 修改 pom.xml

 1         <project ... >
 2             ...
 3 
 4             <build>
 5                 <finalName>SpringbootExample02finalName>
 6                 <plugins>
 7                     <plugin>
 8                         <groupId>org.springframework.bootgroupId>
 9                         <artifactId>spring-boot-maven-pluginartifactId>
10                         <configuration>
11                             <mainClass>com.example.AppmainClass>
12                             <layout>JARlayout>
13                         configuration>
14                         <executions>
15                         <execution>
16                             <goals>
17                             <goal>repackagegoal>
18                             goals>
19                         execution>
20                         executions>
21                     plugin>
22                 plugins>
23                 ...
24             build>
25         project>


        layout 属性用来指定打成 jar 还是 war 文件,可用的值包括:ZIP 、JAR 、WAR、 NONE

        在IDE中项目列表 -> SpringbootExample02 -> 点击鼠标右键 -> Maven -> Reload Project

    2) 打包 jar

        菜单 View -> Tool Windows -> Maven -> SpringbootExample02 -> Lifecycle -> Clean & Package

        jar 包生成在目录 target/ 里

            SpringbootExample02.jar
            SpringbootExample02.jar.original  

        注:SpringbootExample02.jar  包含依赖包,可以直接运行。 SpringbootExample02.jar.original 里不包含依赖的包(要手动配置依赖环境),运行前要把文件名上的 “.original” 去掉。

        点击 IDEA 底部 Terminal 标签页,执行如下命令。

            $ java -jar target/SpringbootExample02.jar

                ...

                Spring boot example project       

            访问 http://localhost:9090/test

                Test Page

        复制 src/main/resources/application.properties 文件到 target/ 目录下,修改 target/application.properties 配置如下:

            spring.main.banner-mode=console

            # Web server
            server.display-name=SpringbootExample02-Test
            server.address=localhost
            server.port=8080  

        运行如下命令:   
        
            $ java -jar target/SpringbootExample02.jar --spring.config.location target/application.properties

                    ...

                Spring boot example project       

            访问 http://localhost:8080/test

                Test Page

            注:访问 http://localhost:9090/home 时,不能正常显示文字,因为需要国际化配置文件支持,这部分会在以后的外部配置文件实例中详细讲解。   

    3) 打包 war

        把 pom.xml 里 jar 改成 war, spring-boot-maven-plugin 的 layout 属性改成 WAR,并把 spring-boot-starter-tomcat 依赖注释掉。

        菜单 View -> Tool Windows -> Maven -> SpringbootExample02 -> Lifecycle -> Clean & Package

        war 包生成在目录 target/ 里

            SpringbootExample02.war
            SpringbootExample02.war.original

        把 SpringbootExample02.war 放到独立运行的 Tomcat 的 webapp 目录下,假设 Tomcat 运行在 8080 端口。
        
        访问 http://localhost:8080/SpringbootExample02/test

            Test Page