Spring Boot 使用模板引擎開發 Web 項目
1. 前言
模板引擎這個詞,咋聽起來,有點高大上的意味。
實際上,模板引擎是非常平易近人的技術。譬如大家可能都比較熟悉的 JSP ,就是一種比較典型的模板引擎。
當瀏覽器將請求拋給控制器,控制器處理好數據后,就跳轉 JSP 等模板引擎頁面。注意在跳轉的同時,還會將數據組裝好,也交給模板引擎處理。
模板引擎會根據數據,和模板引擎的規則,動態生成 HTML 頁面,最后返回給瀏覽器顯示。
2. 模板引擎使用場景
我們使用 Spring Boot 開發 Web 項目,大體上有兩種方式。
第一種方式,是后端服務化的方式,也是當前的主流方式。前端是靜態的 HTML 頁面,通過 Ajax 請求 Spring Boot 的后端接口。 Spring Boot 返回數據一般采用 JSON 格式,前端接收后將數據顯示。
第二種方式,是采取模板引擎的方式。前端的請求,到達 Spring Boot 的控制器后,控制器處理請求,然后將返回數據交給模板引擎。模板引擎負責根據數據生成 HTML 頁面,最后將 HTML 返回給瀏覽器。
我個人比較推薦第一種方式,說一下該方式的幾個優點:
- 便于分工協作:后端可以按自己的進度開發接口,前端可以開發頁面,需要的時候直接調用后端 API ;
- 便于項目拓展:比如前期是做的網站,后續要加一個 APP ,后端接口可以直接復用;
- 降低服務端壓力:后端只提供數據,一部分業務邏輯在前端處理了。服務端要做的事情少了,自然壓力就小。
本篇是講模板引擎,也得說說模板引擎的優點,王婆賣瓜不能光夸草莓啊。模板引擎開發的頁面,對搜索引擎 SEO 比較友好;還有就是簡單的頁面,如果用模板引擎開發速度比較快,畢竟模板化的方法,目的就是減少重復提高效率。
3. Spring Boot 中常用的模板引擎
Spring Boot 支持的模板引擎種類很多,常見的有 FreeMarker 、 Thymeleaf 、 JSP 。
因為這些模板引擎使用的用戶都不少,所以我們逐一介紹下其實現過程。
至于孰優孰劣,請各位看官自行評價。正所謂:尺有所短,寸有所長,各取所愛,萬物生長!
4. 整體流程說明
本篇我們開發一個商品瀏覽項目實例。
此處說一個我個人的經驗:在做一個項目或一個模塊的時候,不要一開始就動手寫代碼,最好是謀定而后動。
我們作為程序員,實際上是整個程序世界的總指揮。應該先整體規劃,再實現局部。這種總分型的開發方法便于我們理順思路,提高編碼效率!
好的,我們來思考下,實現商品瀏覽項目實例的整體流程:
可以看到,我們是先建立了控制器方法和頁面,再去實現其中的具體細節。這樣可以讓我們的思維保持連貫性和整體性,在做一些頁面和方法較多的項目時,會感覺更加順暢。
5. 使用 FreeMarker
我們按整體流程,使用 FreeMarker 模板引擎,來實現商品瀏覽功能。
5.1 創建 Spring Boot 項目并導入開發環境
使用 Spring Initializr 創建項目,Spring Boot 版本選擇 2.2.5 , Group 為 com.imooc
, Artifact 為 spring-boot-freemarker
,生成項目后導入 Eclipse 開發環境。
5.2 在 pom.xml 中引入相關依賴
引入 Web 項目及 FreeMarker 模板相關的依賴項,代碼如下:
實例:
<!-- 引入web項目相關依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- freemarker -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
5.3 創建控制器方法,指向商品頁面
創建控制器類,由于是商品相關的控制器,所以命名為 GoodsController ,代碼如下:
實例:
/**
* 商品控制器
*/
@Controller // 標注為控制器
public class GoodsController {
/**
* 獲取商品列表
*/
@RequestMapping("/goods") // 請求路徑
public String goods() {
return "goods";// 跳轉到goods.ftl頁面
}
}
我們具體解釋下該類的作用。
- @Controller 注解標注在 GoodsController 類上,會為該類注冊一個控制器組件,放入 Spring 容器中。該組件具備處理請求的能力,其中的方法可以響應 HTTP 請求;
- @RequestMapping ("/goods") 注解標注在方法 goods () 上,所以請求路徑如果匹配
/goods
,則由該方法進行處理; - 返回值是字符串
"goods"
,由于我們已經引入 FreeMarker 依賴,所以該返回值會跳轉到 goods.ftl 頁面。
Tips: 注意需要在 application.properties 文件中設置模板文件的后綴,即:
spring.freemarker.suffix=.ftl
。如果不添加該配置,直接return "goods.ftl";
會報錯。
5.4 創建商品頁面
我們 resource/templates
目錄下新建商品頁面 goods.ftl
,先不必實現具體功能,代碼如下:
實例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品列表</title>
</head>
<body>
商品列表
</body>
</html>
此時我們啟動項目,然后訪問 http://127.0.0.1:8080/goods
,即可顯示對應頁面內容。
5.5 在控制器方法中,調用服務方法獲取商品信息,并將信息交給模板引擎處理
定義商品類 GoodsDo 用來描述商品信息,注意 Do 表示數據模型對象(Data Object),代碼如下:
實例:
/**
* 商品數據對象
*/
public class GoodsDo {
/**
* 商品名稱
*/
private String name;
/**
* 商品價格
*/
private String price;
/**
* 商品圖片
*/
private String pic;
// 省略get set方法
}
然后我們編寫服務類 GoodsService ,提供獲取商品列表的方法。注意此處僅僅是演示模板引擎,并不需要訪問數據庫,直接返回一個指定內容的商品列表。
實例:
/**
* 商品服務
*/
@Service // 為GoodsService注冊一個組件
public class GoodsService {
public List<GoodsDo> getGoodsList() {
List<GoodsDo> list = new ArrayList<GoodsDo>();
GoodsDo goods = new GoodsDo();
goods.setName("蘋果");
goods.setPic("apple.jpg");
goods.setPrice("3.5");
list.add(goods);
return list;
}
}
此時,我們的控制器就可以注入 GoodsService 類型的組件,然后調用其方法了。
實例:
@Controller
public class GoodsController {
@Autowired
private GoodsService goodsService;// 自動裝配
@RequestMapping("/goods") // 請求路徑
public String goods(Model model) {
model.addAttribute("goodsList", goodsService.getGoodsList());// 交給模板引擎處理的數據
return "goods";// 跳轉到goods.ftl頁面
}
}
注意 model.addAttribute("goodsList", goodsService.getGoodsList());
,我們將商品列表相關的數據交給模板引擎去處理。
5.6 在商品頁面通過模板引擎規則顯示商品信息
此時我們可以根據 FreeMarker 模板引擎,按模板規則顯示商品信息了。
實例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品列表</title>
</head>
<body>
<div>商品列表:</div>
<#list goodsList as item>
${item.name}--${item.price}--${item.pic}
</#list>
</body>
</html>
注意我們通過 FreeMarker 的模板語法,輸出了商品列表信息。關于 FreeMarker 模板引擎更多的語法規則,感興趣的同學可以后續查閱更多資料。
5.7 測試
啟動項目,打開瀏覽器訪問 http://127.0.0.1:8080/goods
,即可查看輸出結果。
6. 使用 Thymeleaf
Thymeleaf 和 FreeMarker ,都是模板引擎,使用方法基本類似。此處我們僅僅是給出一個范例,不再做過多的解釋。
6.1 創建 Spring Boot 項目并導入開發環境
使用 Spring Initializr 創建項目, Spring Boot 版本選擇 2.2.5 , Group 為 com.imooc
, Artifact 為 spring-boot-thymeleaf
,生成項目后導入 Eclipse 開發環境。
6.2 在 pom.xml 中引入相關依賴
引入 Web 項目及 Thymeleaf 模板相關的依賴項。
實例:
<!-- 引入web項目相關依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- ThymeLeaf依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
6.3 創建控制器方法,指向商品頁面
創建控制器類, GoodsController , Thymeleaf 直接使用 HTML 作為模板頁面,故代碼如下:
實例:
/**
* 商品控制器
*/
@Controller // 標注為控制器
public class GoodsController {
/**
* 獲取商品列表
*/
@RequestMapping("/goods") // 請求路徑
public String goods() {
return "goods.html";// 跳轉到goods.html頁面
}
}
6.4 創建商品頁面
我們在 resource/templates
目錄下新建商品頁面 goods.html
,先不必實現具體功能,代碼如下:
實例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品列表</title>
</head>
<body>
商品列表
</body>
</html>
此時我們啟動項目,然后訪問 http://127.0.0.1:8080/goods
,即可顯示對應頁面內容。
6.5 在控制器方法中,調用服務方法獲取商品信息,并將信息交給模板引擎處理
商品類 GoodsDo ,服務類 GoodsService ,這兩個類與上面沒有區別直接放出代碼。
實例:
/**
* 商品數據對象
*/
public class GoodsDo {
/**
* 商品名稱
*/
private String name;
/**
* 商品價格
*/
private String price;
/**
* 商品圖片
*/
private String pic;
// 省略get set方法
}
實例:
/**
* 商品服務
*/
@Service // 為GoodsService注冊一個組件
public class GoodsService {
public List<GoodsDo> getGoodsList() {
List<GoodsDo> list = new ArrayList<GoodsDo>();
GoodsDo goods = new GoodsDo();
goods.setName("蘋果");
goods.setPic("apple.jpg");
goods.setPrice("3.5");
list.add(goods);
return list;
}
}
好的,此時我們的控制器就可以注入 GoodsService 類型的組件,然后調用其方法了。
實例:
@Controller
public class GoodsController {
@Autowired
private GoodsService goodsService;// 自動裝配
@RequestMapping("/goods") // 請求路徑
public String goods(Model model) {
model.addAttribute("goodsList", goodsService.getGoodsList());// 交給模板引擎處理的數據
return "goods.html";// 跳轉到goods.html頁面
}
}
6.6 在商品頁面通過模板引擎規則顯示商品信息
此時我們可以根據 Thymeleaf 模板引擎,按模板規則顯示商品信息了。
實例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品列表</title>
</head>
<body>
<div>商品列表:</div>
<div th:each="item:${goodsList}">
<span th:text="${item.name}"></span>
<span th:text="${item.price}"></span>
<span th:text="${item.pic}"></span>
</div>
</body>
</html>
注意我們通過 Thymeleaf 的模板語法,輸出了商品列表信息。關于 Thymeleaf 模板引擎更多的語法規則,感興趣的同學可以后續查閱更多資料。
6.7 測試
啟動項目,打開瀏覽器訪問 http://127.0.0.1:8080/goods
,即可查看輸出結果。
到此,大家基本上也能發現,這兩種方式除了模板頁面文件內容不同,其他地方基本都是一模一樣的。
也就是說,模板引擎主要負責通過一些模板標簽,將控制器返回的數據解析為網頁。
7. 使用 JSP
注意 Spring Boot 官方已經不推薦使用 JSP 了,確實操作起來也比較麻煩。但是由于 JSP 用戶體量還是比較大的,所以此處還是簡單演示下,開發步驟與 FreeMarker / Thymeleaf 基本一致。
7.1 創建 Spring Boot 項目并導入開發環境
使用 Spring Initializr 創建項目, Spring Boot 版本選擇 2.2.5 , Group 為 com.imooc
, Artifact 為 spring-boot-jsp
,生成項目后導入 Eclipse 開發環境。
7.2 在 pom.xml 中引入相關依賴
引入 Web 項目及 JSP 模板相關的依賴項。
實例:
<!-- 添加web開發功能 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--內嵌的tomcat支持模塊 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<!-- 對jstl的支持 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
7.3 創建控制器方法,指向商品頁面
創建控制器類, GoodsController ,代碼如下:
實例:
/**
* 商品控制器
*/
@Controller // 標注為控制器
public class GoodsController {
/**
* 獲取商品列表
*/
@RequestMapping("/goods") // 請求路徑
public String goods() {
return "goods";// 跳轉到goods.jsp頁面
}
}
7.4 創建商品頁面
手工添加 src/main/webapp
及子目錄如下,同時目錄下放一個 goods.jsp 用于測試。注意該目錄是一個 Source Folder 源代碼目錄,不是普通文件夾目錄。
實例:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品列表</title>
</head>
<body>商品列表
</body>
</html>
注意,我們還需要添加一個視圖解析器,實現 JSP 頁面往指定目錄跳轉。
實例:
@SpringBootApplication
public class SpringBootJspApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootJspApplication.class, args);
}
@Bean // 注冊視圖解析器
public InternalResourceViewResolver setupViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");// 自動添加前綴
resolver.setSuffix(".jsp");// 自動添加后綴
return resolver;
}
}
此時我們啟動項目,然后訪問 http://127.0.0.1:8080/goods
,即可顯示對應頁面內容。
7.5 在控制器方法中,調用服務方法獲取商品信息,并將信息交給模板引擎處理
商品類 GoodsDo ,服務類 GoodsService ,這兩個類與上面沒有區別直接放出代碼。
實例:
/**
* 商品數據對象
*/
public class GoodsDo {
/**
* 商品名稱
*/
private String name;
/**
* 商品價格
*/
private String price;
/**
* 商品圖片
*/
private String pic;
// 省略get set方法
}
實例:
/**
* 商品服務
*/
@Service // 為GoodsService注冊一個組件
public class GoodsService {
public List<GoodsDo> getGoodsList() {
List<GoodsDo> list = new ArrayList<GoodsDo>();
GoodsDo goods = new GoodsDo();
goods.setName("蘋果");
goods.setPic("apple.jpg");
goods.setPrice("3.5");
list.add(goods);
return list;
}
}
好的,此時我們的控制器就可以注入 GoodsService 類型的組件,然后調用其方法了。
實例:
@Controller
public class GoodsController {
@Autowired
private GoodsService goodsService;// 自動裝配
@RequestMapping("/goods") // 請求路徑
public String goods(Model model) {
model.addAttribute("goodsList", goodsService.getGoodsList());// 交給模板引擎處理的數據
return "goods";// 跳轉到goods.jsp
}
}
7.6 在商品頁面通過模板引擎規則顯示商品信息
此時我們可以根據 JSP 模板引擎,按模板規則顯示商品信息了。
實例:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品列表</title>
</head>
<body>
<div>商品列表:</div>
<c:forEach var="item" items="${goodsList}">
${item.name}--${item.price}--${item.pic}
</c:forEach>
</body>
</html>
注意我們通過 JSP 的模板語法,輸出了商品列表信息。關于 JSP 模板引擎更多的語法規則,感興趣的同學可以后續查閱更多資料。
7.7 測試
啟動項目,打開瀏覽器訪問 http://127.0.0.1:8080/goods
,即可查看輸出結果。
8. 小結
最后大家應該也發現了, FreeMarker 和 Thymeleaf 的用法幾乎是一模一樣的,而 JSP 還需要手工添加一些目錄和配置。
三種方式各有優劣, FreeMarker 模板語法比較簡潔, Thymeleaf 可以直接使用 HTML 作為模板文件, JSP 用戶群體廣泛。
但是三種方式,都是一種模板引擎而已,將控制器返回數據轉化為 HTML 頁面顯示,本質上沒啥區別,大家對模板引擎有一個了解即可。