SpringBoot从入门到放弃,第三章
一、静态资源映射规则
在springBoot项目中,springmvc的相关配置都在WebMvcAutoConfiguration类中
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
//===========webjars访问===================
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
//===========静态资源访问================
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
//===========静态资源存放文件路径================
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
};
1、webjars访问
所有/webjars/ ,都去classpath:/META-INF/resources/webjars/找资源
webjars:以jar包的方式引入静态资源。官网
引入:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.4.1</version>
</dependency>
目录结构:
访问:
http://localhost:8080/webjars/jquery/3.4.1/jquery.js
2、静态资源访问
/**访问当前项目的任何资源,静态资源访问文件目录:
classpath:/META-INF/resources/,
classpath:/resources/,
classpath:/static/,
classpath:/public/
/
访问:
3、欢迎页index的映射
静态资源文件夹下所有index.html页面,被/**映射
访问:
http://localhost8080/index.html 或者 http://localhost8080/
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(...) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
return welcomePageHandlerMapping;
}
private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
4、favicon图标
@Configuration
@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
public static class FaviconConfiguration {
private final ResourceProperties resourceProperties;
public FaviconConfiguration(ResourceProperties resourceProperties) {
this.resourceProperties = resourceProperties;
}
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
//所有 **/favicon.ico
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
faviconRequestHandler()));
return mapping;
}
@Bean
public ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
requestHandler
.setLocations(this.resourceProperties.getFaviconLocations());
return requestHandler;
}
}
spring.mvc.favicon.enabled=true默认启用
存放位置:静态资源文件夹下
favicon.ico
5、静态资源参数设置
@ConfigurationProperties(prefix = "spring.resources")
public class ResourceProperties {
//可以设置和资源有关的参数,如缓存时间等
}
如果想改变静态资源文件夹的路径,修改yml:
spring.resources.static-locations=classpath:/hello,classpath:/mystatic
二、模板引擎thymeleaf
1、引入thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
切换thymeleaf版本(旧版本的springboot切换)
<properties>
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
</properties>
2、Thymeleaf使用语法
springboot对thymeleaf的自动配置文件
表示:只要我们把HTML页面放在classpath:/templates/下,就可以自动渲染了
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
//表示:只要我们把HTML页面放在classpath:/templates/下,就可以自动渲染了
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
}
2.1)、application.yml配置
###ThymeLeaf配置
spring:
thymeleaf:
#模板的模式,支持 HTML, XML TEXT JAVASCRIPT
mode: HTML5
#编码 可不用配置
encoding: UTF-8
#内容类别,可不用配置
content-type: text/html
#开发配置为false,避免修改模板还要重启服务器
cache: false
#配置模板路径,默认是templates,可以不用配置
prefix: classpath:/templates
##实际项目中可能会有不太严格的HTML格式,此时设置mode=HTML5将会对非严格的报错,可以参考以下配置:
spring.thymeleaf.mode=LEGACYHTML5
##你可能会发现在默认配置下,thymeleaf对.html的内容要求很严格,比如<meta charset="UTF-8" />,
##如果少最后的标签封闭符号/,就会报错而转到错误页。
#因此,建议增加下面这段:
spring.thymeleaf.mode = LEGACYHTML5
##spring.thymeleaf.mode的默认值是HTML5,其实是一个很严格的检查,改为LEGACYHTML5可以得到一个可能更友好
##亲切的格式要求。
##需要注意的是,LEGACYHTML5需要搭配一个额外的库NekoHTML才可用。
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
常见的一些可以被修改的配置:
# THYMELEAF (ThymeleafAutoConfiguration)
spring.thymeleaf.cache=true
spring.thymeleaf.check-template=true
spring.thymeleaf.check-template-location=true
spring.thymeleaf.enabled=true
spring.thymeleaf.enable-spring-el-compiler=false
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.excluded-view-names=
spring.thymeleaf.mode=HTML
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.reactive.chunked-mode-view-names=
spring.thymeleaf.reactive.full-mode-view-names=
spring.thymeleaf.reactive.media-types=
spring.thymeleaf.suffix=.html
spring.thymeleaf.template-resolver-order=
spring.thymeleaf.view-names=
2.2)、导入thymeleaf的名称空间
<html xmlns:th="http://www.thymeleaf.org">
2.3)、语法
文本内容(覆盖):<div th:text="${hello}">默认文本内容</div>
替换原生属性值:<div id="aa" class="bb" th:id="#{cc}" th:class="${dd}"></div>
①、 赋值、字符串拼接
+:字符串拼接字体串
简化字符中拼接操作:(使用”|”包围字符串,不需要对字符串使用”’”)
<input type="text" name="userName" value="James Carrot" th:value="${user.name}" />
<span th:text="'The name of the user is ' + ${user.name}" ></span>
<span th:text="'The name of the user is ' + ${user.name} + '_' + ${user.age}"> </span>
<span th:text="|Welcome to our application, ${user.name}!|"></span>
等价于
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
②、数字输出和计算
<span th:text="2013">1492</span>
<span th:text="2013 + 2">1494</span>
<span th:if="${user.isAdmin()} == false"> false </span>
<span th:if="${user.other} == null"> null</span>
③、算术表达式
*二进制运算 +, -, , /, %
布尔表达式 true, false, !, not
以及 and, or
<span th:text="${user.age} % 2 == 0"> </span> 结果:true
<span th:text="true"> </span> 结果:true
<span th:text="!(${user.age} % 2 == 0)"> </span> 结果:false
<span th:text="(${user.age} % 2 == 0) and true"> </span>结果:true
④、比较操作符
比较 >, <, >=, <= (gt, lt, ge, le)
判断 ==, != (eq, ne)
<span th:if="${user.age} > 18"> 大人 </span>
<span th:if="${user.age} != 18"> 大人_no_equality </span>
⑤、条件操作符
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
<span th:text="${user.age}%2 == 0 ? 'even'"> </span>
<span th:text="${user.age}%2 == 0 ? 'even' : 'odd'"> </span>
<span th:if="${member.age lt 18}">
未成年人!
</span>
<span th:if="${member.name eq '啊三'}">
欢迎小三来访问!
</span>
##不满足判断条件
<span th:unless="${member.age gt 18}">
你还不满18岁,不能够看电影!
</span>
##switch分支判断
<span th:switch="${member.uid}">
<p th:case="100"> uid为101的员工来了 </p>
<p th:case="99"> uid为102的员工来了 </p>
<p th:case="*"> 没有匹配成功的数据! </p>
</span>
⑥、调用对象的成员变量的属性
<input type="text" name="userName" th:value="${family.father.name}" />
⑦、调用map对象的属性
通过map的key从hashMap获取对象的属性name值: 可以使用”.”或者使用”[]”获取对象值
<input type="text" name="userName" th:value="${hashMap.hashMapKey.name}" />
等价于
<input type="text" name="userName" th:value="${hashMap['hashMapKey'].name}" />
⑧、调用list对象的属性
<input type="text" name="userName" th:value="${family.childList[0].name}" />
⑨、调用属性的方法
<input type="text" name="userName" th:value="${family.father.name.toUpperCase()}" />
⑩、获取原生对象
<p th:text="${#httpServletRequest.getRemoteAddr()}"/>
<p th:text="${#httpServletRequest.getAttribute('requestMessage')}"/>
<p th:text="${#httpSession.getId()}"/>
<p th:text="${#httpServletRequest.getServletContext().getRealPath('/')}"/>
⑪、生成URL地址@{}
th:href生成的值替换的href值 @{}
url中加入变量值(orderId=${id})做为url的请求参数
<!-- th:href生成的值替换<a>的href值; (orderId=${id})做为url的请求参数 -->
<a th:href="@{http://localhost:8080/order/details(orderId=${id})}">view</a>
结果:
<a href="http://localhost:8080/order/details?orderId=123">view</a>
<!-- 生成:/order/details?orderId=123 -->
<a th:href="@{/order/details(orderId=${id})}">view</a>
结果:
<a href="/order/details?orderId=123">view</a>
<!-- 替换url中变量值,生成/order/123/details -->
<a th:href="@{/order/{orderId}/details(orderId=${id})}">view</a>
结果:
<a href="/order/123/details">view</a>
⑫、表达式工具对象
<body>
<p th:text="${#dates.format(mydate,'yyyy-MM-dd')}"/>
<p th:text="${#dates.format(mydate,'yyyy-MM-dd HH:mm:ss.SSS')}"/>
<hr/>
<p th:text="${#strings.replace('www.baidu.cn','.','$')}"/>
<p th:text="${#strings.toUpperCase('www.baidu.cn')}"/>
<p th:text="${#strings.trim('www.baidu.cn')}"/>
<hr/>
<p th:text="${#sets.contains(names,'boot-0')}"/>
<p th:text="${#sets.contains(names,'boot-9')}"/>
<p th:text="${#sets.size(names)}"/>
<hr/>
<p th:text="${#sets.contains(ids,0)}"/>
<p th:text="${ids[1]}"/>
<p th:text="${names[1]}"/>
</body>
⑬、迭代
<!-- 常用的迭代 th:each 用法 -->
<tr th:each="user : ${userList}">
<td th:text="${user.name}"></td>
<td th:text="${user.age}"></td>
<td th:text="${user.isAdmin}"></td>
</tr>
##获取迭代的中间的状态,定义在iterStat中
index :当前节点的索引,从0开始
size : 迭代节点总数
even/odd:当前是偶数/奇数行,boolean值
first/last:当前是每天/最后一个元素
<!-- 获取迭代的中间的状态,定义在iterStat中-->
<tr th:each="user,iterStat : ${userList}">
<!-- index: 当前迭代的索引 -->
<td th:text="${iterStat.index }"></td>
<!-- first: 当前元素是第一个元素; last: 当前元素是最后个元素 -->
<td th:text="${iterStat.first } ? '第一个元素':(${iterStat.last} ? '最后一个元素':'')" ></td>
<!-- -->
<td th:text="${iterStat.odd} ? 'odd' : 'even'" ></td>
<td th:text="${user.name}"></td>
<td th:text="${user.age}"></td>
<td th:text="${user.isAdmin}"></td>
</tr>
⑭、条件语法
**th:if th:unless **
<!-- th:if:如果值是true,则打印<span>整个节点 -->
<span th:if="${user.isAdmin}" th:text="${user.name} + '是管理员'"> </span>
<!-- th:unless: 和th:if是相反功能,如果值为false,则打印<span>整个节点 -->
<span th:unless="not ${user.isAdmin}" th:text="${user.name} + '是管理员'"> </span>
<!-- th:switch / th:case -->
<div th:switch="${user.name}">
<p th:case="'admin'">User is an administrator</p>
<!-- *: case的默认的选项 -->
<p th:case="*">User is some other thing</p>
</div>
⑮、模板template
在web开发中,我们经常会将公共头,公共尾,菜单等部分提取成模板供其它页面使用。在thymeleaf中,通过th:fragment、th:include、th:replace、参数化模板配置、css选择器加载代码块等实现。
公共页 /templates/template/footer.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8" />
<body>
<!-- th:fragment 定义用于加载的块 -->
<span th:fragment="copy"> 2017 hry loaded by fragment=copy</span>
<span id="copy-section"> 2017 hry loaded by id=copy-section</span>
<!-- 定义模板时,可以传入参数 -->
<span th:fragment="frag(month, date) ">
<span th:text="'welcome hry come in ' + ${month} + '-' + ${date}"></span>
</span>
</body>
</html>
通过th:include在本页中加载以上的代码块copy :
templatename::selector:”::”前面是模板文件名,后面是选择器
::selector:只写选择器,这里指fragment名称,则加载本页面对应的fragment
templatename:只写模板文件名,则加载整个页面
<!-- 语法说明 "::"前面是模板文件名,后面是选择器 -->
<div th:include="template/footer::copy"></div>
<!-- 只写选择器,这里指fragment名称,则加载本页面对应的fragment -->
<div th:include="::#thispage"></div>
<!-- 只写模板文件名,则加载整个页面 -->
<div th:include="template/footer"></div>
<!--本页面的加载块-->
<span id="thispage">
div in this page.
</span>
通过th:fragment和css选择器加载代码块
<!-- 这里加载”th:fragment 定义用于加载的块“ -->
<div th:include="template/footer::copy"></div>
<!-- 这里加载”id=copy-section“的节点 -->
<div th:include="template/footer::#copy-section"></div>
th:include 和 th:replace
## th:include:加载模板的内容: 读取加载节点的内容(不含节点名称),替换div内容
## th:replace:替换当前标签为模板中的标签,加载的节点会整个替换掉加载他的div
<!-- 加载模板的内容: 读取加载节点的内容(不含节点名称),替换<div>的内容 -->
<div th:include="template/footer::copy">1</div>
结果:
<div> 2017 hry loaded by fragment=copy</div>
<!-- 替换当前标签为模板中的标签: 加载的节点会整个替换掉加载他的<div> -->
<div th:replace="template/footer::copy">2</div>
结果:
<span> 2017 hry loaded by fragment=copy</span>
调用模板时传递参数
<div th:include="template/footer::frag(${month},${date})">...</div>