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 .Locale ;
24
24
import java .util .Map ;
25
- import java .util .Optional ;
25
+ import java .util .Set ;
26
26
27
27
import javax .servlet .Servlet ;
28
+ import javax .servlet .ServletContext ;
28
29
29
30
import org .apache .commons .logging .Log ;
30
31
import org .apache .commons .logging .LogFactory ;
74
75
import org .springframework .core .task .AsyncTaskExecutor ;
75
76
import org .springframework .format .FormatterRegistry ;
76
77
import org .springframework .format .support .FormattingConversionService ;
77
- import org .springframework .http .CacheControl ;
78
78
import org .springframework .http .MediaType ;
79
79
import org .springframework .http .converter .HttpMessageConverter ;
80
80
import org .springframework .util .ClassUtils ;
88
88
import org .springframework .web .context .request .NativeWebRequest ;
89
89
import org .springframework .web .context .request .RequestAttributes ;
90
90
import org .springframework .web .context .request .RequestContextListener ;
91
+ import org .springframework .web .context .support .ServletContextResource ;
91
92
import org .springframework .web .filter .FormContentFilter ;
92
93
import org .springframework .web .filter .HiddenHttpMethodFilter ;
93
94
import org .springframework .web .filter .RequestContextFilter ;
94
95
import org .springframework .web .servlet .DispatcherServlet ;
95
96
import org .springframework .web .servlet .FlashMapManager ;
96
97
import org .springframework .web .servlet .HandlerExceptionResolver ;
98
+ import org .springframework .web .servlet .HandlerMapping ;
97
99
import org .springframework .web .servlet .LocaleResolver ;
98
100
import org .springframework .web .servlet .ThemeResolver ;
99
101
import org .springframework .web .servlet .View ;
109
111
import org .springframework .web .servlet .config .annotation .WebMvcConfigurationSupport ;
110
112
import org .springframework .web .servlet .config .annotation .WebMvcConfigurer ;
111
113
import org .springframework .web .servlet .handler .AbstractHandlerExceptionResolver ;
114
+ import org .springframework .web .servlet .handler .SimpleUrlHandlerMapping ;
112
115
import org .springframework .web .servlet .i18n .AcceptHeaderLocaleResolver ;
113
116
import org .springframework .web .servlet .i18n .FixedLocaleResolver ;
114
117
import org .springframework .web .servlet .mvc .method .annotation .ExceptionHandlerExceptionResolver ;
115
118
import org .springframework .web .servlet .mvc .method .annotation .RequestMappingHandlerAdapter ;
116
119
import org .springframework .web .servlet .mvc .method .annotation .RequestMappingHandlerMapping ;
117
120
import org .springframework .web .servlet .resource .EncodedResourceResolver ;
121
+ import org .springframework .web .servlet .resource .ResourceHttpRequestHandler ;
118
122
import org .springframework .web .servlet .resource .ResourceResolver ;
119
123
import org .springframework .web .servlet .resource .ResourceUrlProvider ;
120
124
import org .springframework .web .servlet .resource .VersionResourceResolver ;
@@ -158,7 +162,7 @@ public class WebMvcAutoConfiguration {
158
162
*/
159
163
public static final String DEFAULT_SUFFIX = "" ;
160
164
161
- private static final String [] SERVLET_LOCATIONS = { "/" } ;
165
+ private static final String SERVLET_LOCATION = "/" ;
162
166
163
167
@ Bean
164
168
@ ConditionalOnMissingBean (HiddenHttpMethodFilter .class )
@@ -174,13 +178,6 @@ public OrderedFormContentFilter formContentFilter() {
174
178
return new OrderedFormContentFilter ();
175
179
}
176
180
177
- static String [] getResourceLocations (String [] staticLocations ) {
178
- String [] locations = new String [staticLocations .length + SERVLET_LOCATIONS .length ];
179
- System .arraycopy (staticLocations , 0 , locations , 0 , staticLocations .length );
180
- System .arraycopy (SERVLET_LOCATIONS , 0 , locations , staticLocations .length , SERVLET_LOCATIONS .length );
181
- return locations ;
182
- }
183
-
184
181
// Defined as a nested config to ensure WebMvcConfigurer is not read when not
185
182
// on the classpath
186
183
@ SuppressWarnings ("deprecation" )
@@ -191,10 +188,6 @@ static String[] getResourceLocations(String[] staticLocations) {
191
188
@ Order (0 )
192
189
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
193
190
194
- private static final Log logger = LogFactory .getLog (WebMvcConfigurer .class );
195
-
196
- private final Resources resourceProperties ;
197
-
198
191
private final WebMvcProperties mvcProperties ;
199
192
200
193
private final ListableBeanFactory beanFactory ;
@@ -207,15 +200,11 @@ public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
207
200
208
201
final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer ;
209
202
210
- public WebMvcAutoConfigurationAdapter (
211
- org .springframework .boot .autoconfigure .web .ResourceProperties resourceProperties ,
212
- WebProperties webProperties , WebMvcProperties mvcProperties , ListableBeanFactory beanFactory ,
213
- ObjectProvider <HttpMessageConverters > messageConvertersProvider ,
203
+ public WebMvcAutoConfigurationAdapter (WebProperties webProperties , WebMvcProperties mvcProperties ,
204
+ ListableBeanFactory beanFactory , ObjectProvider <HttpMessageConverters > messageConvertersProvider ,
214
205
ObjectProvider <ResourceHandlerRegistrationCustomizer > resourceHandlerRegistrationCustomizerProvider ,
215
206
ObjectProvider <DispatcherServletPath > dispatcherServletPath ,
216
207
ObjectProvider <ServletRegistrationBean <?>> servletRegistrations ) {
217
- this .resourceProperties = resourceProperties .hasBeenCustomized () ? resourceProperties
218
- : webProperties .getResources ();
219
208
this .mvcProperties = mvcProperties ;
220
209
this .beanFactory = beanFactory ;
221
210
this .messageConvertersProvider = messageConvertersProvider ;
@@ -327,39 +316,6 @@ public void addFormatters(FormatterRegistry registry) {
327
316
ApplicationConversionService .addBeans (registry , this .beanFactory );
328
317
}
329
318
330
- @ Override
331
- public void addResourceHandlers (ResourceHandlerRegistry registry ) {
332
- if (!this .resourceProperties .isAddMappings ()) {
333
- logger .debug ("Default resource handling disabled" );
334
- return ;
335
- }
336
- Duration cachePeriod = this .resourceProperties .getCache ().getPeriod ();
337
- CacheControl cacheControl = this .resourceProperties .getCache ().getCachecontrol ().toHttpCacheControl ();
338
- if (!registry .hasMappingForPattern ("/webjars/**" )) {
339
- customizeResourceHandlerRegistration (registry .addResourceHandler ("/webjars/**" )
340
- .addResourceLocations ("classpath:/META-INF/resources/webjars/" )
341
- .setCachePeriod (getSeconds (cachePeriod )).setCacheControl (cacheControl )
342
- .setUseLastModified (this .resourceProperties .getCache ().isUseLastModified ()));
343
- }
344
- String staticPathPattern = this .mvcProperties .getStaticPathPattern ();
345
- if (!registry .hasMappingForPattern (staticPathPattern )) {
346
- customizeResourceHandlerRegistration (registry .addResourceHandler (staticPathPattern )
347
- .addResourceLocations (getResourceLocations (this .resourceProperties .getStaticLocations ()))
348
- .setCachePeriod (getSeconds (cachePeriod )).setCacheControl (cacheControl )
349
- .setUseLastModified (this .resourceProperties .getCache ().isUseLastModified ()));
350
- }
351
- }
352
-
353
- private Integer getSeconds (Duration cachePeriod ) {
354
- return (cachePeriod != null ) ? (int ) cachePeriod .getSeconds () : null ;
355
- }
356
-
357
- private void customizeResourceHandlerRegistration (ResourceHandlerRegistration registration ) {
358
- if (this .resourceHandlerRegistrationCustomizer != null ) {
359
- this .resourceHandlerRegistrationCustomizer .customize (registration );
360
- }
361
- }
362
-
363
319
@ Bean
364
320
@ ConditionalOnMissingBean ({ RequestContextListener .class , RequestContextFilter .class })
365
321
@ ConditionalOnMissingFilterBean (RequestContextFilter .class )
@@ -376,6 +332,8 @@ public static RequestContextFilter requestContextFilter() {
376
332
@ EnableConfigurationProperties (WebProperties .class )
377
333
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
378
334
335
+ private static final Log logger = LogFactory .getLog (WebMvcConfigurer .class );
336
+
379
337
private final Resources resourceProperties ;
380
338
381
339
private final WebMvcProperties mvcProperties ;
@@ -386,18 +344,25 @@ public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfigurat
386
344
387
345
private final WebMvcRegistrations mvcRegistrations ;
388
346
347
+ private final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer ;
348
+
389
349
private ResourceLoader resourceLoader ;
390
350
351
+ private final Set <String > autoConfiguredResourceHandlers = new HashSet <>();
352
+
391
353
@ SuppressWarnings ("deprecation" )
392
354
public EnableWebMvcConfiguration (
393
355
org .springframework .boot .autoconfigure .web .ResourceProperties resourceProperties ,
394
356
WebMvcProperties mvcProperties , WebProperties webProperties ,
395
- ObjectProvider <WebMvcRegistrations > mvcRegistrationsProvider , ListableBeanFactory beanFactory ) {
357
+ ObjectProvider <WebMvcRegistrations > mvcRegistrationsProvider ,
358
+ ObjectProvider <ResourceHandlerRegistrationCustomizer > resourceHandlerRegistrationCustomizerProvider ,
359
+ ListableBeanFactory beanFactory ) {
396
360
this .resourceProperties = resourceProperties .hasBeenCustomized () ? resourceProperties
397
361
: webProperties .getResources ();
398
362
this .mvcProperties = mvcProperties ;
399
363
this .webProperties = webProperties ;
400
364
this .mvcRegistrations = mvcRegistrationsProvider .getIfUnique ();
365
+ this .resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider .getIfAvailable ();
401
366
this .beanFactory = beanFactory ;
402
367
}
403
368
@@ -437,6 +402,71 @@ public RequestMappingHandlerMapping requestMappingHandlerMapping(
437
402
resourceUrlProvider );
438
403
}
439
404
405
+ @ Bean
406
+ @ Override
407
+ public HandlerMapping resourceHandlerMapping (ContentNegotiationManager contentNegotiationManager ,
408
+ FormattingConversionService conversionService , ResourceUrlProvider resourceUrlProvider ) {
409
+ HandlerMapping mapping = super .resourceHandlerMapping (contentNegotiationManager , conversionService ,
410
+ resourceUrlProvider );
411
+ if (mapping instanceof SimpleUrlHandlerMapping ) {
412
+ addServletContextResourceHandlerMapping ((SimpleUrlHandlerMapping ) mapping );
413
+ }
414
+ return mapping ;
415
+ }
416
+
417
+ private void addServletContextResourceHandlerMapping (SimpleUrlHandlerMapping mapping ) {
418
+ Map <String , ?> urlMap = mapping .getUrlMap ();
419
+ String pattern = this .mvcProperties .getStaticPathPattern ();
420
+ Object handler = urlMap .get (pattern );
421
+ if (handler instanceof ResourceHttpRequestHandler
422
+ && this .autoConfiguredResourceHandlers .contains (pattern )) {
423
+ addServletContextResourceHandlerMapping ((ResourceHttpRequestHandler ) handler );
424
+ }
425
+ }
426
+
427
+ private void addServletContextResourceHandlerMapping (ResourceHttpRequestHandler handler ) {
428
+ ServletContext servletContext = getServletContext ();
429
+ if (servletContext != null ) {
430
+ List <Resource > locations = handler .getLocations ();
431
+ locations .add (new ServletContextResource (servletContext , SERVLET_LOCATION ));
432
+ }
433
+ }
434
+
435
+ @ Override
436
+ protected void addResourceHandlers (ResourceHandlerRegistry registry ) {
437
+ super .addResourceHandlers (registry );
438
+ if (!this .resourceProperties .isAddMappings ()) {
439
+ logger .debug ("Default resource handling disabled" );
440
+ return ;
441
+ }
442
+ addResourceHandler (registry , "/webjars/**" , "classpath:/META-INF/resources/webjars/" );
443
+ addResourceHandler (registry , this .mvcProperties .getStaticPathPattern (),
444
+ this .resourceProperties .getStaticLocations ());
445
+
446
+ }
447
+
448
+ private void addResourceHandler (ResourceHandlerRegistry registry , String pattern , String ... locations ) {
449
+ if (registry .hasMappingForPattern (pattern )) {
450
+ return ;
451
+ }
452
+ ResourceHandlerRegistration registration = registry .addResourceHandler (pattern );
453
+ registration .addResourceLocations (locations );
454
+ registration .setCachePeriod (getSeconds (this .resourceProperties .getCache ().getPeriod ()));
455
+ registration .setCacheControl (this .resourceProperties .getCache ().getCachecontrol ().toHttpCacheControl ());
456
+ customizeResourceHandlerRegistration (registration );
457
+ this .autoConfiguredResourceHandlers .add (pattern );
458
+ }
459
+
460
+ private Integer getSeconds (Duration cachePeriod ) {
461
+ return (cachePeriod != null ) ? (int ) cachePeriod .getSeconds () : null ;
462
+ }
463
+
464
+ private void customizeResourceHandlerRegistration (ResourceHandlerRegistration registration ) {
465
+ if (this .resourceHandlerRegistrationCustomizer != null ) {
466
+ this .resourceHandlerRegistrationCustomizer .customize (registration );
467
+ }
468
+ }
469
+
440
470
@ Bean
441
471
public WelcomePageHandlerMapping welcomePageHandlerMapping (ApplicationContext applicationContext ,
442
472
FormattingConversionService mvcConversionService , ResourceUrlProvider mvcResourceUrlProvider ) {
@@ -480,22 +510,34 @@ public FlashMapManager flashMapManager() {
480
510
return super .flashMapManager ();
481
511
}
482
512
483
- private Optional <Resource > getWelcomePage () {
484
- String [] locations = getResourceLocations (this .resourceProperties .getStaticLocations ());
485
- return Arrays .stream (locations ).map (this ::getIndexHtml ).filter (this ::isReadable ).findFirst ();
513
+ private Resource getWelcomePage () {
514
+ for (String location : this .resourceProperties .getStaticLocations ()) {
515
+ Resource indexHtml = getIndexHtml (location );
516
+ if (indexHtml != null ) {
517
+ return indexHtml ;
518
+ }
519
+ }
520
+ ServletContext servletContext = getServletContext ();
521
+ if (servletContext != null ) {
522
+ return getIndexHtml (new ServletContextResource (servletContext , SERVLET_LOCATION ));
523
+ }
524
+ return null ;
486
525
}
487
526
488
527
private Resource getIndexHtml (String location ) {
489
- return this .resourceLoader .getResource (location + "index.html" );
528
+ return getIndexHtml ( this .resourceLoader .getResource (location ) );
490
529
}
491
530
492
- private boolean isReadable (Resource resource ) {
531
+ private Resource getIndexHtml (Resource location ) {
493
532
try {
494
- return resource .exists () && (resource .getURL () != null );
533
+ Resource resource = location .createRelative ("index.html" );
534
+ if (resource .exists () && (resource .getURL () != null )) {
535
+ return resource ;
536
+ }
495
537
}
496
538
catch (Exception ex ) {
497
- return false ;
498
539
}
540
+ return null ;
499
541
}
500
542
501
543
@ Bean
0 commit comments