IDEA專案搭建十四——Web站點Controller基類及佈局頁靜態資源設計
一、簡介
站點搭建完成後,編寫頁面時一般會有如下幾個需求
1、巢狀靜態頁面時有很大一部分通用程式碼,如css、js這部分可以使用thymeleaf的區域性片段程式碼塊組成
2、這些靜態資源預設放在程式中,但後期可能會為了節省伺服器系統資源做動靜分離,或架在CDN上,所以需要有獨立的靜態資源站點設計,目前我是單獨搭建了nginx做靜態資源站
3、編寫Controller時有一部分的公用程式碼可以提出BaseController基類來提供,簡化子類程式碼,統一程式碼規範
根據這幾個需求,設計如下解決方案
二、程式碼
先看一下我的專案結構,靜態資源站存放純公用的資源,各站點又提供了各自的私有資源存放
1、首先需要動態配置靜態資源站URL,所以我們使用Enum來實現
SysConfigEnum列舉類,其中http://localhost:8800是我搭建nginx做靜態伺服器地址,我們站點多了一點,大家可以簡化
/** * 程式列舉 */ public interface SysConfigEnum { /** * 動態站點地址 */ enum SiteUrl { AdminBase(""), AgentBase(""), AdminFlight(""), AgentFlight(""), AdminHotel(""), AgentHotel(""), private String url; SiteUrl(String value) { this.url = value; } public String getUrl() { return this.url; } } /** * 靜態資源地址 */ enum StaticUrl { Common("http://localhost:8800/content/", "20180801"), PlatformAdmin("http://localhost:8800/content/admin/content/", "20180801"), PlatformAgent("http://localhost:8800/content/agent/content/", "20180801"), ProductBaseAdmin("/admin/content/", "20180801"), ProductBaseAgent("/agent/content/", "20180801"), ProductFlightAdmin("/admin/content/", "20180801"), ProductFlightAgent("/agent/content/", "20180801"), ProductHotelAdmin("/admin/content/", "20180801"), ProductHotelAgent("/agent/content/", "20180801"); private String url; private String ver; StaticUrl(String url, String ver) { this.url = url; this.ver = ver; } public String getUrl() { return this.url; } public String getVer() { return "?v=" + this.ver; } } }
2、有一個Model實體來標明當前站點的靜態資源Url和Ver
StaticModel實體裡面標明瞭Common全域性通用靜態資源,Platform平臺通用靜態資源,Product產品站點內部靜態資源
get、set方法做了一點修改,由於屬性型別是列舉,set直接設定,get時拆分開,便於做擴充套件
/** * 靜態資源實體 */ public class StaticModel { private SysConfigEnum.StaticUrl common; private SysConfigEnum.StaticUrl platform; private SysConfigEnum.StaticUrl product; public void setCommon(SysConfigEnum.StaticUrl common) { this.common = common; } public void setPlatform(SysConfigEnum.StaticUrl platform) { this.platform = platform; } public void setProduct(SysConfigEnum.StaticUrl product) { this.product = product; } public String getCommonUrl() { return this.common.getUrl(); } public String getCommonVer() { return this.common.getVer(); } public String getPlatformUrl() { return this.platform.getUrl(); } public String getPlatformVer() { return this.platform.getVer(); } public String getProductUrl() { return this.product.getUrl(); } public String getProductVer() { return this.product.getVer(); } }
3、靜態資源的資訊準備好後我們要把它寫在每個頁面上,action到page由model來傳遞,那我們就要在每一個action時都設定一下這個model,那我們新建一個BaseController基類來實現
Model、ModelMap、Map<>都是同一個BindingAwareModelMap例項,所以我是用了Model,大家也可以各自更換
在基類中提供request、response、model來給子類使用,簡化子類單獨注入的操作,@ModelAttribute註解的方法,會在每一個action執行之前執行【多個ModelAttribute之間沒有執行順序是亂序的】
import com.ysl.ts.common.StaticModel; import com.ysl.ts.common.SysConfigEnum; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Controller基類Admin * @author TaiYongHai */ public class BaseController { //request是執行緒安全的,可以自動注入 @Autowired private HttpServletRequest request; //response是非執行緒安全的,使用本地執行緒控制 private ThreadLocal<HttpServletResponse> response = new ThreadLocal<>(); //model是非執行緒安全的,使用本地執行緒控制 private ThreadLocal<Model> model = new ThreadLocal<>(); /* HttpServletRequest req HttpServletResponse res Model m action方法中的這些引數Servlet會自動幫你填充 */ /** * 獲取Request * @return */ protected final HttpServletRequest getRequest() { return this.request; } /** * 注入Response * @param res */ @ModelAttribute private void setResponse(HttpServletResponse res) { this.response.set(res); } /** * 獲取Response * @return */ protected final HttpServletResponse getResponse() { return response.get(); } /** * 注入Model * @param m */ @ModelAttribute private void setModel(Model m) { this.model.set(m); } /** * 獲取Model * (Model、ModelMap、Map<>將使用BindingAwareModelMap作為模型物件的實現, * 都是同一個BindingAwareModelMap例項,所以都共享同一份資料) * @return */ protected final Model getModel() { return model.get(); } //@ModelAttribute註解的方法,會在每一個action執行之前執行【多個ModelAttribute之間沒有執行順序是亂序的】 //設定靜態資源引數 @ModelAttribute private void setStaticParams(Model m) { StaticModel staticModel = new StaticModel(); staticModel.setCommon(SysConfigEnum.StaticUrl.Common); staticModel.setPlatform(SysConfigEnum.StaticUrl.PlatformAdmin); staticModel.setProduct(SysConfigEnum.StaticUrl.ProductBaseAdmin); //存入Model中 m.addAttribute("yslTsStatic", staticModel); } }
4、子類繼承BaseController可以直接使用提供的model等物件
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Controller @RequestMapping("/admin/home") @Component("AdminHome") public class HomeController extends BaseController { @RequestMapping("/index") public String index() { //直接使用基類提供的物件 HttpServletRequest request = super.getRequest(); HttpServletResponse response = super.getResponse(); Model model = super.getModel(); return "/admin/home/index"; } }
5、後臺完成了,前臺渲染頁面時的公用部分使用 th:fragment 佈局程式碼塊來實現
<html xmlns:th="http://www.thymeleaf.org"> <!-- header 放在<head>標籤內,並在其下方寫css --> <div th:fragment="header" th:remove="tag"> <div th:replace="~{/admin/layout/fragments :: metaLib}"></div> <div th:replace="~{/admin/layout/fragments :: cssLib}"></div> <div th:replace="~{/admin/layout/fragments :: cssAssist}"></div> </div> <!-- footer 放在<body>標籤內,並在其下方寫js --> <div th:fragment="footer" th:remove="tag"> <div th:replace="~{/admin/layout/fragments :: jsLib}"></div> <div th:replace="~{/admin/layout/fragments :: jsAssist}"></div> </div> <!-- 引入變數包 --> <!--/*@thymesVar id="yslTsStatic" type="com.ysl.ts.common.StaticModel"*/--> <!-- meta資訊 --> <div th:fragment="metaLib" th:remove="tag"> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <meta name="renderer" content="webkit"/> <meta name="Author" content="EB.Group"/> </div> <!-- 引入css --> <div th:fragment="cssLib" th:remove="tag"> <link rel="icon" th:href="@{${yslTsStatic.getCommonUrl()}+'img/favicon.ico'+${yslTsStatic.getCommonVer()}}" type="image/x-icon"/> <link rel="stylesheet" th:href="@{${yslTsStatic.getCommonUrl()}+'css/bootstrap.min.css'+${yslTsStatic.getCommonVer()}}"/> </div> <!-- 全域性css --> <div th:fragment="cssAssist" th:remove="tag"> <style> body { overflow: hidden; } </style> </div> <!-- 引入js --> <div th:fragment="jsLib" th:remove="tag"> <script type="text/javascript" th:src="@{${yslTsStatic.getCommonUrl()}+'js/jquery-1.9.1.min.js'+${yslTsStatic.getCommonVer()}}"></script> <script type="text/javascript" th:src="@{${yslTsStatic.getPlatformUrl()}+'js/ysl-ts-common.js'+${yslTsStatic.getPlatformVer()}}"></script> </div> <!-- 全域性js --> <div th:fragment="jsAssist" th:remove="tag"> <script type="text/javascript"> console.log("jsAssist"); </script> </div> </html>
6、其他頁面引入佈局頁的程式碼塊
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>管理後臺</title> <div th:replace="~{/admin/layout/fragments :: header}"></div> <!-- 從這裡以下寫頁面獨立的css --> </head> <body> <h1>歡迎</h1> <div th:replace="~{/admin/layout/fragments :: footer}"></div> <!-- 每個頁面可以引入自己獨有的js --> <script type="text/javascript" th:src="@{${yslTsStatic.getProductUrl()}+'js/test.js'+${yslTsStatic.getProductVer()}}"></script> <!-- 從這裡以下寫頁面獨立的js --> <script type="text/javascript"> console.log("page js"); </script> </body> </html>
好,到此這個解決方案就完成了,如有什麼不足還望指點