SpringBoot学习(九) ------访问静态web资源
SpringBoot可以通过3种方式来获取静态资源
方式一:访问所有webjars/**,访问静态资源的路径为META-INF/resources/webjars,可以在此路径中查找静态资源。
举个例子:
新建一个项目工程
并在pom.xml文件中引入webjars包,
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | <?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> |
核心代码:
1 2 3 4 5 | <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类的源代码分析其原因:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @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静态资源(静态资源的文件夹)
1 2 3 4 5 | 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类的源代码分析其原因:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | // // 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; } } } |
核心代码:
1 | private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{ "classpath:/META-INF/resources/" , "classpath:/resources/" , "classpath:/static/" , "classpath:/public/" }; |
这里定义了我们的四个访问路径,因此,静态资源可以通过这四个访问路径进行访问。
配置欢迎页映射
1 2 3 4 5 6 7 | @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()看到页面被"/**"映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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
1 2 3 4 5 6 7 8 9 | <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文件夹。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | 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设置进行访问
1 | 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文件中添加如下代码:
1 2 3 4 | <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> |
加入thymeleaf依赖,引入jar包。
thymeleaf的默认静态资源路径为classpath:/templates/,必须放置在此路径下,才能够访问到静态thymeleaf资源,否则无法访问。
项目结构如下:
HelloController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | <?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
1 2 3 4 5 6 7 8 9 10 | <!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文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <!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,则需要引入标签:
1 | <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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 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后台封装代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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" ; } } } |
同样设置启动项:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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配置文件内容:
1 | spring.resources. static -locations=classpath\:/ static ,classpath\:/ public ,classpath\:/resources,classpath\:/META-INF/resources |
我们访问一下thymeleaf静态资源网页,访问效果如下:
访问成功
SpringBoot中引入freemark
首先在pom.xml文件中引入freemarker的依赖
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <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页面文件
1 2 3 4 5 | this is itmayiedu<br> ${name} <#list userlist as user> ${user} </#list> |
写业务controller文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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" ; } } |
最后写启动文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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
1 2 3 4 5 6 7 8 9 10 11 12 | <%@ 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
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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
1 2 | spring.mvc.view.prefix=/WEB-INF/jsp/ spring.mvc.view.suffix=.jsp |
依赖文件:pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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" ); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)