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

前言

Thymeleaf 是一種模板語(yǔ)言古胆。那模板語(yǔ)言或模板引擎是什么甩骏?常見(jiàn)的模板語(yǔ)言都包含以下幾個(gè)概念:數(shù)據(jù)(Data)揖庄、模板(Template)、模板引擎(Template Engine)和結(jié)果文檔(Result Documents)林束。

Spring boot 支持多種模板語(yǔ)言(Thymeleaf 像棘、Freemarker、Mustache壶冒、Groovy Templates)
Thymeleaf 跟大部分的模板語(yǔ)言類(lèi)似缕题,上手容易,使用簡(jiǎn)單

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

項(xiàng)目結(jié)構(gòu)

最終運(yùn)行結(jié)果

最終運(yùn)行結(jié)果

下面開(kāi)始一步一步的編寫(xiě)代碼了

增加Spring boot的maven 依賴(lài)

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

<?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>

增加一個(gè)消息類(lèi)

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

public class Message {

    private Long id;

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

    // 編寫(xiě)不能為空的提示語(yǔ)
    @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 {

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

    // 用來(lái)存儲(chǔ)消息
    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) {
            // 生成一個(gè)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);
    }
}

編寫(xiě)控制層代碼

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)入消息列表頁(yè)面
    @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)建消息頁(yè)面
    @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)容驗(yàn)證
        if (result.hasErrors()) {
            return new ModelAndView("messages/form", "formErrors", result.getAllErrors());
        }
        // 保存消息
        message = this.messageRepository.save(message);
        // 重定向增加一個(gè)消息
        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)入修改消息頁(yè)面
    @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)存管理消息實(shí)例
    @Bean
    public MessageRepository messageRepository() {
        return new InMemoryMessageRepository();
    }

    // 自定義類(lèi)型轉(zhuǎn)換胖腾,Controller 入?yún)⒆址D(zhuǎn)換為 Message 類(lèi)型
    @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);
    }
}

編寫(xiě)布局頁(yè)面

<!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>

編寫(xiě)列表頁(yè)面

<!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">
            <!-- 使用下面注解類(lèi)解決IntelliJ提示問(wèn)題 -->
            <!--/*@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>

編寫(xiě)增加頁(yè)面

<!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">
        <!-- 使用下面注解類(lèi)解決IntelliJ提示問(wèn)題 -->
        <!--/*@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>

編寫(xiě)詳情頁(yè)面

<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>
<!-- 使用下面注解類(lèi)解決IntelliJ提示問(wèn)題 -->
<!--/*@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

編寫(xiě)日志文件 logback.xml

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

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

到這里所有的類(lèi)都編寫(xiě)完了烟零,讓我們來(lái)用用看吧

讓我們打開(kāi)瀏覽器地址欄訪問(wèn)
http://localhost:8080/

演示

你的運(yùn)行結(jié)果對(duì)了嗎?

更多精彩內(nèi)容

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

關(guān)注我們

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末性宏,一起剝皮案震驚了整個(gè)濱河市群井,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌毫胜,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诬辈,死亡現(xiàn)場(chǎng)離奇詭異酵使,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)焙糟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)口渔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人穿撮,你說(shuō)我怎么就攤上這事缺脉』居” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵攻礼,是天一觀的道長(zhǎng)业踢。 經(jīng)常有香客問(wèn)我,道長(zhǎng)礁扮,這世上最難降的妖魔是什么知举? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮太伊,結(jié)果婚禮上雇锡,老公的妹妹穿的比我還像新娘。我一直安慰自己僚焦,他們只是感情好锰提,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著芳悲,像睡著了一般立肘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上芭概,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天赛不,我揣著相機(jī)與錄音,去河邊找鬼罢洲。 笑死踢故,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惹苗。 我是一名探鬼主播殿较,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼桩蓉!你這毒婦竟也來(lái)了淋纲?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤院究,失蹤者是張志新(化名)和其女友劉穎洽瞬,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體业汰,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伙窃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了样漆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片为障。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鳍怨,到底是詐尸還是另有隱情呻右,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布鞋喇,位于F島的核電站声滥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏确徙。R本人自食惡果不足惜醒串,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鄙皇。 院中可真熱鬧芜赌,春花似錦、人聲如沸伴逸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)错蝴。三九已至洲愤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間顷锰,已是汗流浹背柬赐。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留官紫,地道東北人肛宋。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像束世,于是被迫代替她去往敵國(guó)和親酝陈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,822評(píng)論 6 342
  • 摘要: 原創(chuàng)出處:www.bysocket.com 泥瓦匠BYSocket 希望轉(zhuǎn)載毁涉,保留摘要沉帮,謝謝! Thyme...
    子木聊出海閱讀 2,551評(píng)論 1 50
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理贫堰,服務(wù)發(fā)現(xiàn)穆壕,斷路器,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • 1.0 簡(jiǎn)單介紹Thymeleaf Thymeleaf是一個(gè)java類(lèi)庫(kù)其屏,它是一個(gè)xml/xhtml/html5的...
    曲海峰閱讀 2,585評(píng)論 0 0
  • 我看《你的名字》那天晚上粱檀,哭得稀里嘩啦。 我跟閨蜜講漫玄,大陸上映那天,我就告訴他,喜歡他這件事睦优。啊 渗常,真是,好形式主...
    四夕四夕醬閱讀 610評(píng)論 0 0