Commit ef7a8275 authored by Scott Frederick's avatar Scott Frederick

Allow overriding ThemeResolver and FlashMapManager beans

Previously, an error would occur when a user contributed a
`ThemeResolver` or `FlashMapManager` bean because those beans would
would not properly override the default beans provided by Spring
Framework. This commit adds conditional auto-configuration of these
bean types, preferring user-provided beans and falling back to
Framework-provided defaults.

Fixes gh-24207
parent cca8a386
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -92,8 +92,10 @@ import org.springframework.web.filter.FormContentFilter; ...@@ -92,8 +92,10 @@ import org.springframework.web.filter.FormContentFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter; import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.filter.RequestContextFilter; import org.springframework.web.filter.RequestContextFilter;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ThemeResolver;
import org.springframework.web.servlet.View; import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer; import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
...@@ -134,6 +136,7 @@ import org.springframework.web.util.pattern.PathPatternParser; ...@@ -134,6 +136,7 @@ import org.springframework.web.util.pattern.PathPatternParser;
* @author Kristine Jetzke * @author Kristine Jetzke
* @author Bruce Brouwer * @author Bruce Brouwer
* @author Artsiom Yudovin * @author Artsiom Yudovin
* @author Scott Frederick
* @since 2.0.0 * @since 2.0.0
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
...@@ -463,6 +466,20 @@ public class WebMvcAutoConfiguration { ...@@ -463,6 +466,20 @@ public class WebMvcAutoConfiguration {
return localeResolver; return localeResolver;
} }
@Override
@Bean
@ConditionalOnMissingBean(name = DispatcherServlet.THEME_RESOLVER_BEAN_NAME)
public ThemeResolver themeResolver() {
return super.themeResolver();
}
@Override
@Bean
@ConditionalOnMissingBean(name = DispatcherServlet.FLASH_MAP_MANAGER_BEAN_NAME)
public FlashMapManager flashMapManager() {
return super.flashMapManager();
}
private Optional<Resource> getWelcomePage() { private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations()); String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst(); return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
......
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -87,10 +87,13 @@ import org.springframework.web.filter.FormContentFilter; ...@@ -87,10 +87,13 @@ import org.springframework.web.filter.FormContentFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter; import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.filter.RequestContextFilter; import org.springframework.web.filter.RequestContextFilter;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ThemeResolver;
import org.springframework.web.servlet.View; import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer; import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
...@@ -119,6 +122,9 @@ import org.springframework.web.servlet.resource.ResourceResolver; ...@@ -119,6 +122,9 @@ import org.springframework.web.servlet.resource.ResourceResolver;
import org.springframework.web.servlet.resource.ResourceTransformer; import org.springframework.web.servlet.resource.ResourceTransformer;
import org.springframework.web.servlet.resource.VersionResourceResolver; import org.springframework.web.servlet.resource.VersionResourceResolver;
import org.springframework.web.servlet.resource.VersionStrategy; import org.springframework.web.servlet.resource.VersionStrategy;
import org.springframework.web.servlet.support.AbstractFlashMapManager;
import org.springframework.web.servlet.support.SessionFlashMapManager;
import org.springframework.web.servlet.theme.FixedThemeResolver;
import org.springframework.web.servlet.view.AbstractView; import org.springframework.web.servlet.view.AbstractView;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver; import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.UrlPathHelper;
...@@ -137,6 +143,7 @@ import static org.mockito.Mockito.mock; ...@@ -137,6 +143,7 @@ import static org.mockito.Mockito.mock;
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Kristine Jetzke * @author Kristine Jetzke
* @author Artsiom Yudovin * @author Artsiom Yudovin
* @author Scott Frederick
*/ */
class WebMvcAutoConfigurationTests { class WebMvcAutoConfigurationTests {
...@@ -362,6 +369,42 @@ class WebMvcAutoConfigurationTests { ...@@ -362,6 +369,42 @@ class WebMvcAutoConfigurationTests {
}); });
} }
@Test
void customThemeResolverWithMatchingNameReplacesDefaultThemeResolver() {
this.contextRunner.withBean("themeResolver", CustomThemeResolver.class, CustomThemeResolver::new)
.run((context) -> {
assertThat(context).hasSingleBean(ThemeResolver.class);
assertThat(context.getBean("themeResolver")).isInstanceOf(CustomThemeResolver.class);
});
}
@Test
void customThemeResolverWithDifferentNameDoesNotReplaceDefaultThemeResolver() {
this.contextRunner.withBean("customThemeResolver", CustomThemeResolver.class, CustomThemeResolver::new)
.run((context) -> {
assertThat(context.getBean("customThemeResolver")).isInstanceOf(CustomThemeResolver.class);
assertThat(context.getBean("themeResolver")).isInstanceOf(FixedThemeResolver.class);
});
}
@Test
void customFlashMapManagerWithMatchingNameReplacesDefaultFlashMapManager() {
this.contextRunner.withBean("flashMapManager", CustomFlashMapManager.class, CustomFlashMapManager::new)
.run((context) -> {
assertThat(context).hasSingleBean(FlashMapManager.class);
assertThat(context.getBean("flashMapManager")).isInstanceOf(CustomFlashMapManager.class);
});
}
@Test
void customFlashMapManagerWithDifferentNameDoesNotReplaceDefaultFlashMapManager() {
this.contextRunner.withBean("customFlashMapManager", CustomFlashMapManager.class, CustomFlashMapManager::new)
.run((context) -> {
assertThat(context.getBean("customFlashMapManager")).isInstanceOf(CustomFlashMapManager.class);
assertThat(context.getBean("flashMapManager")).isInstanceOf(SessionFlashMapManager.class);
});
}
@Test @Test
void defaultDateFormat() { void defaultDateFormat() {
this.contextRunner.run((context) -> { this.contextRunner.run((context) -> {
...@@ -1407,4 +1450,33 @@ class WebMvcAutoConfigurationTests { ...@@ -1407,4 +1450,33 @@ class WebMvcAutoConfigurationTests {
} }
static class CustomThemeResolver implements ThemeResolver {
@Override
public String resolveThemeName(HttpServletRequest request) {
return "custom";
}
@Override
public void setThemeName(HttpServletRequest request, HttpServletResponse response, String themeName) {
}
}
static class CustomFlashMapManager extends AbstractFlashMapManager {
@Override
protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {
return null;
}
@Override
protected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request,
HttpServletResponse response) {
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment