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");
    }
}
posted @   leagueandlegends  阅读(993)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示