SpringBoot学习(九) ------访问静态web资源
SpringBoot可以通过3种方式来获取静态资源
方式一:访问所有webjars/**,访问静态资源的路径为META-INF/resources/webjars,可以在此路径中查找静态资源。
举个例子:
新建一个项目工程

并在pom.xml文件中引入webjars包,
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.example.demo.DemoApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
核心代码:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.2.1</version>
</dependency>
可以参考一下这个引入静态资源的网站https://www.webjars.org/

然后我们通过浏览器访问一下web静态资源,路径为http://localhost:8080/webjars/jquery/3.2.1/jquery.js

为什么可以访问webjars目录下的所有静态资源,我们可以通过查看org.springframework.boot.autoconfigure.web包下面的WebMvcAutoConfiguration.class类的源代码分析其原因:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Integer cachePeriod = this.resourceProperties.getCachePeriod();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(
registry.addResourceHandler("/webjars/**")
.addResourceLocations(
"classpath:/META-INF/resources/webjars/")
.setCachePeriod(cachePeriod));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(
registry.addResourceHandler(staticPathPattern)
.addResourceLocations(
this.resourceProperties.getStaticLocations())
.setCachePeriod(cachePeriod));
}
}
这里直接给staticPathPattern赋值,然后将该值赋给了资源访问路径方法。所以能够通过/webjars/**对静态资源进行访问。
方式二:./**访问当前项目的任何资源
可以通过以下四种路径放置web静态资源,并访问web静态资源(静态资源的文件夹)
classpath:/META-INF/resources/* classpath:/resources/* classpath:/static/* classpath:/public/* "/":当前项目的根路径
这里四个路径的优先级顺序分别从上到下依次递减。
举个例子:
在项目resource文件夹下建一个static静态文件夹,在image目录下放beautiful.jpeg文件

我们访问这个路径:localhost:8080/image/a.png,可以访问到a.png静态资源

为什么可以访问以上四个路径下的所有静态资源,我们可以通过查看org.springframework.boot.autoconfigure.web包下面的ResourceProperties.class类的源代码分析其原因:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.boot.autoconfigure.web;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.http.CacheControl;
@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;
public ResourceProperties() {
this.staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
this.addMappings = true;
this.chain = new ResourceProperties.Chain();
this.cache = new ResourceProperties.Cache();
}
public String[] getStaticLocations() {
return this.staticLocations;
}
public void setStaticLocations(String[] staticLocations) {
this.staticLocations = this.appendSlashIfNecessary(staticLocations);
}
private String[] appendSlashIfNecessary(String[] staticLocations) {
String[] normalized = new String[staticLocations.length];
for(int i = 0; i < staticLocations.length; ++i) {
String location = staticLocations[i];
normalized[i] = location.endsWith("/") ? location : location + "/";
}
return normalized;
}
public boolean isAddMappings() {
return this.addMappings;
}
public void setAddMappings(boolean addMappings) {
this.addMappings = addMappings;
}
public ResourceProperties.Chain getChain() {
return this.chain;
}
public ResourceProperties.Cache getCache() {
return this.cache;
}
public static class Cache {
@DurationUnit(ChronoUnit.SECONDS)
private Duration period;
private final ResourceProperties.Cache.Cachecontrol cachecontrol = new ResourceProperties.Cache.Cachecontrol();
public Cache() {
}
public Duration getPeriod() {
return this.period;
}
public void setPeriod(Duration period) {
this.period = period;
}
public ResourceProperties.Cache.Cachecontrol getCachecontrol() {
return this.cachecontrol;
}
public static class Cachecontrol {
@DurationUnit(ChronoUnit.SECONDS)
private Duration maxAge;
private Boolean noCache;
private Boolean noStore;
private Boolean mustRevalidate;
private Boolean noTransform;
private Boolean cachePublic;
private Boolean cachePrivate;
private Boolean proxyRevalidate;
@DurationUnit(ChronoUnit.SECONDS)
private Duration staleWhileRevalidate;
@DurationUnit(ChronoUnit.SECONDS)
private Duration staleIfError;
@DurationUnit(ChronoUnit.SECONDS)
private Duration sMaxAge;
public Cachecontrol() {
}
public Duration getMaxAge() {
return this.maxAge;
}
public void setMaxAge(Duration maxAge) {
this.maxAge = maxAge;
}
public Boolean getNoCache() {
return this.noCache;
}
public void setNoCache(Boolean noCache) {
this.noCache = noCache;
}
public Boolean getNoStore() {
return this.noStore;
}
public void setNoStore(Boolean noStore) {
this.noStore = noStore;
}
public Boolean getMustRevalidate() {
return this.mustRevalidate;
}
public void setMustRevalidate(Boolean mustRevalidate) {
this.mustRevalidate = mustRevalidate;
}
public Boolean getNoTransform() {
return this.noTransform;
}
public void setNoTransform(Boolean noTransform) {
this.noTransform = noTransform;
}
public Boolean getCachePublic() {
return this.cachePublic;
}
public void setCachePublic(Boolean cachePublic) {
this.cachePublic = cachePublic;
}
public Boolean getCachePrivate() {
return this.cachePrivate;
}
public void setCachePrivate(Boolean cachePrivate) {
this.cachePrivate = cachePrivate;
}
public Boolean getProxyRevalidate() {
return this.proxyRevalidate;
}
public void setProxyRevalidate(Boolean proxyRevalidate) {
this.proxyRevalidate = proxyRevalidate;
}
public Duration getStaleWhileRevalidate() {
return this.staleWhileRevalidate;
}
public void setStaleWhileRevalidate(Duration staleWhileRevalidate) {
this.staleWhileRevalidate = staleWhileRevalidate;
}
public Duration getStaleIfError() {
return this.staleIfError;
}
public void setStaleIfError(Duration staleIfError) {
this.staleIfError = staleIfError;
}
public Duration getSMaxAge() {
return this.sMaxAge;
}
public void setSMaxAge(Duration sMaxAge) {
this.sMaxAge = sMaxAge;
}
public CacheControl toHttpCacheControl() {
PropertyMapper map = PropertyMapper.get();
CacheControl control = this.createCacheControl();
map.from(this::getMustRevalidate).whenTrue().toCall(control::mustRevalidate);
map.from(this::getNoTransform).whenTrue().toCall(control::noTransform);
map.from(this::getCachePublic).whenTrue().toCall(control::cachePublic);
map.from(this::getCachePrivate).whenTrue().toCall(control::cachePrivate);
map.from(this::getProxyRevalidate).whenTrue().toCall(control::proxyRevalidate);
map.from(this::getStaleWhileRevalidate).whenNonNull().to((duration) -> {
control.staleWhileRevalidate(duration.getSeconds(), TimeUnit.SECONDS);
});
map.from(this::getStaleIfError).whenNonNull().to((duration) -> {
control.staleIfError(duration.getSeconds(), TimeUnit.SECONDS);
});
map.from(this::getSMaxAge).whenNonNull().to((duration) -> {
control.sMaxAge(duration.getSeconds(), TimeUnit.SECONDS);
});
return control.getHeaderValue() == null ? null : control;
}
private CacheControl createCacheControl() {
if (Boolean.TRUE.equals(this.noStore)) {
return CacheControl.noStore();
} else if (Boolean.TRUE.equals(this.noCache)) {
return CacheControl.noCache();
} else {
return this.maxAge != null ? CacheControl.maxAge(this.maxAge.getSeconds(), TimeUnit.SECONDS) : CacheControl.empty();
}
}
}
}
public static class Fixed {
private boolean enabled;
private String[] paths = new String[]{"/**"};
private String version;
public Fixed() {
}
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String[] getPaths() {
return this.paths;
}
public void setPaths(String[] paths) {
this.paths = paths;
}
public String getVersion() {
return this.version;
}
public void setVersion(String version) {
this.version = version;
}
}
public static class Content {
private boolean enabled;
private String[] paths = new String[]{"/**"};
public Content() {
}
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String[] getPaths() {
return this.paths;
}
public void setPaths(String[] paths) {
this.paths = paths;
}
}
public static class Strategy {
private final ResourceProperties.Fixed fixed = new ResourceProperties.Fixed();
private final ResourceProperties.Content content = new ResourceProperties.Content();
public Strategy() {
}
public ResourceProperties.Fixed getFixed() {
return this.fixed;
}
public ResourceProperties.Content getContent() {
return this.content;
}
}
public static class Chain {
private Boolean enabled;
private boolean cache = true;
private boolean htmlApplicationCache = false;
private boolean compressed = false;
private final ResourceProperties.Strategy strategy = new ResourceProperties.Strategy();
public Chain() {
}
public Boolean getEnabled() {
return getEnabled(this.getStrategy().getFixed().isEnabled(), this.getStrategy().getContent().isEnabled(), this.enabled);
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isCache() {
return this.cache;
}
public void setCache(boolean cache) {
this.cache = cache;
}
public ResourceProperties.Strategy getStrategy() {
return this.strategy;
}
public boolean isHtmlApplicationCache() {
return this.htmlApplicationCache;
}
public void setHtmlApplicationCache(boolean htmlApplicationCache) {
this.htmlApplicationCache = htmlApplicationCache;
}
public boolean isCompressed() {
return this.compressed;
}
public void setCompressed(boolean compressed) {
this.compressed = compressed;
}
static Boolean getEnabled(boolean fixedEnabled, boolean contentEnabled, Boolean chainEnabled) {
return !fixedEnabled && !contentEnabled ? chainEnabled : Boolean.TRUE;
}
}
}
核心代码:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
这里定义了我们的四个访问路径,因此,静态资源可以通过这四个访问路径进行访问。
配置欢迎页映射
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
return welcomePageHandlerMapping;
}
可以跟踪this.mvcProperties.getStaticPathPattern()看到页面被"/**"映射
private org.springframework.validation.DefaultMessageCodesResolver.Format messageCodesResolverFormat;
private Locale locale;
private WebMvcProperties.LocaleResolver localeResolver;
private final WebMvcProperties.Format format;
private boolean dispatchTraceRequest;
private boolean dispatchOptionsRequest;
private boolean ignoreDefaultModelOnRedirect;
private boolean publishRequestHandledEvents;
private boolean throwExceptionIfNoHandlerFound;
private boolean logRequestDetails;
private boolean logResolvedException;
private String staticPathPattern;
private final WebMvcProperties.Async async;
private final WebMvcProperties.Servlet servlet;
private final WebMvcProperties.View view;
private final WebMvcProperties.Contentnegotiation contentnegotiation;
private final WebMvcProperties.Pathmatch pathmatch;
public WebMvcProperties() {
this.localeResolver = WebMvcProperties.LocaleResolver.ACCEPT_HEADER;
this.format = new WebMvcProperties.Format();
this.dispatchTraceRequest = false;
this.dispatchOptionsRequest = true;
this.ignoreDefaultModelOnRedirect = true;
this.publishRequestHandledEvents = 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();
}

当在/resources/public的目录下创建index.html文件之后,默认的访问首页为此index.html
index.html
<html>
<head>
<meta charset="utf-8">
<title>首页</title>
</head>
<body>
<h1 size="10px">首页</h1>
</body>
</html>
运行SpringBoot程序,访问http://localhost:8080/

方式三:可以自定义静态资源进行访问

新建一个MyWebMvcConfig.java,重写WebMvcConfigurer接口,这里我只重写了addResourceHandlers方法,定义了一个test文件夹。
package com.zk.Springboot;
import java.util.List;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 静态资源映射
*/
@Component
public class MyWebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/test/**")
.addResourceLocations("classpath:/test/");
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> arg0) {
// TODO Auto-generated method stub
}
@Override
public void addCorsMappings(CorsRegistry arg0) {
// TODO Auto-generated method stub
}
@Override
public void addFormatters(FormatterRegistry arg0) {
// TODO Auto-generated method stub
}
@Override
public void addInterceptors(InterceptorRegistry arg0) {
// TODO Auto-generated method stub
}
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> arg0) {
// TODO Auto-generated method stub
}
@Override
public void addViewControllers(ViewControllerRegistry arg0) {
// TODO Auto-generated method stub
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer arg0) {
// TODO Auto-generated method stub
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer arg0) {
// TODO Auto-generated method stub
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer arg0) {
// TODO Auto-generated method stub
}
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {
// TODO Auto-generated method stub
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> arg0) {
// TODO Auto-generated method stub
}
@Override
public void configurePathMatch(PathMatchConfigurer arg0) {
// TODO Auto-generated method stub
}
@Override
public void configureViewResolvers(ViewResolverRegistry arg0) {
// TODO Auto-generated method stub
}
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {
// TODO Auto-generated method stub
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> arg0) {
// TODO Auto-generated method stub
}
@Override
public MessageCodesResolver getMessageCodesResolver() {
// TODO Auto-generated method stub
return null;
}
@Override
public Validator getValidator() {
// TODO Auto-generated method stub
return null;
}
}
访问刚才自定义路径http://localhost:8080/test/a.png

同样能够访问我们的静态资源文件。
方式四:可以在application.properties设置进行访问
spring.resources.static-locations=classpath:/hello,classpath:/atguigu/
当更改了静态资源的访问路径时,访问localhost:8080,此时无法访问到/resources/public/index.html文件


SpringBoot中引入thymeleaf
首先我们介绍一下什么是thymeleaf
thymeleaf是个XML/XHTML/HTML5模板引擎,可以用于Web与非Web应用。
我们现在需要使用thymeleaf,将thymeleaf引入springboot中。
在pom.xml文件中添加如下代码:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
加入thymeleaf依赖,引入jar包。

thymeleaf的默认静态资源路径为classpath:/templates/,必须放置在此路径下,才能够访问到静态thymeleaf资源,否则无法访问。
项目结构如下:


HelloController.java
package com.example.demo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@ResponseBody
@RequestMapping("/hello")
public String hello(){
// 默认要去classpath:templates下查找success.html
return "success";
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.example.demo.DemoApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
classpath:templates目录下的success.html文件
success.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>success</title>
</head>
<body>
<h1>success</h1>
</body>
</html>
分析其原因可以查看源代码,访问spring-boot-autoconfigure-1.4.3.RELEASE.jar 包下的org.springframework.boot.autoconfigure.thymeleaf中的ThymeleafProperties.class文件
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
private boolean cache;
private Integer templateResolverOrder;
private String[] viewNames;
private String[] excludedViewNames;
private boolean enableSpringElCompiler;
private boolean renderHiddenMarkersBeforeCheckboxes;
private boolean enabled;
private final ThymeleafProperties.Servlet servlet;
private final ThymeleafProperties.Reactive reactive;
从该class文件中可以分析到,访问静态资源的路径必须为classpath:/templates/,且静态资源为.html。当html页面放在classpath:/templates目录下时,thymeleaf就可以自动渲染。
我们举个例子,
我们建个springboot项目,项目目录如下:

首先在classpath:/templates/构建success.html文件,文件内容如下:
success.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<title>success.html</title>
<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
</head>
<body>
success,helloworld <br/>
<!-- 将div中的文本内容设置成自定义值 -->
<h1 th:text="${hello}" th:id="${hello}" th:class="${hello}"></h1>
<h1 th:utext="${hello}"></h1>
<!-- 遍历user -->
<div th:each="user:${users}">
<span th:text="${user}"></span>
</div>
<table border="1">
<thead>
<th>学生id</th>
<th>学生姓名</th>
<th>学生年龄</th>
</thead>
<tbody>
<tr th:each="entries:${resultList}">
<td th:text="${entries['sid']}"></td>
<td th:text="${entries['sname']}"></td>
<td th:text="${entries['sage']}"></td>
</tr>
</tbody>
</table>
</body>
</html>
如果想使用thymeleaf,则需要引入标签:
<html xmlns:th="http://www.thymeleaf.org">
th:text改变当前元素里面的文本内容,th:class th:id可以修改任意的html属性,替换原生class、id
thymeleaf官方文档中的标签,th官方文档 https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#attribute-precedence

thymeleaf的表达式语法 https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#standard-expression-syntax
Simple expressions:
Variable Expressions: ${...}
(1)获取对象的属性、调用方法
(2)使用内置的基本对象
#ctx: the context object.
#vars: the context variables.
#locale: the context locale.
#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.
(3)内置一些工具对象
Selection Variable Expressions: *{...}选择表达式:和${}在功能上是一样的
补充:配合 th:object="${session.user}"
<div th:object="${session.user}">
Message Expressions: #{...} 获取国际化内容
Link URL Expressions: @{...}定义url链接
<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
Fragment Expressions: ~{...}:片段引用表达式
Literals(字面量)
Text literals: 'one text', 'Another one!',…
Number literals: 0, 34, 3.0, 12.3,…
Boolean literals: true, false
Null literal: null
Literal tokens: one, sometext, main,…
Text operations:(文本操作)
String concatenation: +
Literal substitutions: |The name is ${name}|
Arithmetic operations:(数学运算)
Binary operators: +, -, *, /, %
Minus sign (unary operator): -
Boolean operations:(布尔运算)
Binary operators: and, or
Boolean negation (unary operator): !, not
Comparisons and equality:(比较运算)
Comparators: >, <, >=, <= (gt, lt, ge, le)
Equality operators: ==, != (eq, ne)
Conditional operators:(条件运算)
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)三元运算符
Default: (value) ?: (defaultvalue)
Special tokens:(特殊字符)
No-Operation: _
在success.html文件中访问了user参数列表和resultList,在HelloController后台封装代码如下:
package com.zk.Springboot;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/hello")
public String hello() {
return "hello";
}
//插入一些数据在页面显示
@RequestMapping("/success")
public String successs(Map<String,Object> map) {
map.put("hello", "你好");
map.put("users", Arrays.asList("zhang","zhao","qian"));
return "success";
}
@Controller
@RequestMapping("studentMgmt")
public class StudentController {
@RequestMapping("queryStudentInfo")
public String queryStudentInfo(Model model) {
List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>();
Map<String, Object> student = new HashMap<String, Object>(){{
put("sid", "101");
put("sname", "张三");
put("sage", "20");
}};
resultList.add(student);
student = new HashMap<String, Object>(){{
put("sid", "102");
put("sname", "李四");
put("sage", "30");
}};
resultList.add(student);
model.addAttribute("resultList", resultList);
return "success";
}
}
}
同样设置启动项:
package com.zk.Springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootApplicationFirst {
public static void main(String[]args){
SpringApplication.run(SpringBootApplicationFirst.class, args);
}
public static void run(String...arg0) {
System.out.println("Hello world from Command Line Runner");
}
}
application.properties配置文件内容:
spring.resources.static-locations=classpath\:/static,classpath\:/public,classpath\:/resources,classpath\:/META-INF/resources
我们访问一下thymeleaf静态资源网页,访问效果如下:


访问成功
SpringBoot中引入freemark

首先在pom.xml文件中引入freemarker的依赖
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>myspringboot003</groupId> <artifactId>myspringboot003</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.3.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> </dependencies> </project>
接着,写ftl页面文件
this is itmayiedu<br>
${name}
<#list userlist as user>
${user}
</#list>
写业务controller文件
package com.zk.myspringboot003;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
//标识该接口全部返回json格式
@Controller
public class indexController {
@RequestMapping("/indexController")
public String indexController(Map<String,Object> result) {
result.put("name", "zk");
List<String> list=new ArrayList<String>();
list.add("zhangkun");
list.add("lisi");
list.add("26");
result.put("userlist", list);
return "index";
}
}
最后写启动文件
package com.zk.myspringboot003;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableAutoConfiguration
@SpringBootApplication
public class SpringBootApplicationThird {
public static void main(String[]args){
SpringApplication.run(SpringBootApplicationThird.class, args);
}
public static void run(String...arg0) {
System.out.println("Hello world from Command Line Runner");
}
}
运行结果如下:

SpringBoot中引入JSP
需要注意的是,在创建MAVEN工程项目的时候需要选择war,而不是我们平时选择的jar包


页面:index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
springboot 你好
</body>
</html>
Controller类:IndexController.java
package com.zk.myspringboot004;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping("/index")
public String index() {
return "index";
}
}
application属性文件:application.properties
spring.mvc.view.prefix=/WEB-INF/jsp/ spring.mvc.view.suffix=.jsp
依赖文件:pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.myspringboot004</groupId> <artifactId>myspringboot004</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.3.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> </dependencies> </project>
启动程序:SpringBootApplicationTwice.java
package com.zk.myspringboot004;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableAutoConfiguration
@SpringBootApplication
public class SpringBootApplicationTwice {
public static void main(String[]args){
SpringApplication.run(SpringBootApplicationTwice.class, args);
}
public static void run(String...arg0) {
System.out.println("Hello world from Command Line Runner");
}
}

浙公网安备 33010602011771号