一:Thymeleaf
Spring官方也推荐使用Thymeleaf,Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用。
JSP技术Spring Boot官方是不推荐的,原因有三:
- tomcat只支持war的打包方式,不支持可执行的jar。
- Jetty 嵌套的容器不支持jsp
- Undertow
- 创建自定义error.jsp页面不会覆盖错误处理的默认视图,而应该使用自定义错误页面
1. pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
spring-boot-starter-thymeleaf会自动包含spring-boot-starter-web,所以我们就不需要单独引入web依赖了
2. application.properties
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=true
spring.thymeleaf.enabled=true
默认的配置如下:
# THYMELEAF (ThymeleafAutoConfiguration)
#开启模板缓存(默认值:true)
spring.thymeleaf.cache=true
#Check that the template exists before rendering it.
spring.thymeleaf.check-template=true
#检查模板位置是否正确(默认值:true)
spring.thymeleaf.check-template-location=true
#Content-Type的值(默认值:text/html)
spring.thymeleaf.content-type=text/html
#开启MVC Thymeleaf视图解析(默认值:true)
spring.thymeleaf.enabled=true
#模板编码
spring.thymeleaf.encoding=UTF-8
#要被排除在解析之外的视图名称列表,用逗号分隔
spring.thymeleaf.excluded-view-names=
#要运用于模板之上的模板模式。另见StandardTemplate-ModeHandlers(默认值:HTML5)
spring.thymeleaf.mode=HTML5
#在构建URL时添加到视图名称前的前缀(默认值:classpath:/templates/)
spring.thymeleaf.prefix=classpath:/templates/
#在构建URL时添加到视图名称后的后缀(默认值:.html)
spring.thymeleaf.suffix=.html
#Thymeleaf模板解析器在解析器链中的顺序。默认情况下,它排第一位。顺序从1开始,只有在定义了额外的TemplateResolver Bean时才需要设置这个属性。
spring.thymeleaf.template-resolver-order=
#可解析的视图名称列表,用逗号分隔
spring.thymeleaf.view-names=
3. controller
@Controller
public class SampleController {
@GetMapping("/index")
public ModelAndView index(Model model){
ModelAndView modelAndView = new ModelAndView("index");
List<Fans> fans = new ArrayList<>();
fans.add(new Fans(1L, "zhangsan"));
fans.add(new Fans(2L, "lis"));
fans.add(new Fans(3L, "wangwu"));
modelAndView.addObject("user", new User(1L, "mengday", "xxx@gmail.com", fans, true, new Date()));
return modelAndView;
}
@Data
@AllArgsConstructor
public class User {
private Long id;
private String username;
private String email;
private List<Fans> fans;
private Boolean isAdmin;
private Date birthday;
}
@Data
@AllArgsConstructor
public class Fans {
private Long id;
private String name;
}
}
4. index.html
注意:在html标签中使用xmlns:th=”http://www.thymeleaf.org”
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<link rel="stylesheet" type="text/css" href="css/style.css"/>
<body>
<div th:object="${user}">
<span th:text="*{id}"></span><br/>
<span th:text="'nickname:' + *{username}"></span><br/>
<span th:text="|email: ${user.email}|"></span><br/>
<span th:if="${user.isAdmin}">超级管理员</span><br/>
<span th:unless="${user.isAdmin == true}">普通用户</span><br/>
<span> [[${#dates.format(user.birthday,'yyyy-MM-dd HH:mm:ss')}]] </span><br/>
<table border="1px" >
<tr> <td>index</td> <td>id</td> <td>name</td> <td>length</td></tr>
<tr th:each="item, stat : ${user.fans}" th:style="'background-color:' + ${stat.even == true ? 'bisque' : 'gray'}">
<td>[[${stat.index}]]</td>
<td>[[${item.id}]]</td>
<td>
<a th:href="@{/fans/{id}(id=${item.id})}">[[${item.name}]]</a>
</td>
<td>[[${#strings.length(user.username)}]]</td>
</tr>
</table>
</div>
<br/>
</body>
</html>
src/resources/static/css/style.css
一般静态资源(js、css、image等)都放在static目录下
body { color: red; }
二:静态资源的存放位置
Spring Boot 提供了对静态资源的支持,在创建Spring Boot项目时默认只创建了一个存放静态资源的目录src/main/resources/static,一共有四个,其它三个如果需要可以手动创建出目录,而且这四个是有优先级,当同一个资源出现在静态目录的多个地方时是按照优先级获取的,先获取优先级高的。
- classpath:/META-INF/resources
- classpath:/resources
- classpath:/static
- classpath:/public
1. application.properties配置
对应的源码:spring-configuration-metadata.json
{
"sourceType": "org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties",
"defaultValue": "\/**",
"name": "spring.mvc.static-path-pattern",
"description": "Path pattern used for static resources.",
"type": "java.lang.String"
},
{
"sourceType": "org.springframework.boot.autoconfigure.web.ResourceProperties",
"defaultValue": [
"classpath:\/META-INF\/resources\/",
"classpath:\/resources\/",
"classpath:\/static\/",
"classpath:\/public\/"
],
"name": "spring.resources.static-locations",
"description": "Locations of static resources. Defaults to classpath:[\/META-INF\/resources\/,\n \/resources\/, \/static\/, \/public\/].",
"type": "java.lang.String[]"
},
对应application.properties的默认配置
spring.mvc.static-path-pattern=/**
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
在应用程序启动的时候会有日志打印,可以看到
- /** 映射到 /static (或/public、/resources、/META-INF/resources)
- /webjars/** 映射到 classpath:/META-INF/resources/webjars/
假如 在src/main/resources/static目录下放一个index.html, 通过http://localhost:8080/index.html就能直接访问,路径中不用在包含static子路径了。
如果访问静态资源想在路径上有体现,也可以自定义,如将static-path-pattern配置成spring.mvc.static-path-pattern=/static/**
就需要用http://localhost:8080/static/index.html来访问
同样如果对于系统定义的静态资源的存放位置不满意也可以通过spring.resources.static-locations来定义,一般静态资源都放在src/main/resource下面。
2. JavaConfig - WebMvcConfigurationSupport
addResourceHandlers用于配置spring.mvc.static-path-pattern。
传统Spring MVC开发经常将jsp页面放置在webapp/WEB-INF下,这样要想访问页面就需要写一个controller对应的方法,即使方法中没什么业务逻辑,只仅仅用于页面跳转也不得不写,感觉有点low,在Spring Boot中通过配置addViewControllers,配置一个路径和一个视图名称的映射即可,也不用再controller中写页面跳转的RequestMapping方法了。啰嗦一句:看到addViewControllers这个方法感觉突然好熟悉,原来在iOS中有个addChildViewController方法,而且在iOS中有UIViewController这种类,类似于Java中的Controller的概念
@Configuration
public class MyWebMvcConfigurerAdapter extends WebMvcConfigurationSupport {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/html/");
// addResourceLocations也可以指定一个系统中的一个目录
// registry.addResourceHandler("/static/**").addResourceLocations("file:/Users/xxx/Documents/demo/static");
super.addResourceHandlers(registry);
}
// http://localhost:8080/home
@Override
protected void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/home").setViewName("home");
super.addViewControllers(registry);
}
}
webjars
webjars 就是将web开发中常用的静态资源放到classpath:/META-INF/resources/webjars/ 中,然后打包成.jar发布到maven仓库中,使用时通过maven来引入,假如项目中用到bootstrap,常规的做法就是手动去官网下载,然后手动将bootstrap放到静态目录。一般常用的像jquery、bootstrap、angularjs、react、vue、swagger-ui等都有相应的maven依赖,目前有92个这种依赖。
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
在页面上通过/webjars/jquery/3.3.1/jquery.js 来引入,Spring Boot 默认将 /webjars/** 映射到 classpath:/META-INF/resources/webjars/,可以通过http://localhost:8080/webjars/jquery/3.3.1/jquery.js来验证一下看是否能获取到。
<script src="${pageContext.request.contextPath}/webjars/jquery/3.3.1/jquery.js"></script>
webjars-locator
在上面访问静态资源时路径中包含了版本号(http://localhost:8080/webjars/jquery/3.3.1/jquery.js),假如版本升级需要全局替换所有引入的文件,当然也不太费事一个全局替换就搞定了,当然也可以防止于未然,webjars提供了一个webjars-locator的依赖,目的是让前端页面引入的静态资源不包含路径,当请求一个静态资源时会自动帮你映射到对的版本对应的资源。
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>0.33</version>
</dependency>
@Controller
public class WebJarsController {
private final WebJarAssetLocator assetLocator = new WebJarAssetLocator();
@ResponseBody
@RequestMapping("/webjarslocator/{webjar}/**")
public ResponseEntity<Object> locateWebjarAsset(@PathVariable String webjar, HttpServletRequest request) {
try {
// /webjarslocator/jquery/
String mvcPrefix = "/webjarslocator/" + webjar + "/"; // This prefix must match the mapping path!
// /webjarslocator/jquery/jquery.js
String mvcPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
// META-INF/resources/webjars/jquery/3.3.1/jquery.js
String fullPath = assetLocator.getFullPath(webjar, mvcPath.substring(mvcPrefix.length()));
return new ResponseEntity<>(new ClassPathResource(fullPath), HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
<script src="${pageContext.request.contextPath}/webjarslocator/jquery/jquery.js"></script>
假如前端访问http://localhost:8080/webjarslocator/jquery/jquery.js(此地址中不包含版本号),地址映射到WebJarsController,返回META-INF/resources/webjars/jquery/3.3.1/jquery.js对应的资源。这样就达到前端引入的静态资源中不包含具体版本号,当需要升级的时候直接修改maven对应的依赖版本即可,前端也不用全局替换版本号操作了。感觉这个功能不实用,版本升级很久才会一次,而且全局替换也很简单,也是秒秒钟搞定的事情。
静态资源浏览器缓存
开发时,业务对应的js经常会变动,一般浏览器会缓存静态资源,一般防止浏览器缓存静态资源的做法是在地址后面使用版本号参数,或者在地址后面使用时间戳,Spring Boot 给出了自己的解决方案。
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
@ControllerAdvice
public class ResourceUrlProviderController {
@Autowired
private ResourceUrlProvider resourceUrlProvider;
@ModelAttribute("urls")
public ResourceUrlProvider urls() {
return this.resourceUrlProvider;
}
}
common.js放在 src/main/resources/static/js/目录下
<script src="${pageContext.request.contextPath }${urls.getForLookupPath('/js/common.js') }"></script>
当我们访问页面后,HTML中实际生成的代码为common-<md5>.js, 每当common.js内容发生变化时MD5值就会重新生成,当js文件没有时md5值不变,当js文件内容发生改变时,md5值发生变化,js文件的内容也随之改变。这样就保证了js文件始终是最新的。
<script src="/js/common-c6b7da8fffc9be141b48c073e39c7340.js"></script>
三: Interceptor拦截器
拦截器的使用分两步:
- 声明拦截器,实现HandlerInterceptor
- 配置拦截器,在WebMvcConfigurerAdapter#addInterceptors配置
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println(request.getRequestURI() + "-------------");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
System.out.println("------------postHandle------------");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("------------afterCompletion------------");
}
}
@Configuration
public class MyWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
super.addResourceHandlers(registry);
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/static/");
super.addInterceptors(registry);
}
}
注意:访问http://localhost:8080/static/index.html确实对静态资源不拦截了,访问http://localhost:8080/index被拦截到了