1
1
/*
2
- * Copyright 2012-2020 the original author or authors.
2
+ * Copyright 2012-2021 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
17
17
package org .springframework .boot .autoconfigure .web .servlet ;
18
18
19
19
import java .time .Duration ;
20
- import java .util .Arrays ;
20
+ import java .util .HashSet ;
21
21
import java .util .List ;
22
22
import java .util .ListIterator ;
23
23
import java .util .Map ;
24
- import java .util .Optional ;
24
+ import java .util .Set ;
25
25
26
26
import javax .servlet .Servlet ;
27
+ import javax .servlet .ServletContext ;
27
28
28
29
import org .apache .commons .logging .Log ;
29
30
import org .apache .commons .logging .LogFactory ;
70
71
import org .springframework .core .task .AsyncTaskExecutor ;
71
72
import org .springframework .format .FormatterRegistry ;
72
73
import org .springframework .format .support .FormattingConversionService ;
73
- import org .springframework .http .CacheControl ;
74
74
import org .springframework .http .MediaType ;
75
75
import org .springframework .http .converter .HttpMessageConverter ;
76
76
import org .springframework .util .ClassUtils ;
77
+ import org .springframework .util .PathMatcher ;
77
78
import org .springframework .validation .DefaultMessageCodesResolver ;
78
79
import org .springframework .validation .MessageCodesResolver ;
79
80
import org .springframework .validation .Validator ;
85
86
import org .springframework .web .context .request .NativeWebRequest ;
86
87
import org .springframework .web .context .request .RequestAttributes ;
87
88
import org .springframework .web .context .request .RequestContextListener ;
89
+ import org .springframework .web .context .support .ServletContextResource ;
88
90
import org .springframework .web .filter .FormContentFilter ;
89
91
import org .springframework .web .filter .HiddenHttpMethodFilter ;
90
92
import org .springframework .web .filter .RequestContextFilter ;
91
93
import org .springframework .web .servlet .DispatcherServlet ;
92
94
import org .springframework .web .servlet .HandlerExceptionResolver ;
95
+ import org .springframework .web .servlet .HandlerMapping ;
93
96
import org .springframework .web .servlet .LocaleResolver ;
94
97
import org .springframework .web .servlet .View ;
95
98
import org .springframework .web .servlet .ViewResolver ;
104
107
import org .springframework .web .servlet .config .annotation .WebMvcConfigurationSupport ;
105
108
import org .springframework .web .servlet .config .annotation .WebMvcConfigurer ;
106
109
import org .springframework .web .servlet .handler .AbstractHandlerExceptionResolver ;
110
+ import org .springframework .web .servlet .handler .SimpleUrlHandlerMapping ;
107
111
import org .springframework .web .servlet .i18n .AcceptHeaderLocaleResolver ;
108
112
import org .springframework .web .servlet .i18n .FixedLocaleResolver ;
109
113
import org .springframework .web .servlet .mvc .method .annotation .ExceptionHandlerExceptionResolver ;
110
114
import org .springframework .web .servlet .mvc .method .annotation .RequestMappingHandlerAdapter ;
111
115
import org .springframework .web .servlet .mvc .method .annotation .RequestMappingHandlerMapping ;
112
116
import org .springframework .web .servlet .resource .AppCacheManifestTransformer ;
113
117
import org .springframework .web .servlet .resource .EncodedResourceResolver ;
118
+ import org .springframework .web .servlet .resource .ResourceHttpRequestHandler ;
114
119
import org .springframework .web .servlet .resource .ResourceResolver ;
115
120
import org .springframework .web .servlet .resource .ResourceUrlProvider ;
116
121
import org .springframework .web .servlet .resource .VersionResourceResolver ;
117
122
import org .springframework .web .servlet .view .BeanNameViewResolver ;
118
123
import org .springframework .web .servlet .view .ContentNegotiatingViewResolver ;
119
124
import org .springframework .web .servlet .view .InternalResourceViewResolver ;
125
+ import org .springframework .web .util .UrlPathHelper ;
120
126
121
127
/**
122
128
* {@link EnableAutoConfiguration Auto-configuration} for {@link EnableWebMvc Web MVC}.
@@ -151,7 +157,7 @@ public class WebMvcAutoConfiguration {
151
157
*/
152
158
public static final String DEFAULT_SUFFIX = "" ;
153
159
154
- private static final String [] SERVLET_LOCATIONS = { "/" } ;
160
+ private static final String SERVLET_LOCATION = "/" ;
155
161
156
162
@ Bean
157
163
@ ConditionalOnMissingBean (HiddenHttpMethodFilter .class )
@@ -167,13 +173,6 @@ public OrderedFormContentFilter formContentFilter() {
167
173
return new OrderedFormContentFilter ();
168
174
}
169
175
170
- static String [] getResourceLocations (String [] staticLocations ) {
171
- String [] locations = new String [staticLocations .length + SERVLET_LOCATIONS .length ];
172
- System .arraycopy (staticLocations , 0 , locations , 0 , staticLocations .length );
173
- System .arraycopy (SERVLET_LOCATIONS , 0 , locations , staticLocations .length , SERVLET_LOCATIONS .length );
174
- return locations ;
175
- }
176
-
177
176
// Defined as a nested config to ensure WebMvcConfigurer is not read when not
178
177
// on the classpath
179
178
@ Configuration (proxyBeanMethods = false )
@@ -182,26 +181,17 @@ static String[] getResourceLocations(String[] staticLocations) {
182
181
@ Order (0 )
183
182
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
184
183
185
- private static final Log logger = LogFactory .getLog (WebMvcConfigurer .class );
186
-
187
- private final ResourceProperties resourceProperties ;
188
-
189
184
private final WebMvcProperties mvcProperties ;
190
185
191
186
private final ListableBeanFactory beanFactory ;
192
187
193
188
private final ObjectProvider <HttpMessageConverters > messageConvertersProvider ;
194
189
195
- final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer ;
196
-
197
- public WebMvcAutoConfigurationAdapter (ResourceProperties resourceProperties , WebMvcProperties mvcProperties ,
198
- ListableBeanFactory beanFactory , ObjectProvider <HttpMessageConverters > messageConvertersProvider ,
199
- ObjectProvider <ResourceHandlerRegistrationCustomizer > resourceHandlerRegistrationCustomizerProvider ) {
200
- this .resourceProperties = resourceProperties ;
190
+ public WebMvcAutoConfigurationAdapter (WebMvcProperties mvcProperties , ListableBeanFactory beanFactory ,
191
+ ObjectProvider <HttpMessageConverters > messageConvertersProvider ) {
201
192
this .mvcProperties = mvcProperties ;
202
193
this .beanFactory = beanFactory ;
203
194
this .messageConvertersProvider = messageConvertersProvider ;
204
- this .resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider .getIfAvailable ();
205
195
}
206
196
207
197
@ Override
@@ -303,37 +293,6 @@ public void addFormatters(FormatterRegistry registry) {
303
293
ApplicationConversionService .addBeans (registry , this .beanFactory );
304
294
}
305
295
306
- @ Override
307
- public void addResourceHandlers (ResourceHandlerRegistry registry ) {
308
- if (!this .resourceProperties .isAddMappings ()) {
309
- logger .debug ("Default resource handling disabled" );
310
- return ;
311
- }
312
- Duration cachePeriod = this .resourceProperties .getCache ().getPeriod ();
313
- CacheControl cacheControl = this .resourceProperties .getCache ().getCachecontrol ().toHttpCacheControl ();
314
- if (!registry .hasMappingForPattern ("/webjars/**" )) {
315
- customizeResourceHandlerRegistration (registry .addResourceHandler ("/webjars/**" )
316
- .addResourceLocations ("classpath:/META-INF/resources/webjars/" )
317
- .setCachePeriod (getSeconds (cachePeriod )).setCacheControl (cacheControl ));
318
- }
319
- String staticPathPattern = this .mvcProperties .getStaticPathPattern ();
320
- if (!registry .hasMappingForPattern (staticPathPattern )) {
321
- customizeResourceHandlerRegistration (registry .addResourceHandler (staticPathPattern )
322
- .addResourceLocations (getResourceLocations (this .resourceProperties .getStaticLocations ()))
323
- .setCachePeriod (getSeconds (cachePeriod )).setCacheControl (cacheControl ));
324
- }
325
- }
326
-
327
- private Integer getSeconds (Duration cachePeriod ) {
328
- return (cachePeriod != null ) ? (int ) cachePeriod .getSeconds () : null ;
329
- }
330
-
331
- private void customizeResourceHandlerRegistration (ResourceHandlerRegistration registration ) {
332
- if (this .resourceHandlerRegistrationCustomizer != null ) {
333
- this .resourceHandlerRegistrationCustomizer .customize (registration );
334
- }
335
- }
336
-
337
296
@ Bean
338
297
@ ConditionalOnMissingBean ({ RequestContextListener .class , RequestContextFilter .class })
339
298
@ ConditionalOnMissingFilterBean (RequestContextFilter .class )
@@ -349,22 +308,31 @@ public static RequestContextFilter requestContextFilter() {
349
308
@ Configuration (proxyBeanMethods = false )
350
309
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
351
310
311
+ private static final Log logger = LogFactory .getLog (WebMvcConfigurer .class );
312
+
352
313
private final ResourceProperties resourceProperties ;
353
314
354
315
private final WebMvcProperties mvcProperties ;
355
316
356
- private final ListableBeanFactory beanFactory ;
357
-
358
317
private final WebMvcRegistrations mvcRegistrations ;
359
318
319
+ private final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer ;
320
+
360
321
private ResourceLoader resourceLoader ;
361
322
323
+ private final ListableBeanFactory beanFactory ;
324
+
325
+ private final Set <String > autoConfiguredResourceHandlers = new HashSet <>();
326
+
362
327
public EnableWebMvcConfiguration (ResourceProperties resourceProperties ,
363
328
ObjectProvider <WebMvcProperties > mvcPropertiesProvider ,
364
- ObjectProvider <WebMvcRegistrations > mvcRegistrationsProvider , ListableBeanFactory beanFactory ) {
329
+ ObjectProvider <WebMvcRegistrations > mvcRegistrationsProvider ,
330
+ ObjectProvider <ResourceHandlerRegistrationCustomizer > resourceHandlerRegistrationCustomizerProvider ,
331
+ ListableBeanFactory beanFactory ) {
365
332
this .resourceProperties = resourceProperties ;
366
333
this .mvcProperties = mvcPropertiesProvider .getIfAvailable ();
367
334
this .mvcRegistrations = mvcRegistrationsProvider .getIfUnique ();
335
+ this .resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider .getIfAvailable ();
368
336
this .beanFactory = beanFactory ;
369
337
}
370
338
@@ -401,6 +369,72 @@ public RequestMappingHandlerMapping requestMappingHandlerMapping(
401
369
resourceUrlProvider );
402
370
}
403
371
372
+ @ Bean
373
+ @ Override
374
+ public HandlerMapping resourceHandlerMapping (UrlPathHelper urlPathHelper , PathMatcher pathMatcher ,
375
+ ContentNegotiationManager contentNegotiationManager , FormattingConversionService conversionService ,
376
+ ResourceUrlProvider resourceUrlProvider ) {
377
+ HandlerMapping mapping = super .resourceHandlerMapping (urlPathHelper , pathMatcher , contentNegotiationManager ,
378
+ conversionService , resourceUrlProvider );
379
+ if (mapping instanceof SimpleUrlHandlerMapping ) {
380
+ addServletContextResourceHandlerMapping ((SimpleUrlHandlerMapping ) mapping );
381
+ }
382
+ return mapping ;
383
+ }
384
+
385
+ private void addServletContextResourceHandlerMapping (SimpleUrlHandlerMapping mapping ) {
386
+ Map <String , ?> urlMap = mapping .getUrlMap ();
387
+ String pattern = this .mvcProperties .getStaticPathPattern ();
388
+ Object handler = urlMap .get (pattern );
389
+ if (handler instanceof ResourceHttpRequestHandler
390
+ && this .autoConfiguredResourceHandlers .contains (pattern )) {
391
+ addServletContextResourceHandlerMapping ((ResourceHttpRequestHandler ) handler );
392
+ }
393
+ }
394
+
395
+ private void addServletContextResourceHandlerMapping (ResourceHttpRequestHandler handler ) {
396
+ ServletContext servletContext = getServletContext ();
397
+ if (servletContext != null ) {
398
+ List <Resource > locations = handler .getLocations ();
399
+ locations .add (new ServletContextResource (servletContext , SERVLET_LOCATION ));
400
+ }
401
+ }
402
+
403
+ @ Override
404
+ protected void addResourceHandlers (ResourceHandlerRegistry registry ) {
405
+ super .addResourceHandlers (registry );
406
+ if (!this .resourceProperties .isAddMappings ()) {
407
+ logger .debug ("Default resource handling disabled" );
408
+ return ;
409
+ }
410
+ addResourceHandler (registry , "/webjars/**" , "classpath:/META-INF/resources/webjars/" );
411
+ addResourceHandler (registry , this .mvcProperties .getStaticPathPattern (),
412
+ this .resourceProperties .getStaticLocations ());
413
+
414
+ }
415
+
416
+ private void addResourceHandler (ResourceHandlerRegistry registry , String pattern , String ... locations ) {
417
+ if (registry .hasMappingForPattern (pattern )) {
418
+ return ;
419
+ }
420
+ ResourceHandlerRegistration registration = registry .addResourceHandler (pattern );
421
+ registration .addResourceLocations (locations );
422
+ registration .setCachePeriod (getSeconds (this .resourceProperties .getCache ().getPeriod ()));
423
+ registration .setCacheControl (this .resourceProperties .getCache ().getCachecontrol ().toHttpCacheControl ());
424
+ customizeResourceHandlerRegistration (registration );
425
+ this .autoConfiguredResourceHandlers .add (pattern );
426
+ }
427
+
428
+ private Integer getSeconds (Duration cachePeriod ) {
429
+ return (cachePeriod != null ) ? (int ) cachePeriod .getSeconds () : null ;
430
+ }
431
+
432
+ private void customizeResourceHandlerRegistration (ResourceHandlerRegistration registration ) {
433
+ if (this .resourceHandlerRegistrationCustomizer != null ) {
434
+ this .resourceHandlerRegistrationCustomizer .customize (registration );
435
+ }
436
+ }
437
+
404
438
@ Bean
405
439
public WelcomePageHandlerMapping welcomePageHandlerMapping (ApplicationContext applicationContext ,
406
440
FormattingConversionService mvcConversionService , ResourceUrlProvider mvcResourceUrlProvider ) {
@@ -412,22 +446,34 @@ public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext ap
412
446
return welcomePageHandlerMapping ;
413
447
}
414
448
415
- private Optional <Resource > getWelcomePage () {
416
- String [] locations = getResourceLocations (this .resourceProperties .getStaticLocations ());
417
- return Arrays .stream (locations ).map (this ::getIndexHtml ).filter (this ::isReadable ).findFirst ();
449
+ private Resource getWelcomePage () {
450
+ for (String location : this .resourceProperties .getStaticLocations ()) {
451
+ Resource indexHtml = getIndexHtml (location );
452
+ if (indexHtml != null ) {
453
+ return indexHtml ;
454
+ }
455
+ }
456
+ ServletContext servletContext = getServletContext ();
457
+ if (servletContext != null ) {
458
+ return getIndexHtml (new ServletContextResource (servletContext , SERVLET_LOCATION ));
459
+ }
460
+ return null ;
418
461
}
419
462
420
463
private Resource getIndexHtml (String location ) {
421
- return this .resourceLoader .getResource (location + "index.html" );
464
+ return getIndexHtml ( this .resourceLoader .getResource (location ) );
422
465
}
423
466
424
- private boolean isReadable (Resource resource ) {
467
+ private Resource getIndexHtml (Resource location ) {
425
468
try {
426
- return resource .exists () && (resource .getURL () != null );
469
+ Resource resource = location .createRelative ("index.html" );
470
+ if (resource .exists () && (resource .getURL () != null )) {
471
+ return resource ;
472
+ }
427
473
}
428
474
catch (Exception ex ) {
429
- return false ;
430
475
}
476
+ return null ;
431
477
}
432
478
433
479
@ Bean
0 commit comments