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"); } }