一、简介
站点搭建完成后,编写页面时一般会有如下几个需求
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>
好,到此这个解决方案就完成了,如有什么不足还望指点