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

并在pom.xml文件中引入webjars包,
<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 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zk.myspringboot_011</groupId> <artifactId>myspringboot_011</artifactId> <packaging>jar</packaging> <version>0.0.1-SNAPSHOT</version> <name>myspringboot_011 Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- 继承父包 --> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.3.1-1</version> </dependency> </dependencies> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.3.RELEASE</version> </parent> <build> <finalName>myspringboot_011</finalName> </build> </project>
核心代码:
<dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.3.1-1</version> </dependency>
可以参考一下这个引入静态资源的网站https://www.webjars.org/

然后我们通过浏览器访问一下web静态资源,路径为http://localhost:8080/webjars/jquery/3.3.1-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目录下放a.png文件

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

为什么可以访问以上四个路径下的所有静态资源,我们可以通过查看org.springframework.boot.autoconfigure.web包下面的ResourceProperties.class类的源代码分析其原因:
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.web;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
/**
* Properties used to configure resource handling.
*
* @author Phillip Webb
* @author Brian Clozel
* @author Dave Syer
* @author Venil Noronha
* @since 1.1.0
*/
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties implements ResourceLoaderAware {
private static final String[] SERVLET_RESOURCE_LOCATIONS = { "/" };
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };
private static final String[] RESOURCE_LOCATIONS;
static {
RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length
+ SERVLET_RESOURCE_LOCATIONS.length];
System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, 0,
SERVLET_RESOURCE_LOCATIONS.length);
System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS,
SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length);
}
/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/] plus context:/ (the root of the servlet context).
*/
private String[] staticLocations = RESOURCE_LOCATIONS;
/**
* Cache period for the resources served by the resource handler, in seconds.
*/
private Integer cachePeriod;
/**
* Enable default resource handling.
*/
private boolean addMappings = true;
private final Chain chain = new Chain();
private ResourceLoader resourceLoader;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public String[] getStaticLocations() {
return this.staticLocations;
}
public void setStaticLocations(String[] staticLocations) {
this.staticLocations = staticLocations;
}
public Resource getWelcomePage() {
for (String location : getStaticWelcomePageLocations()) {
Resource resource = this.resourceLoader.getResource(location);
try {
if (resource.exists()) {
resource.getURL();
return resource;
}
}
catch (Exception ex) {
// Ignore
}
}
return null;
}
private String[] getStaticWelcomePageLocations() {
String[] result = new String[this.staticLocations.length];
for (int i = 0; i < result.length; i++) {
String location = this.staticLocations[i];
if (!location.endsWith("/")) {
location = location + "/";
}
result[i] = location + "index.html";
}
return result;
}
List<Resource> getFaviconLocations() {
List<Resource> locations = new ArrayList<Resource>(
this.staticLocations.length + 1);
if (this.resourceLoader != null) {
for (String location : this.staticLocations) {
locations.add(this.resourceLoader.getResource(location));
}
}
locations.add(new ClassPathResource("/"));
return Collections.unmodifiableList(locations);
}
public Integer getCachePeriod() {
return this.cachePeriod;
}
public void setCachePeriod(Integer cachePeriod) {
this.cachePeriod = cachePeriod;
}
public boolean isAddMappings() {
return this.addMappings;
}
public void setAddMappings(boolean addMappings) {
this.addMappings = addMappings;
}
public Chain getChain() {
return this.chain;
}
/**
* Configuration for the Spring Resource Handling chain.
*/
public static class Chain {
/**
* Enable the Spring Resource Handling chain. Disabled by default unless at least
* one strategy has been enabled.
*/
private Boolean enabled;
/**
* Enable caching in the Resource chain.
*/
private boolean cache = true;
/**
* Enable HTML5 application cache manifest rewriting.
*/
private boolean htmlApplicationCache = false;
/**
* Enable resolution of already gzipped resources. Checks for a resource name
* variant with the "*.gz" extension.
*/
private boolean gzipped = false;
@NestedConfigurationProperty
private final Strategy strategy = new Strategy();
/**
* Return whether the resource chain is enabled. Return {@code null} if no
* specific settings are present.
* @return whether the resource chain is enabled or {@code null} if no specified
* settings are present.
*/
public Boolean getEnabled() {
Boolean strategyEnabled = getStrategy().getFixed().isEnabled()
|| getStrategy().getContent().isEnabled();
return (strategyEnabled ? Boolean.TRUE : 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 Strategy getStrategy() {
return this.strategy;
}
public boolean isHtmlApplicationCache() {
return this.htmlApplicationCache;
}
public void setHtmlApplicationCache(boolean htmlApplicationCache) {
this.htmlApplicationCache = htmlApplicationCache;
}
public boolean isGzipped() {
return this.gzipped;
}
public void setGzipped(boolean gzipped) {
this.gzipped = gzipped;
}
}
/**
* Strategies for extracting and embedding a resource version in its URL path.
*/
public static class Strategy {
@NestedConfigurationProperty
private final Fixed fixed = new Fixed();
@NestedConfigurationProperty
private final Content content = new Content();
public Fixed getFixed() {
return this.fixed;
}
public Content getContent() {
return this.content;
}
}
/**
* Version Strategy based on content hashing.
*/
public static class Content {
/**
* Enable the content Version Strategy.
*/
private boolean enabled;
/**
* Comma-separated list of patterns to apply to the Version Strategy.
*/
private String[] paths = new String[] { "/**" };
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;
}
}
/**
* Version Strategy based on a fixed version string.
*/
public static class Fixed {
/**
* Enable the fixed Version Strategy.
*/
private boolean enabled;
/**
* Comma-separated list of patterns to apply to the Version Strategy.
*/
private String[] paths = new String[] { "/**" };
/**
* Version string to use for the Version Strategy.
*/
private String version;
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;
}
}
}
核心代码:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };
这里定义了我们的四个访问路径,因此,静态资源可以通过这四个访问路径进行访问。
方式三:可以自定义静态资源进行访问

新建一个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:/static,classpath:/public,classpath:/resources,classpath:/META-INF/resources

访问localhost:8080/static/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资源,否则无法访问。

分析其原因可以查看源代码,访问spring-boot-autoconfigure-1.4.3.RELEASE.jar 包下的org.springframework.boot.autoconfigure.thymeleaf中的ThymeleafProperties.class文件
@ConfigurationProperties("spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8");
private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html");
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
从该class文件中可以分析到,访问静态资源的路径必须为classpath:/templates/,且静态资源为.html
我们举个例子,
我们建个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">
在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");
}
}