public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
//收到 /webjars/**请求后 ,会去classpath:/META-INF/resources/webjars/ 查找资源文件
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/METAINF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[{staticPathPattern}).addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>jqueryartifactId>
<version>3.3.1version>
dependency>
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/METAINF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
// 接收/**
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[{staticPathPattern}).addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
public WebMvcProperties() {
this.localeResolver = WebMvcProperties.LocaleResolver.ACCEPT_HEADER;
this.dispatchTraceRequest = false;
this.dispatchOptionsRequest = true;
this.ignoreDefaultModelOnRedirect = true;
this.throwExceptionIfNoHandlerFound = false;
this.logResolvedException = false;
=======接收 /**请求
this.staticPathPattern = "/**";
this.async = new WebMvcProperties.Async();
this.servlet = new WebMvcProperties.Servlet();
this.view = new WebMvcProperties.View();
this.contentnegotiation = new WebMvcProperties.Contentnegotiation();
this.pathmatch = new WebMvcProperties.Pathmatch();
}
@ConfigurationProperties(prefix = "spring.resources",ignoreUnknownFields = false)
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/",
"classpath:/public/"};
private String[] staticLocations;
private boolean addMappings;
private final ResourceProperties.Chain chain;
private final ResourceProperties.Cache cache;
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext) {
return new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(
applicationContext), applicationContext
=======查找欢迎页========
, this.getWelcomePage()
, this.mvcProperties.getStaticPathPattern());
}
private Optional<Resource> getWelcomePage() {
String[] locations =
===2. 上面说的4个静态资源路径加上 "/" 路径
getResourceLocations(
=====1. 获取上面说的4个静态资源路径
this.resourceProperties.getStaticLocations());
==================================在上面路径下查找 index.html 页面
return Arrays.stream(locations).map(this::getIndexHtml).
filter(this::isReadable).findFirst();
}
// 上面获取的路径中查找 index.html 页面
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
/: 当前项目根路径下
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
/: 当前项目根路径下
Spring Boot 官方不推荐使用JSP,因为内嵌的 Tomcat 、Jetty 容器不支持以 jar 形式运行 JSP。Spring Boot中提供了大量模板引擎,包含 Freemarker、Mastache、Thymeleaf 等。 而 Spring Boot 官方推荐使用Thymeleaf 作为模板引擎, 因为 Thymeleaf 提供了完美的 SpringMVC 的支持。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
@ConfigurationProperties( prefix = "spring.thymeleaf" )
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
@RequestMapping("/execute")
public String execute(Map<String, Object> map) {
map.put("name", "梦学谷");
// classpath:/templates/success.html
return "success";
}
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>hellotitle>
head>
<body>
<h2 >成功h2>
<p th:text="${name}">这里显示名字p>
body>
html>
一、Simple expressions(表达式语法)
1. Variable Expressions(变量表达式): ${...} (参考: 4.2 Variables)
1)、获取变量值;使用OGNL表达式;
2)、获取对象的属性, 调用方法
3)、使用内置的基本对象:
#ctx : the context object.(当前上下文对象)
#vars: the context variables.(当前上下文里的变量)
#locale : the context locale. (当前上下文里的区域信息)
下面是Web环境下的隐式对象
#request : (only in Web Contexts) the HttpServletRequest object.
#response : (only in Web Contexts) the HttpServletResponse object.
#session : (only in Web Contexts) the HttpSession object.
#servletContext : (only in Web Contexts) the ServletContext object.
示例: ${session.foo} (用法参考: 18 Appendix A: Expression Basic Objects)
4)、使用内置的工具对象:(用法参考: 19 Appendix B: Expression Utility Objects)
#execInfo : information about the template being processed.
#messages : methods for obtaining externalized messages inside variables
expressions, in the same way as they would be obtained using #{…} syntax.
#uris : methods for escaping parts of URLs/URIs
#conversions : methods for executing the configured conversion service
(if any).
#dates : methods for java.util.Date objects: formatting, component
extraction, etc.
#calendars : analogous to #dates , but for java.util.Calendar
objects.
#numbers : methods for formatting numeric objects.
#strings : methods for String objects: contains, startsWith,
prepending/appending, etc.
#objects : methods for objects in general.
#bools : methods for boolean evaluation.
#arrays : methods for arrays.
#lists : methods for lists.
#sets : methods for sets.
#maps : methods for maps.
#aggregates : methods for creating aggregates on arrays or collections.
#ids : methods for dealing with id attributes that might be repeated
(for example, as a result of an iteration).
2. Selection Variable Expressions(选择表达式): *{...}
(参考:4.3 Expressions on selections)
1)、和${}在功能上是一样, 额外新增:配合 th:object 使用
<div th:object="${session.user}">
省得每次写${session.user.firstName}, 直接取出对象,然后写对象名即可
<p>Name: <span th:text="*{firstName}">Sebastian</span> </p>
<p>Email: <span th:text="*{email}">Saturn</span> </p>
</div>
3. Message Expressions(获取国际化内容): #{...} (参考:4.1 Messages)
4. Link URL Expressions(定义URL): @{...} (参考:4.4 Link URLs)
5. Fragment Expressions(片段引用表达式): ~{...} (参考:4.5 Fragments)
<div th:insert="~{commons :: main}">...</div>
二、Literals(字面量) (参考: 4.6 Literals)
1. Text literals: 'one text' , 'Another one!' ,…
2. Number literals: 0 , 34 , 3.0 , 12.3 ,…
3. Boolean literals: true , false
4. Null literal: null
5. Literal tokens: one , sometext , main ,…
三、Text operations(文本操作) (参考: 4.7 Appending texts)
1. String concatenation: +
2. Literal substitutions: |The name is ${name}|
四、Arithmetic operations(数学运算) (参考: 4.9 Arithmetic operations)
1. Binary operators: + , - , * , / , %
2. Minus sign (unary operator): -
五、Boolean operations(布尔运算)
1. Binary operators: and , or
2. Boolean negation (unary operator): ! , not
六、Comparisons and equality(比较运算) (参考: 4.10 Comparators and Equality)
1. Comparators: > , < , >= , <= ( gt , lt , ge , le )
2. Equality operators: == , != ( eq , ne )
七、Conditional operators(条件表达式;三元运算符) (参考: 4.11 Conditional expressions)
1. If-then: (if) ? (then)
2. If-then-else: (if) ? (then) : (else)
3. Default: (value) ?: (defaultvalue)
八、Special tokens(特殊操作) (参考: 4.13 The No-Operation token)
1. No-Operation: _
<body>
<div th:fragment="header_common">
这是th:fragment声明公共片段
div>
<div id="header_common_id">
这是id选择器声明公共片段
div>
body>
<div th:replace="header :: header_common">div>
<div th:replace="header :: #header_common_id">div>
<h2 th:insert="header :: #header_common_id">h2>
练习:将项目中的 公共模块抽取出来到 public.html 中
@RequestMapping("/study")
public String study(Map<String, Object> map, HttpServletRequest request) {
List<User> userList = new ArrayList<>();
userList.add(new User("小梦", 1));
userList.add(new User("小李", 2));
userList.add(new User("小张", 1));
map.put("userList", userList);
return "study";
}
<table border="1px">
<tr>
<th>姓名th>
tr>
<tr th:each="user : ${userList}">
<td th:text="${user}">mengxuegutd>
tr>
table>
<hr/>
<ul>
<li th:each="user : ${userList}" th:text="${user}">li>
ul>
<table border="1px">
<tr>
<th>编号th>
<th>姓名th>
<th>总数th>
<th>偶数/奇数th>
<th>第一个元素th>
<th>最后一个元素th>
tr>
<tr th:each="user, iterStat : ${userList}">
<td th:text="${iterStat.count}">0td>
<td th:text="${user.username}">mengxuegutd>
<td th:text="${user.gender == 1 ? '女' : '男'}">未知td>
<td th:text="${iterStat.size}">0td>
<td th:text="${iterStat.even}? '偶数' : '奇数'">td>
<td th:text="${iterStat.first}">td>
<td th:text="${iterStat.last}">td>
tr>
table>
下面加not
th:if判断,如果此文字显示说明有值
th:unless判断,如果此文字显示说明有值
@RequestMapping("/study")
public String study(Map<String, Object> map, HttpServletRequest request) {
List<User> userList = new ArrayList<>();
userList.add(new User("小梦", 1));
userList.add(new User("小李", 2));
userList.add(new User("小张", 1));
map.put("userList", userList);
// 1女, 2男
map.put("sex", 1);
map.put("man", 2);
return "study";
}
<div th:switch="${sex}">
<p th:case="1">女p>
<p th:case="${man}">男p>
<p th:case="*">未知p>
div>
@RequestMapping("/study")
public String study(Map<String, Object> map, HttpServletRequest request) {
List<User> userList = new ArrayList<>();
userList.add(new User("小梦", 1));
userList.add(new User("小李", 2));
userList.add(new User("小张", 1));
map.put("userList", userList);
// 1女, 2男
map.put("sex", 1);
map.put("man", 2);
// th:text th:utext
map.put("desc", "欢迎来到梦学谷"
);
return "study";
}
<hr/>
<div th:text="${desc}"> div>
<div th:utext="${desc}"> div>
<input type="checkbox" /> [[${desc}]]
<p>Hello, [[${desc}]] 。。。p>
@RequestMapping("/study")
public String study(Map<String, Object> map, HttpServletRequest request) {
List<User> userList = new ArrayList<>();
userList.add(new User("小梦", 1));
userList.add(new User("小李", 2));
userList.add(new User("小张", 1));
map.put("userList", userList);
// 1女, 2男
map.put("sex", 1);
map.put("man", 2);
// th:text th:utext
map.put("desc", "欢迎来到梦学谷"
);
request.getSession().setAttribute("user", new User("小不点", 2));
return "study";
}
<div th:object="${session.user}">
<p>
姓名:<span th:text="*{username}">xxxxspan>
p>
<p>
性别:<span th:text="*{gender == 1 ? '女' : '男'}">xxxxspan>
p>
div>
#开发环境下关闭thymeleaf模板缓存,thymeleaf默认是开启状态
spring.thymeleaf.cache=false
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
Spring Boot 为 Spring MVC 提供了适用于多数应用的自动配置功能( WebMvcAutoConfiguration)。
在Spring默认基础上,自动配置添加了以下特性:
public class ContentNegotiatingViewResolver
//146
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
if (requestedMediaTypes != null) {
//选择所有候选的视图对象
List<View> candidateViews = this.getCandidateViews(viewName, locale,requestedMediaTypes);
//从候选中选择最合适的视图对象
View bestView = this.getBestView(candidateViews, requestedMediaTypes,attrs);
//存入所有视图解析器
private List<ViewResolver> viewResolvers;
107
protected void initServletContext(ServletContext servletContext) {
Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
//从容器中获取所有的视图解析器
this.obtainApplicationContext(), ViewResolver.class).values();
@Bean
public ViewResolver myViewResolver () {
return new MyViewResolver();
}
private class MyViewResolver implements ViewResolver {
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}
// DispatcherServlet.doDispatch 断点后,发送任意请求,可查看已被容器自动管理了
@Bean
public FormattingConversionService mvcConversionService() {
//传入日期格式, spring.mvc.date-format配置日期格式
WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
this.addFormatters(conversionService);
return conversionService;
}
//将格式化器添加容器中
protected void addFormatters(FormatterRegistry registry) {
this.configurers.addFormatters(registry);
}
如果想保留 Spring Boot MVC的特性,而且还想扩展新的功能(拦截器, 格式化器, 视图控制器等),你可以在你自
定义的 WebMvcConfigurer 类上增加 @Configuration 注解。
如果你想全面控制SpringMVC(也就是不使用默认配置功能), 你在自定义的Web配置类上添加
@Configuration 和 @EnableWebMvc 注解。
<mvc:view-controller path="/mengxuegu" view-name="success"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean>bean>
mvc:interceptor>
mvc:interceptors>
package com.mengxuegu.springboot.config;
......
/**
* @Auther: www.mengxuegu.com
*/
@Configuration
public class MySpringMvcConfigurer implements WebMvcConfigurer{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//发送 /mengxuegu 请求来到 success.html
registry.addViewController("/mengxuegu").setViewName("success");
}
}
原理:
导入EnableWebMvcConfiguration.class
@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer,
ResourceLoaderAware {
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
//存储所有的mvc配置类组件
private final WebMvcConfigurerComposite configurers =
new WebMvcConfigurerComposite();
@Autowired( required = false )
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
/*
一个参考实现;将所有的WebMvcConfigurer相关配置都来一起调用;
public void addViewControllers(ViewControllerRegistry registry) {
Iterator var2 = this.delegates.iterator();
while(var2.hasNext()) {
WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
delegate.addViewControllers(registry);
}
}
*/
}
}
如果你想全面控制SpringMVC(SpringBoot对SpringMVC的自动配置都废弃), 在自定义的Web配置类上添加@Configuration 和 @EnableWebMvc 注解。
/**
* @Auther: www.mengxuegu.com
*/
@EnableWebMvc
@Configuration
public class MySpringMvcConfigurer implements WebMvcConfigurer{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//发送 /mengxuegu 请求来到 success.html
registry.addViewController("/mengxuegu").setViewName("success");
}
}
原理: 为什么添加 @EnableWebMvc 自动配置就失效了?
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
容器中没有这个组件的时候,这个自动配置类才生效
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class,ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
**而 @ConditionalOnMissingBean 表示的是没有WebMvcConfigurationSupport这个组件,**
**WebMvcAutoConfiguration自动配置类才会生效.**