1. 程式人生 > >IDEA專案搭建十四——Web站點Controller基類及佈局頁靜態資源設計

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>

好,到此這個解決方案就完成了,如有什麼不足還望指點