架構(gòu)實戰(zhàn)篇(四):Spring Boot整合 Thymeleaf

前言

Thymeleaf 是一種模板語言。那模板語言或模板引擎是什么?常見的模板語言都包含以下幾個概念:數(shù)據(jù)(Data)、模板(Template)、模板引擎(Template Engine)和結(jié)果文檔(Result Documents)。

Spring boot 支持多種模板語言(Thymeleaf 、Freemarker、Mustache、Groovy Templates)
Thymeleaf 跟大部分的模板語言類似,上手容易,使用簡單

我們先看下已經(jīng)完成的項目結(jié)構(gòu)圖

項目結(jié)構(gòu)

最終運行結(jié)果

最終運行結(jié)果

下面開始一步一步的編寫代碼了

增加Spring boot的maven 依賴

在原有基礎(chǔ)的pom結(jié)構(gòu)中追加Swagger2的依賴

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.RELEASE</version>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring-boot-web-thymeleaf</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- Compile -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

增加一個消息類

package sample.web.ui;
import java.util.Calendar;
import org.hibernate.validator.constraints.NotEmpty;

public class Message {

    private Long id;

    // 編寫不能為空的提示語
    @NotEmpty(message = "Message is required.")
    private String text;

    // 編寫不能為空的提示語
    @NotEmpty(message = "Summary is required.")
    private String summary;

    private Calendar created = Calendar.getInstance();

        // get set
}

保存消息的接口

package sample.web.ui;

public interface MessageRepository {
    Iterable<Message> findAll();
    Message save(Message message);
    Message findMessage(Long id);
    void deleteMessage(Long id);
}

使用內(nèi)存保存消息

package sample.web.ui;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;

public class InMemoryMessageRepository implements MessageRepository {

    // 用來模擬主鍵自增
    private static AtomicLong counter = new AtomicLong();

    // 用來存儲消息
    private final ConcurrentMap<Long, Message> messages = new ConcurrentHashMap<Long, Message>();

    @Override
    public Iterable<Message> findAll() {
        return this.messages.values();
    }

    @Override
    public Message save(Message message) {
        Long id = message.getId();
        if (id == null) {
            // 生成一個ID
            id = counter.incrementAndGet();
            message.setId(id);
        }
        // 保存消息
        this.messages.put(id, message);
        return message;
    }

    @Override
    public Message findMessage(Long id) {
        return this.messages.get(id);
    }

    @Override
    public void deleteMessage(Long id) {
        this.messages.remove(id);
    }
}

編寫控制層代碼

package sample.web.ui.mvc;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import sample.web.ui.Message;
import sample.web.ui.MessageRepository;
import javax.validation.Valid;

@Controller
@RequestMapping("/messages")
public class MessageController {

    private final MessageRepository messageRepository;

    public MessageController(MessageRepository messageRepository) {
        this.messageRepository = messageRepository;
    }

    // 進(jìn)入消息列表頁面
    @GetMapping
    public ModelAndView list() {
        Iterable<Message> messages = this.messageRepository.findAll();
        return new ModelAndView("messages/list", "messages", messages);
    }

    // 查看消息詳情
    @GetMapping("{id}")
    public ModelAndView view(@PathVariable("id") Message message) {
        return new ModelAndView("messages/view", "message", message);
    }

    // 進(jìn)入創(chuàng)建消息頁面
    @GetMapping(params = "form")
    public String createForm(@ModelAttribute Message message) {
        return "messages/form";
    }

    // 創(chuàng)建消息
    @PostMapping
    public ModelAndView create(@Valid Message message, BindingResult result,
                               RedirectAttributes redirect) {
        // 內(nèi)容驗證
        if (result.hasErrors()) {
            return new ModelAndView("messages/form", "formErrors", result.getAllErrors());
        }
        // 保存消息
        message = this.messageRepository.save(message);
        // 重定向增加一個消息
        redirect.addFlashAttribute("globalMessage", "Successfully created a new message");
        return new ModelAndView("redirect:/messages/{message.id}", "message.id", message.getId());
    }

    // 刪除消息
    @GetMapping(value = "delete/{id}")
    public ModelAndView delete(@PathVariable("id") Long id) {
        this.messageRepository.deleteMessage(id);
        Iterable<Message> messages = this.messageRepository.findAll();
        return new ModelAndView("messages/list", "messages", messages);
    }

    // 進(jìn)入修改消息頁面
    @GetMapping(value = "modify/{id}")
    public ModelAndView modifyForm(@PathVariable("id") Message message) {
        return new ModelAndView("messages/form", "message", message);
    }
}

程序入口main

package sample.web.ui;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@SpringBootApplication
public class SampleWebUiApplication {

    // 在Spring 容器中加入內(nèi)存管理消息實例
    @Bean
    public MessageRepository messageRepository() {
        return new InMemoryMessageRepository();
    }

    // 自定義類型轉(zhuǎn)換,Controller 入?yún)⒆址D(zhuǎn)換為 Message 類型
    @Bean
    public Converter<String, Message> messageConverter() {
        return new Converter<String, Message>() {
            @Override
            public Message convert(String id) {
                return messageRepository().findMessage(Long.valueOf(id));
            }
        };
    }

    // 跳轉(zhuǎn)到消息列表
    @GetMapping("/")
    public ModelAndView index(){
        return new ModelAndView("redirect:/messages");
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(SampleWebUiApplication.class, args);
    }
}

編寫布局頁面

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
<head>
<title>Layout</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"
    href="../../css/bootstrap.min.css" />
    <link rel="icon" th:href="@{/favicon.jpg}" href="favicon.jpg" />
</head>
<body>
    <div class="container">
        <div class="navbar">
            <div class="navbar-inner">
                <a class="brand" th:href="@{/messages}" href="#">Thymeleaf - Layout</a>
                <ul class="nav">
                    <li><a th:href="@{/messages}" href="messages.html"> Messages </a></li>
                </ul>
            </div>
        </div>
        <h1 layout:fragment="header">Layout</h1>
        <div layout:fragment="content">Fake content</div>
    </div>
</body>
</html>

編寫列表頁面

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
    layout:decorator="layout">
<head>
<title>Messages : View all</title>
</head>
<body>
    <h1 layout:fragment="header">Messages : View all</h1>
    <div layout:fragment="content" class="container">
        <div class="pull-right">
            <a href="form.html" th:href="@{/messages/(form)}">Create Message</a>
        </div>
        <table class="table table-bordered table-striped">
            <!-- 使用下面注解類解決IntelliJ提示問題 -->
            <!--/*@thymesVar id="messages" type="java.util.List"*/-->
            <!--/*@thymesVar id="message" type="sample.web.ui.Message"*/-->
            <thead>
                <tr>
                    <td>ID</td>
                    <td>Created</td>
                    <td>Summary</td>
                </tr>
            </thead>
            <tbody>
                <tr th:if="${messages.isEmpty()}">
                    <td colspan="3">No messages</td>
                </tr>
                <tr th:each="message : ${messages}">
                    <td th:text="${message.id}">1</td>
                    <td th:text="${#calendars.format(message.created)}">July 11,
                        2012 2:17:16 PM CDT</td>
                    <td><a href="view.html" th:href="@{'/messages/' + ${message.id}}"
                        th:text="${message.summary}"> The summary </a></td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>

編寫增加頁面

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
    layout:decorator="layout">
<head>
<title>Messages : Create</title>
</head>
<body>
    <h1 layout:fragment="header">Messages : Create</h1>
    <div layout:fragment="content" class="container">
        <!-- 使用下面注解類解決IntelliJ提示問題 -->
        <!--/*@thymesVar id="message" type="sample.web.ui.Message"*/-->
        <form id="messageForm" th:action="@{/messages/(form)}" th:object="${message}"
            action="#" method="post">
            <div th:if="${#fields.hasErrors('*')}" class="alert alert-error">
                <p th:each="error : ${#fields.errors('*')}" th:text="${error}">
                    Validation error</p>
            </div>
            <div class="pull-right">
                <a th:href="@{/messages}" href="messages.html"> Messages </a>
            </div>
            <input type="hidden" th:field="*{id}"
                th:class="${#fields.hasErrors('id')} ? 'field-error'" /> <label
                for="summary">Summary</label> <input type="text"
                th:field="*{summary}"
                th:class="${#fields.hasErrors('summary')} ? 'field-error'" /> <label
                for="text">Message</label>
            <textarea th:field="*{text}"
                th:class="${#fields.hasErrors('text')} ? 'field-error'"></textarea>
            <div class="form-actions">
                <input type="submit" value="Save" />
            </div>
        </form>
    </div>
</body>
</html>

編寫詳情頁面

<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
    layout:decorator="layout">
<head>
<title>Messages : View</title>
</head>
<body>
<!-- 使用下面注解類解決IntelliJ提示問題 -->
<!--/*@thymesVar id="message" type="sample.web.ui.Message"*/-->
    <h1 layout:fragment="header">Messages : Create</h1>
    <div layout:fragment="content" class="container">
        <!--/*@thymesVar id="globalMessage" type=""*/-->
        <div class="alert alert-success" th:if="${globalMessage}"
            th:text="${globalMessage}">Some Success message</div>
        <div class="pull-right">
            <a th:href="@{/messages}" href="list.html"> Messages </a>
        </div>
        <dl>
            <dt>ID</dt>
            <dd id="id" th:text="${message.id}">123</dd>
            <dt>Date</dt>
            <dd id="created" th:text="${#calendars.format(message.created)}">
                July 11, 2012 2:17:16 PM CDT</dd>
            <dt>Summary</dt>
            <dd id="summary" th:text="${message.summary}">A short summary...
            </dd>
            <dt>Message</dt>
            <dd id="text" th:text="${message.text}">A detailed message that
                is longer than the summary.</dd>
        </dl>
        <div class="pull-left">
            <a href="messages" th:href="@{'/messages/delete/' + ${message.id}}">
                delete </a> | <a href="form.html"
                th:href="@{'/messages/modify/' + ${message.id}}"> modify </a>
        </div>
    </div>
</body>
</html>

Spring 設(shè)置 application.properties

spring.thymeleaf.cache=false
server.tomcat.basedir=target/tomcat
server.tomcat.accesslog.enabled=true

編寫日志文件 logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
</configuration>

bootstrap v2.0 請到官網(wǎng)下載

到這里所有的類都編寫完了,讓我們來用用看吧

讓我們打開瀏覽器地址欄訪問
http://localhost:8080/

演示

你的運行結(jié)果對了嗎?

更多精彩內(nèi)容

架構(gòu)實戰(zhàn)篇(一):Spring Boot 整合MyBatis
架構(gòu)實戰(zhàn)篇(二):Spring Boot 整合Swagger2
架構(gòu)實戰(zhàn)篇(三):Spring Boot 整合MyBatis(二)
架構(gòu)實戰(zhàn)篇(四):Spring Boot 整合 Thymeleaf
架構(gòu)實戰(zhàn)篇(五):Spring Boot 表單驗證和異常處理
架構(gòu)實戰(zhàn)篇(六):Spring Boot RestTemplate的使用

關(guān)注我們

如果需要源碼可以關(guān)注“IT實戰(zhàn)聯(lián)盟”公眾號并留言(源碼名稱+郵箱),小萌看到后會聯(lián)系作者發(fā)送到郵箱,也可以加入交流群和作者互撩哦~~~!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容