4.SpringBoot与Web结合前期准备
首先需要了解SpringBoot整合Web的准备前提需要什么?
因为SpringBoot约定大于配置所以我们需要知道一下资源放置位置
- 导入静态资源(html,css,js等静态资源该如何导入)
- 定制首页
- 用thymeleaf或者freemarker代替jsp(SpringBoot不支持jsp)
- 学会装配扩展SpringMVC(视图解析器,处理器映射器,json,ajax,文件上传与下载等等都是我们需要装配SpringMVC的理由)
- 国际化i18n
- 拦截器(都是通过Bean注入到Spring容器的)
- CRUD(我觉得可以用代码生成器easycode,mybatisplus generator)
4.1 静态导入探究
首先我们配置了web的场景启动器,所以SpringBoot会自动帮我们装配WebMvcAutoConfiguration
- 跟进WebMvcAutoConfiguration可以找到WebMvcAutoConfigurationAdapter这样一个Web适配类
- 发现里面声明了一个静态私有的ResourceProperties配置类
private final ResourceProperties resourceProperties;
- 跟进去可以看到SpringBoot静态资源的存放位置(就下面源代码部分)
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
//第一个classpath:/META-INF/resources/是webjars下的下面会将,其他的直接建在classpath下就行
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
}
- 因此我们可以在指定的路径下创建相应的文件夹存放静态资源(classpath表示类路径也即是resources)
优先级:resources>static>public
同样WebMvcAutoConfigurationAdapter下也有addResourceHandlers(添加资源处理器的类)
- 在这个类中也说明了一种添加静态资源的方式
- 直接上源码
- 她说在"classpath:/META-INF/resources/webjars/"下可以增加静态资源
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
/**
* 如果你自己定义了静态资源的路径,那么默认的就失效
* 源码默认是 private String staticPathPattern = "/**";
* 现在我们可以自定义
* spring.mvc.static-path-pattern= "/xuan"
*/
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
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)
//这源码就是找我们上面说的resourceProperties下的静态资源
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
我们看一下webjars是什么?
- WebJars是打包到JAR(Java存档)文件中的客户端Web库(例如jQuery和Bootstrap)
安装官网
第一步:引入pom.xml
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
第二步:我们可以看到导入依赖对应的目录结构与classpath:/META-INF/resources/webjars/是对应的
第三步:启动服务,访问下 localhost:8081/webjars/jquery/3.5.1/jquery.js 可以访问到jquery.js的静态资源
4.2 定制首页
首先我们配置了web的场景启动器,所以SpringBoot会自动帮我们装配WebMvcAutoConfiguration
- 跟进WebMvcAutoConfiguration可以找到EnableWebMvcConfiguration这个类
- 在这个类下面有个welcomePageHandlerMapping(@Bean表示添加到Spring组件)就是可以让我们定制首页的
- 其次还有一个getWelcomePage方法默认是index页面作为首页
- 而index.html这个页面放在this.resourceProperties也就是我们上面指定的静态资源目录
- 首页一般是放在templates目录下
下面我们看一下源码
//欢迎页的处理映射
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
return welcomePageHandlerMapping;
}
private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
测试一下,编写一个index.html放在public目录下 直接访问localhost:8080 就可以显示首页信息
4.3 Thymeleaf模板引擎
什么是Thymeleaf?
-
https://www.thymeleaf.org/ 官网
-
Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎,能轻易的与Spring MVC等Web框架进行集成作为Web应用。
-
与其它模板引擎(比如FreeMaker)相比,Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用(更加方便前后端分离,比如方便类似VUE前端设计页面),抛弃JSP吧。
-
Thymeleaf的主要目标是为您的开发工作流程带来优雅的自然模板 -HTML可以在浏览器中正确显示,也可以作为静态原型工作,从而可以在开发团队中加强协作。
-
Thymeleaf拥有适用于Spring Framework的模块,与您喜欢的工具的大量集成以及插入您自己的功能的能力,对于现代HTML5 JVM Web开发而言,Thymeleaf是理想的选择-尽管它还有很多工作要做。
使用Thymeleaf
第一步:导入Thymeleaf依赖
<!--Java 8 Time API兼容性-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!--Spring 5整合-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
然后由于SpringBoot的自动装配,我们一定能找到ThymeleafAutoConfiguration自动装配类
接着必然有ThymeleafProperties,我们跟进去看一下源码
- 也就是说我们所有需要被SpringBoot识别的html页面需要放到templates目录下(当然也可以自己指定)
- 当然我们也可以在application.yml指定spring.thymeleaf的属性值
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
}
第二步:编写controller并用model将值存进去
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model){
model.addAttribute("msg","thymeleaf是最好的模板引擎");
return "hello";
}
}
第三步:在templates文件夹下新建一个 hello.html
- 注意这里想要取值,关键之一是 引入thymeleaf支持====
- 其次用==th:text="${msg}"==取出model里面的值 th是thymeleaf的固定前缀 text是取出文本数据 ${} 就是el表达式的取值
<!DOCTYPE html>
<html lang="en">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p th:text="${msg}"></p>
</body>
</html>
4.4 Thymeleaf语法
可以在在线文档查看用法:https://fanlychie.github.io/
thymeleaf语法
- Thymeleaf 提供了非常丰富的标准表达式语法,总共有 8 大类:
- 简单表达式、字面值、文本操作、算术运算、布尔运算、比较和相等、条件运算、无操作符
语法 | 名称 | 描述 | 作用 |
---|---|---|---|
${…} | Variable Expressions | 变量表达式 | 取出上下文变量的值 |
*{…} | Selection Variable Expressions | 选择变量表达式 | 取出选择的对象的属性值 |
#{…} | Message Expressions | 消息表达式 | 使文字消息国际化,I18N |
@{…} | Link URL Expressions | 链接表达式 | 用于表示各种超链接地址 |
~{…} | Fragment Expressions | 片段表达式 | 引用一段公共的代码片段 |
1.使用文本 th:text 与 th:utext
属性th:utext
与th:text
的区别在于:
-
th:text
默认会对含有 HTML 标签的内容进行字符转义; -
th:utext
(Unescaped Text)则不会对含有 HTML 标签的内容进行字符转义;(也就是说utext会解析标签中内容) -
测试
-
controller中部分代码
model.addAttribute("msg","<h1>thymeleaf是最好的模板引擎</h1>");
- hello.html部分代码
<div th:text="${msg}"></div>
<hr/>
<div th:utext="${msg}"></div>
- 页面展示
2.遍历 th:each
- 遍历(迭代)的语法
th:each="自定义的元素变量名称 : ${集合变量名称}"
: - 下面就是遍历取出所有所在城市名字
<div>
<spn>你所在城市:</spn>
<select name="mycity">
<option th:each="city : ${cities}" th:text="${city.name}"></option>
</select>
</div>
- 还有另一种取法(不建议这样取)
<option th:each="city : ${cities}" >[[${city.name}]]</option>
3.条件判断 th:if th:unless th:swith
- 当表达式的评估结果为真时则显示内容,否则不显示:
<a th:href="@{/user/order(uid=${user.id})}" th:if="${user != null}">我的订单</a>
th:unless
与th:if
判断恰好相反,当表达式的评估结果为假时则显示内容,否则不显示:
<a th:href="@{/user/order(uid=${user.id})}" th:unless="${user == null}">我的订单</a>
- 多路选择语句,它需要搭配
th:case
来使用:
<div th:switch="${user.role}">
<p th:case="admin">管理员</p>
<p th:case="user">普通用户</p>
</div>
4.5 装配扩展SpringMVC
自动配置在Spring的默认值之上添加了以下功能:
- 包含
ContentNegotiatingViewResolver
和BeanNameViewResolver
。 - 支持提供静态资源,包括对WebJars的支持。
- 自动注册
Converter
,GenericConverter
和Formatter
豆类。 - 支持
HttpMessageConverters
。 - 自动注册
MessageCodesResolver
。 - 静态
index.html
支持。 - 定制
Favicon
支持。 - 自动使用
ConfigurableWebBindingInitializer
bean。
如果要保留这些Spring Boot MVC定制并进行更多的MVC定制拦截器,格式化程序,视图控制器和其他功能),则可以添加自己@Configuration
的type类,WebMvcConfigurer
但不添加 @EnableWebMvc
。
解释一下为什么自己配置的类不能加@EnableWebMvc注解
- 首先加上这个注解后跟进源码会发现导入了DelegatingWebMvcConfiguration这个类
- 而DelegatingWebMvcConfiguration继承WebMvcConfigurationSupport这个类
- 此时我们再看springBoot中web的核心配置类WebMvcAutoConfiguration(见下面)
- 是不是发现了**@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)只有在XXXSupport不存在SpringIOC容器才接管,也就意味着你加了@EnableWebMvc**后spring容器就无法接管了。
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
}
可以自定义视图解析器之类的 写在config包下,类需要实现WebMvcConfigurer接口
- 重写WebMvcConfigurer的addViewControllers方法实现视图跳转
package com.xuan.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Locale;
/**
* xuan
* 2020/7/23
* 1870136088@qq.com
**/
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
/**
* 视图跳转(因为视图跳转是由controller决定的)
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/yangjieshen").setViewName("hello");
registry.addRedirectViewController("/xuan","/hello");
}
}