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>

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM