SpringBoot整合Thymeleaf的方法
1. 問(wèn)題需求分析
- 在做樂(lè)優(yōu)商城時(shí),頁(yè)面是通過(guò)Thymeleaf模板引擎渲染后返回到客戶(hù)端。當(dāng)商品詳情頁(yè)數(shù)據(jù)渲染時(shí),在后臺(tái)需要大量的數(shù)據(jù)查詢(xún),而后渲染得到HTML頁(yè)面。在用戶(hù)訪問(wèn)量大的情況下會(huì)對(duì)數(shù)據(jù)庫(kù)造成壓力,并且請(qǐng)求的響應(yīng)時(shí)間過(guò)長(zhǎng),并發(fā)能力不高。
如何解決?
- 一般我們優(yōu)先會(huì)考慮使用緩存技術(shù),比如 Redis 分布式緩存,Guava 本地緩存等。然而 Redis 只適合數(shù)據(jù)規(guī)模比較小的情況,假如數(shù)據(jù)量比較大,例如商品詳情頁(yè),每個(gè)頁(yè)面如果10kb,100萬(wàn)商品,就是10GB空間,對(duì)內(nèi)存占用比較大。此時(shí)就給緩存系統(tǒng)帶來(lái)極大壓力,如果緩存崩潰,接下來(lái)倒霉的就是數(shù)據(jù)庫(kù)了。
- 所以緩存并不是萬(wàn)能的,某些場(chǎng)景需要其它技術(shù)來(lái)解決,比如靜態(tài)化技術(shù)。
2. 什么是靜態(tài)化?
- 靜態(tài)化是指把動(dòng)態(tài)生成的HTML頁(yè)面變?yōu)?strong>靜態(tài)內(nèi)容保存,以后用戶(hù)的請(qǐng)求到來(lái),直接訪問(wèn)靜態(tài)頁(yè)面,不再經(jīng)過(guò)服務(wù)的渲染。
- 而靜態(tài)的HTML頁(yè)面可以部署在nginx中,從而大大提高并發(fā)能力,減小tomcat壓力。
3. 如何實(shí)現(xiàn)靜態(tài)化?
目前,靜態(tài)化頁(yè)面都是通過(guò)模板引擎來(lái)生成,而后保存到nginx服務(wù)器來(lái)部署。常用的模板引擎比如:
- Freemarker
- Velocity
- Thymeleaf
樂(lè)優(yōu)項(xiàng)目中使用的是Thymeleaf 模板引擎!
4. Thymeleaf實(shí)現(xiàn)靜態(tài)化
4.1 概念介紹
Thymeleaf 模板引擎實(shí)現(xiàn)頁(yè)面靜態(tài)化的幾個(gè)概念介紹:
- Context:運(yùn)行上下文
- TemplateResolver:模板解析器
- TemplateEngine:模板引擎
Context:
Thymeleaf 模板中的上下文: 用來(lái)保存模型數(shù)據(jù),當(dāng)模板引擎渲染時(shí),可以從Context上下文中獲取數(shù)據(jù)用于渲染。
當(dāng)與Spring Boot結(jié)合使用時(shí),我們放入Model的數(shù)據(jù)就會(huì)被處理到Context,作為模板渲染的數(shù)據(jù)使用。
TemplateResolver:
Thymeleaf 模板中的模板解析器:用來(lái)讀取模板相關(guān)的配置,例如:模板存放的位置信息,模板文件名稱(chēng),模板文件的類(lèi)型等等。
當(dāng)與SpringBoot結(jié)合時(shí),TemplateResolver已經(jīng)由其創(chuàng)建完成,并且各種配置也都有默認(rèn)值,比如模板存放位置,其默認(rèn)值就是:templates。比如模板文件類(lèi)型,其默認(rèn)值就是html。
TemplateEngine:
Thymeleaf 模板中的模板引擎:用來(lái)解析模板的引擎,需要使用到上下文、模板解析器。分別從兩者中獲取模板中需要的數(shù)據(jù),模板文件。然后利用內(nèi)置的語(yǔ)法規(guī)則解析,從而輸出解析后的文件。來(lái)看下模板引擎進(jìn)行處理的函數(shù):
templateEngine.process("模板名", context, writer);
三個(gè)參數(shù):
- 模板名稱(chēng)
- 上下文:里面包含模型數(shù)據(jù)
- writer:輸出目的地的流
在輸出時(shí),我們可以指定輸出的目的地,如果目的地是Response的流,那就是網(wǎng)絡(luò)響應(yīng)。如果目的地是本地文件,那就實(shí)現(xiàn)靜態(tài)化了。
而在SpringBoot中已經(jīng)自動(dòng)配置了模板引擎,因此不需要關(guān)心這個(gè)。我們做靜態(tài)化,就是把輸出的目的地改成本地文件即可!
4.2 具體實(shí)現(xiàn)
GoodsHtmlService
/** * @Auther: csp1999 * @Date: 2020/12/16/19:43 * @Description: 生成HTML頁(yè)面的Service */ @Service public class GoodsHtmlService { // themleaf 模板引擎 @Autowired private TemplateEngine templateEngine; @Autowired private GoodsService goodsService; /** * 根據(jù)spuId 將對(duì)應(yīng)的商品詳情頁(yè)面生成HTML靜態(tài)頁(yè)面 * * @param spuId */ public void createHTML(Long spuId) { // 初始化運(yùn)行上下文: org.thymeleaf.context 包下 Context context = new Context(); // 設(shè)置數(shù)據(jù)模板 // 從goodsService.loadData(spuId) 方法中獲取模板頁(yè)面中需要渲染的數(shù)據(jù) context.setVariables(goodsService.loadData(spuId)); // 獲取輸出流 // 將需要輸出的html 文件地址設(shè)置為服務(wù)器中的nginx 目錄下的html 目錄中的item文件夾下(我這里nginx 部署的是win10本地?。? File file = new File( "M:\\note\\MyProjects\\leyou\\tools\\nginx-1.14.0\\html\\item\\" + spuId + ".html"); PrintWriter writer = null; try { writer = new PrintWriter(file); // 模板引擎生成靜態(tài)html 頁(yè)面 templateEngine.process("item", context, writer); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { // 判斷并關(guān)閉流 if (writer != null) { writer.close(); } } } /** * 新建線程處理頁(yè)面靜態(tài)化 * * @param spuId */ public void asyncExcute(Long spuId) { ThreadUtils.execute(() -> createHTML(spuId)); /*ThreadUtils.execute(new Runnable() { @Override public void run() { createHtml(spuId); } });*/ } }
多線程工具類(lèi)ThreadUtils
/** * @Auther: csp1999 * @Date: 2020/12/16/20:18 * @Description: 多線程工具類(lèi) */ public class ThreadUtils { private static final ExecutorService es = Executors.newFixedThreadPool(10); public static void execute(Runnable runnable) { es.submit(runnable); } }
GoodsController
在調(diào)用GoodsController訪問(wèn)商品頁(yè)面的同時(shí)生成緩存的頁(yè)面:
/** * @Auther: csp1999 * @Date: 2020/12/16/10:00 * @Description: */ @Controller public class GoodsController { @Autowired private GoodsService goodsService; @Autowired private GoodsHtmlService goodsHtmlService; /** * 商品詳情也頁(yè)面 * * @param id * @param model * @return */ @GetMapping("/item/{id}.html") public String toItemPage(@PathVariable("id") Long id, Model model) { Map<String, Object> map = goodsService.loadData(id); model.addAllAttributes(map); // 頁(yè)面靜態(tài)化:調(diào)用createHTML(id) 方法生成靜態(tài)頁(yè)面并保存到服務(wù)器的nginx 對(duì)應(yīng)目錄下緩存 // 通過(guò)多線程方式: goodsHtmlService.asyncExcute(id); // goodsHtmlService.createHTML(id); return "/item"; } }
注意:生成html 的代碼不能對(duì)用戶(hù)請(qǐng)求產(chǎn)生影響,所以這里我們使用額外的線程進(jìn)行異步創(chuàng)建。
5. nginx 中進(jìn)行訪問(wèn)配置
# 前端前臺(tái)相關(guān)配置 server { listen 80; server_name www.leyou.com; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 如果是以item 開(kāi)頭的路徑,代理到前臺(tái)8084端口 # eg: www.leyou.com/item/4586.html location /item { # 先到本地nginx 路徑下的html文件夾 中找頁(yè)面,如果有則走該頁(yè)面(相當(dāng)于緩存效果) root html; if (!-f $request_filename) { # 如果請(qǐng)求的文件不存在,就反向代理,通過(guò)請(qǐng)求去訪問(wèn)該頁(yè)面 proxy_pass http://127.0.0.1:8084; break; } } # 代理到前臺(tái)9002端口 location / { proxy_pass http://127.0.0.1:9002; proxy_connect_timeout 600; proxy_read_timeout 600; } }
nginx 代理的流程圖:
6. 訪問(wèn)頁(yè)面測(cè)試
第一次訪問(wèn)頁(yè)面:
nginx路徑下的html/item/下生成了該頁(yè)面的靜態(tài)資源:
第二次訪問(wèn)該頁(yè)面:
總結(jié)
本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
- SpringBoot詳細(xì)講解視圖整合引擎thymeleaf
- Springboot詳解如何整合使用Thymeleaf
- springboot+thymeleaf整合阿里云OOS對(duì)象存儲(chǔ)圖片的實(shí)現(xiàn)
- springboot整合shiro之thymeleaf使用shiro標(biāo)簽的方法
- SpringBoot整合thymeleaf 報(bào)錯(cuò)的解決方案
- springboot2.1.7整合thymeleaf代碼實(shí)例
- Spring Boot 整合 Shiro+Thymeleaf過(guò)程解析
- Spring Boot整合Thymeleaf詳解
相關(guān)文章
Java實(shí)現(xiàn)把兩個(gè)數(shù)組合并為一個(gè)的方法總結(jié)
這篇文章主要介紹了Java實(shí)現(xiàn)把兩個(gè)數(shù)組合并為一個(gè)的方法,結(jié)合實(shí)例形式總結(jié)分析了java常用的四種數(shù)組合并操作技巧,需要的朋友可以參考下2017-12-12java根據(jù)方法名稱(chēng)取得反射方法的參數(shù)類(lèi)型示例
利用java反射原理調(diào)用方法時(shí),常先需要傳入方法參數(shù)數(shù)組才能取得方法。該方法參數(shù)數(shù)組采用動(dòng)態(tài)取得的方式比較合適2014-02-02解決JDK異常處理No appropriate protocol問(wèn)題
這篇文章主要介紹了解決JDK異常處理No appropriate protocol問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06Java反射之通過(guò)反射獲取一個(gè)對(duì)象的方法信息(實(shí)例代碼)
下面小編就為大家?guī)?lái)一篇Java反射之通過(guò)反射獲取一個(gè)對(duì)象的方法信息(實(shí)例代碼)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10Java同步關(guān)鍵字synchronize底層實(shí)現(xiàn)原理解析
synchronized關(guān)鍵字對(duì)大家來(lái)說(shuō)并不陌生,當(dāng)我們遇到并發(fā)情況時(shí),優(yōu)先會(huì)想到用synchronized關(guān)鍵字去解決,synchronized確實(shí)能夠幫助我們?nèi)ソ鉀Q并發(fā)的問(wèn)題,接下來(lái)通過(guò)本文給大家分享java synchronize底層實(shí)現(xiàn)原理,感興趣的朋友一起看看吧2021-08-08Java中Date類(lèi)和Calendar類(lèi)的常用實(shí)例小結(jié)
這篇文章主要介紹了Java中Date類(lèi)和Calendar類(lèi)的常用實(shí)例小結(jié),是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí)的運(yùn)用,需要的朋友可以參考下2015-08-08基于java TCP網(wǎng)絡(luò)通信的實(shí)例詳解
本篇文章是對(duì)java中TCP網(wǎng)絡(luò)通信的實(shí)例進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05Java中使用數(shù)組實(shí)現(xiàn)棧數(shù)據(jù)結(jié)構(gòu)實(shí)例
這篇文章主要介紹了Java中使用數(shù)組實(shí)現(xiàn)棧數(shù)據(jù)結(jié)構(gòu)實(shí)例,本文先是講解了實(shí)現(xiàn)棧至少應(yīng)該包括以下幾個(gè)方法等知識(shí),然后給出代碼實(shí)例,需要的朋友可以參考下2015-01-01Java中Collections.emptyList()的注意事項(xiàng)
這篇文章主要給大家介紹了關(guān)于Java中Collections.emptyList()的注意事項(xiàng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03