Spring boot开发的web项目打jar包部署时,如果希望template模板及static文件(js/css/img等)能单独更新,可以用下面的方法把它们从jar包分离出来。
工程目录结构调整
把static和template移到resources之外,比如和java目录平级。
1 2 3 4 5 6 7 8
| ├─src │ ├─main │ │ ├─java │ │ ├─resources │ │ │ └─application.properties │ │ ├─static │ │ └─templates │ └─test
|
在 application.properties 分别指定static和template的位置
1 2 3 4
| spring.mvc.static-path-pattern=/static/** spring.resources.static-locations=file:/pathto/yourproject/src/main/static/ spring.thymeleaf.prefix=file:/pathto/yourproject/src/main/templates/
|
html模板里引用static文件的方式
1 2 3
| <link rel="stylesheet" th:href="@{/static/subpath/afile.css}" /> <script th:src="@{/static/subpath/afile.js}"></script> <img th:src="@{/static/subpath/afile.png}"/>
|
注:link favicon不知为何不能加static:(
把static和template合并,方便一起编辑
实践中发现,html模板和对应的静态文件(js,css,images等)分开在两处不方便编辑。尝试把他们合并到同一个文件夹下。首先只要修改下面两个配置都指向一个地方(比如webroot):
1 2
| spring.resources.static-locations=file:/pathto/yourproject/src/main/webroot/ spring.thymeleaf.prefix=file:/pathto/yourproject/src/main/webroot/
|
只是这样会导致html模板也可以被用户直接访问,不太安全。需要调整一下security配置:
- 配置方法configure(WebSecurity webSecurity) :
1 2 3 4 5 6
|
webSecurity.ignoring().antMatchers("/static/**/*.js"); webSecurity.ignoring().antMatchers("/static/**/*.css"); webSecurity.ignoring().antMatchers("/static/**/*.jpg"); webSecurity.ignoring().antMatchers("/static/**/*.png");
|
- 配置方法configure(HttpSecurity httpSecurity):
1 2
| httpSecurity.authorizeRequests().antMatchers("/static/**/*.html").denyAll()
|
Spring boot 处理 error
参考: https://www.cnblogs.com/hyl8218/p/10754894.html
Spring boot 处理 error 的基本流程:
Controller -> 发生错误 -> BasicErrorController -> 根据 @RequestMapping(produces) 判断调用 errorHtml 或者 error 方法,然后:
errorHtml -> getErrorAttributes -> ErrorViewResolver -> 错误显示页面
error -> getErrorAttributes -> @ResponseBody (直接返回JSON)
附:为静态文件加md5(避免浏览器缓存旧文件)
1 2
| spring.resources.chain.strategy.content.enabled=true spring.resources.chain.strategy.content.paths=/**
|
附一:content-path 相关处理
配置项
1
| server.servlet.context-path=/myweb
|
后端获取basePath
1 2 3 4 5 6 7
| public static String getWebBasePath() { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); if (requestAttributes == null) return null; HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getResponse(); if (request == null) return null; return String.format("%s://%s:%s%s/", request.getScheme(), request.getServerName(), request.getServerPort(), request.getContextPath()); }
|
在js里获取contentPath
1 2 3
| <script th:inline="javascript"> var contextPath = ''; </script>
|
注:在html模板里使用th:href或th:src时带”@”符号会自动处理contentPath
附二:为thymeleaf模板设置全局静态变量
以配置第三方库路径为例
配置项
1
| basepath.lib=https://cdnjs.cloudflare.com/ajax/
|
配置 ThymeleafViewResolver
1 2 3 4 5 6 7 8 9 10 11
| @Resource private Environment env;
@Resource private void configureThymeleafStaticVars(ThymeleafViewResolver viewResolver) { if(viewResolver != null) { Map<String, Object> vars = new HashMap<>(); vars.put("libBasePath", env.getProperty("basepath.lib")); viewResolver.setStaticVariables(vars); } }
|
使用libBasePath引入第三方库(比如vue.js)
1
| <script th:src="${libBasePath}+'libs/vue/2.6.10/vue.js'"></script>
|
附三:html模板共用head
common.html 里定义公用head
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <!DOCTYPE html> <html lang="zh" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head th:fragment="head"> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no"/> <link rel="icon" type="image/x-icon" th:href="@{/favicon.ico}"> <link rel="stylesheet" th:href="${libBasePath}+'libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css'"/> <script th:src="${libBasePath}+'libs/vue/2.6.10/vue.js'"></script> <link th:href="@{/static/base.css}" rel="stylesheet"/> <script th:src="@{/static/base.js}"></script> <script th:inline="javascript">var contentPath = '';</script> </head> <body> </body> <html>
|
index.html 里引用common::head
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!DOCTYPE html> <html lang="zh" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title>我的页面</title> <th:block th:include="common::head"></th:block> <link rel="stylesheet" th:href="@{/static/index.css}" /> </head> <body> <script th:inline="javascript"> var varForJs = {}; </script> <script th:src="@{/static/index.js}"></script> <body> </html>
|
附四:使用thymeleaf简单制作vue组件
用tab-bar做例子
tabar.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <!DOCTYPE html> <html lang="zh" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <body> <th:block th:fragment="tabar"> <script type="text/x-template" id="tabar-tpl"> <div class="nav nav-tabs"> <a class="nav-item nav-link" href="#" v-for="(tabName,tabKey) in tabs" v-show="!!tabName" :class="{active: active == tabKey}" @click="onClick(tabKey, tabName)">{{tabName}}</a> <slot></slot> </div> </script> <script th:inline="javascript"> Vue.component('tabar', { template: '#tabar-tpl', props:{ tabs: [Object, Array], active: [String, Number], }, data() { return {} }, methods: { onClick(tabKey, tabName){ this.$emit('click-tab', {key: tabKey, name: tabName}) }, } }); </script> </th:block> </body> </html>
|
引入和使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <head> <th:block th:include="tabar::tabar"></th:block> </head> <body> <div id="root"> <tabar :tabs="tabs" :active="activeTab" @click-tab="activeTab=$event.key"></tabar> </div> </body> <script> new Vue({ el: '#root', data: { tabs: {a:'Tab A', b:'Tab B'}, activeTab: 'a', }, methods: { }, }); </script>
|