From 47c8d1de7bde543089792cb9517ec540993b113c Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 14 Mar 2019 18:51:23 +0100 Subject: [PATCH] Avoid calling other bean methods in web config This commit changes the main configuration classes for Spring MVC and Spring WebFlux to not call other bean methods when setting up the web infrastructure. This allows configuration classes extending `DelegatingWebFluxConfiguration` and `DelegatingWebMvcConfiguration` to opt-in the lite-mode, as introduced in gh-22461. --- .../setup/StandaloneMockMvcBuilder.java | 25 +++-- .../DelegatingWebFluxConfiguration.java | 2 +- .../config/WebFluxConfigurationSupport.java | 74 +++++++++------ .../DelegatingWebFluxConfigurationTests.java | 24 +++-- .../DelegatingWebMvcConfiguration.java | 2 +- .../WebMvcConfigurationSupport.java | 91 ++++++++++++------- .../DelegatingWebMvcConfigurationTests.java | 18 +++- ...MvcConfigurationSupportExtensionTests.java | 58 ++++++++---- 8 files changed, 193 insertions(+), 101 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilder.java index cc1204d6e794..f03f8f5527a9 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilder.java @@ -62,6 +62,7 @@ import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import org.springframework.web.servlet.resource.ResourceUrlProvider; import org.springframework.web.servlet.support.SessionFlashMapManager; import org.springframework.web.servlet.theme.FixedThemeResolver; import org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator; @@ -374,7 +375,16 @@ private void registerMvcSingletons(StubWebApplicationContext wac) { wac.addBeans(this.controllers); wac.addBeans(this.controllerAdvice); - RequestMappingHandlerMapping hm = config.getHandlerMapping(); + FormattingConversionService mvcConversionService = config.mvcConversionService(); + wac.addBean("mvcConversionService", mvcConversionService); + ResourceUrlProvider resourceUrlProvider = config.mvcResourceUrlProvider(); + wac.addBean("mvcResourceUrlProvider", resourceUrlProvider); + ContentNegotiationManager mvcContentNegotiationManager = config.mvcContentNegotiationManager(); + wac.addBean("mvcContentNegotiationManager", mvcContentNegotiationManager); + Validator mvcValidator = config.mvcValidator(); + wac.addBean("mvcValidator", mvcValidator); + + RequestMappingHandlerMapping hm = config.getHandlerMapping(mvcConversionService, resourceUrlProvider); if (sc != null) { hm.setServletContext(sc); } @@ -382,7 +392,8 @@ private void registerMvcSingletons(StubWebApplicationContext wac) { hm.afterPropertiesSet(); wac.addBean("requestMappingHandlerMapping", hm); - RequestMappingHandlerAdapter ha = config.requestMappingHandlerAdapter(); + RequestMappingHandlerAdapter ha = config.requestMappingHandlerAdapter(mvcContentNegotiationManager, + mvcConversionService, mvcValidator); if (sc != null) { ha.setServletContext(sc); } @@ -390,7 +401,7 @@ private void registerMvcSingletons(StubWebApplicationContext wac) { ha.afterPropertiesSet(); wac.addBean("requestMappingHandlerAdapter", ha); - wac.addBean("handlerExceptionResolver", config.handlerExceptionResolver()); + wac.addBean("handlerExceptionResolver", config.handlerExceptionResolver(mvcContentNegotiationManager)); wac.addBeans(initViewResolvers(wac)); wac.addBean(DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME, this.localeResolver); @@ -430,13 +441,15 @@ protected Map extendMvcSingletons(@Nullable ServletContext servl /** Using the MVC Java configuration as the starting point for the "standalone" setup. */ private class StandaloneConfiguration extends WebMvcConfigurationSupport { - public RequestMappingHandlerMapping getHandlerMapping() { + public RequestMappingHandlerMapping getHandlerMapping( + FormattingConversionService mvcConversionService, + ResourceUrlProvider mvcResourceUrlProvider) { RequestMappingHandlerMapping handlerMapping = handlerMappingFactory.get(); handlerMapping.setEmbeddedValueResolver(new StaticStringValueResolver(placeholderValues)); handlerMapping.setUseSuffixPatternMatch(useSuffixPatternMatch); handlerMapping.setUseTrailingSlashMatch(useTrailingSlashPatternMatch); handlerMapping.setOrder(0); - handlerMapping.setInterceptors(getInterceptors()); + handlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); if (removeSemicolonContent != null) { handlerMapping.setRemoveSemicolonContent(removeSemicolonContent); } @@ -506,7 +519,7 @@ protected void configureHandlerExceptionResolvers(List } for (HandlerExceptionResolver resolver : handlerExceptionResolvers) { if (resolver instanceof ApplicationContextAware) { - ApplicationContext applicationContext = getApplicationContext(); + ApplicationContext applicationContext = getApplicationContext(); if (applicationContext != null) { ((ApplicationContextAware) resolver).setApplicationContext(applicationContext); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java index 65601ad6b417..3603ee2cdc3b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java index 07e2e62f0ef4..ab99a8d5d1c5 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -92,6 +92,10 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware { @Nullable private ApplicationContext applicationContext; + @Nullable + public final ApplicationContext getApplicationContext() { + return this.applicationContext; + } @Override public void setApplicationContext(@Nullable ApplicationContext applicationContext) { @@ -103,12 +107,6 @@ public void setApplicationContext(@Nullable ApplicationContext applicationContex } } - @Nullable - public final ApplicationContext getApplicationContext() { - return this.applicationContext; - } - - @Bean public DispatcherHandler webHandler() { return new DispatcherHandler(); @@ -121,10 +119,11 @@ public WebExceptionHandler responseStatusExceptionHandler() { } @Bean - public RequestMappingHandlerMapping requestMappingHandlerMapping() { + public RequestMappingHandlerMapping requestMappingHandlerMapping( + RequestedContentTypeResolver webFluxContentTypeResolver) { RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); mapping.setOrder(0); - mapping.setContentTypeResolver(webFluxContentTypeResolver()); + mapping.setContentTypeResolver(webFluxContentTypeResolver); mapping.setCorsConfigurations(getCorsConfigurations()); PathMatchConfigurer configurer = getPathMatchConfigurer(); @@ -203,10 +202,10 @@ public void configurePathMatching(PathMatchConfigurer configurer) { } @Bean - public RouterFunctionMapping routerFunctionMapping() { + public RouterFunctionMapping routerFunctionMapping(ServerCodecConfigurer serverCodecConfigurer) { RouterFunctionMapping mapping = createRouterFunctionMapping(); mapping.setOrder(-1); // go before RequestMappingHandlerMapping - mapping.setMessageReaders(serverCodecConfigurer().getReaders()); + mapping.setMessageReaders(serverCodecConfigurer.getReaders()); mapping.setCorsConfigurations(getCorsConfigurations()); return mapping; @@ -225,13 +224,13 @@ protected RouterFunctionMapping createRouterFunctionMapping() { * {@link #addResourceHandlers}. */ @Bean - public HandlerMapping resourceHandlerMapping() { + public HandlerMapping resourceHandlerMapping(ResourceUrlProvider resourceUrlProvider) { ResourceLoader resourceLoader = this.applicationContext; if (resourceLoader == null) { resourceLoader = new DefaultResourceLoader(); } ResourceHandlerRegistry registry = new ResourceHandlerRegistry(resourceLoader); - registry.setResourceUrlProvider(resourceUrlProvider()); + registry.setResourceUrlProvider(resourceUrlProvider); addResourceHandlers(registry); AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); @@ -265,11 +264,15 @@ protected void addResourceHandlers(ResourceHandlerRegistry registry) { } @Bean - public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { + public RequestMappingHandlerAdapter requestMappingHandlerAdapter( + ReactiveAdapterRegistry webFluxAdapterRegistry, + ServerCodecConfigurer serverCodecConfigurer, + FormattingConversionService webFluxConversionService, + Validator webfluxValidator) { RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter(); - adapter.setMessageReaders(serverCodecConfigurer().getReaders()); - adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer()); - adapter.setReactiveAdapterRegistry(webFluxAdapterRegistry()); + adapter.setMessageReaders(serverCodecConfigurer.getReaders()); + adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(webFluxConversionService, webfluxValidator)); + adapter.setReactiveAdapterRegistry(webFluxAdapterRegistry); ArgumentResolverConfigurer configurer = new ArgumentResolverConfigurer(); configureArgumentResolvers(configurer); @@ -325,10 +328,12 @@ protected void configureHttpMessageCodecs(ServerCodecConfigurer configurer) { * Return the {@link ConfigurableWebBindingInitializer} to use for * initializing all {@link WebDataBinder} instances. */ - protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() { + protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer( + FormattingConversionService webFluxConversionService, + Validator webFluxValidator) { ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); - initializer.setConversionService(webFluxConversionService()); - initializer.setValidator(webFluxValidator()); + initializer.setConversionService(webFluxConversionService); + initializer.setValidator(webFluxValidator); MessageCodesResolver messageCodesResolver = getMessageCodesResolver(); if (messageCodesResolver != null) { initializer.setMessageCodesResolver(messageCodesResolver); @@ -420,33 +425,42 @@ public SimpleHandlerAdapter simpleHandlerAdapter() { } @Bean - public ResponseEntityResultHandler responseEntityResultHandler() { - return new ResponseEntityResultHandler(serverCodecConfigurer().getWriters(), - webFluxContentTypeResolver(), webFluxAdapterRegistry()); + public ResponseEntityResultHandler responseEntityResultHandler( + ReactiveAdapterRegistry webFluxAdapterRegistry, + ServerCodecConfigurer serverCodecConfigurer, + RequestedContentTypeResolver webFluxContentTypeResolver) { + return new ResponseEntityResultHandler(serverCodecConfigurer.getWriters(), + webFluxContentTypeResolver, webFluxAdapterRegistry); } @Bean - public ResponseBodyResultHandler responseBodyResultHandler() { - return new ResponseBodyResultHandler(serverCodecConfigurer().getWriters(), - webFluxContentTypeResolver(), webFluxAdapterRegistry()); + public ResponseBodyResultHandler responseBodyResultHandler( + ReactiveAdapterRegistry webFluxAdapterRegistry, + ServerCodecConfigurer serverCodecConfigurer, + RequestedContentTypeResolver webFluxContentTypeResolver) { + return new ResponseBodyResultHandler(serverCodecConfigurer.getWriters(), + webFluxContentTypeResolver, webFluxAdapterRegistry); } @Bean - public ViewResolutionResultHandler viewResolutionResultHandler() { + public ViewResolutionResultHandler viewResolutionResultHandler( + ReactiveAdapterRegistry webFluxAdapterRegistry, + RequestedContentTypeResolver webFluxContentTypeResolver) { ViewResolverRegistry registry = getViewResolverRegistry(); List resolvers = registry.getViewResolvers(); ViewResolutionResultHandler handler = new ViewResolutionResultHandler( - resolvers, webFluxContentTypeResolver(), webFluxAdapterRegistry()); + resolvers, webFluxContentTypeResolver, webFluxAdapterRegistry); handler.setDefaultViews(registry.getDefaultViews()); handler.setOrder(registry.getOrder()); return handler; } @Bean - public ServerResponseResultHandler serverResponseResultHandler() { + public ServerResponseResultHandler serverResponseResultHandler( + ServerCodecConfigurer serverCodecConfigurer) { List resolvers = getViewResolverRegistry().getViewResolvers(); ServerResponseResultHandler handler = new ServerResponseResultHandler(); - handler.setMessageWriters(serverCodecConfigurer().getWriters()); + handler.setMessageWriters(serverCodecConfigurer.getWriters()); handler.setViewResolvers(resolvers); return handler; } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java index 96098a290944..bac0c180f596 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,9 +27,12 @@ import org.mockito.MockitoAnnotations; import org.springframework.context.support.StaticApplicationContext; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.format.FormatterRegistry; +import org.springframework.format.support.FormattingConversionService; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.codec.ServerCodecConfigurer; +import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; @@ -72,7 +75,7 @@ public void setup() { @Test public void requestMappingHandlerMapping() throws Exception { delegatingConfig.setConfigurers(Collections.singletonList(webFluxConfigurer)); - delegatingConfig.requestMappingHandlerMapping(); + delegatingConfig.requestMappingHandlerMapping(delegatingConfig.webFluxContentTypeResolver()); verify(webFluxConfigurer).configureContentTypeResolver(any(RequestedContentTypeResolverBuilder.class)); verify(webFluxConfigurer).addCorsMappings(any(CorsRegistry.class)); @@ -82,9 +85,14 @@ public void requestMappingHandlerMapping() throws Exception { @Test public void requestMappingHandlerAdapter() throws Exception { delegatingConfig.setConfigurers(Collections.singletonList(webFluxConfigurer)); + ReactiveAdapterRegistry reactiveAdapterRegistry = delegatingConfig.webFluxAdapterRegistry(); + ServerCodecConfigurer serverCodecConfigurer = delegatingConfig.serverCodecConfigurer(); + FormattingConversionService formattingConversionService = delegatingConfig.webFluxConversionService(); + Validator validator = delegatingConfig.webFluxValidator(); ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) - this.delegatingConfig.requestMappingHandlerAdapter().getWebBindingInitializer(); + this.delegatingConfig.requestMappingHandlerAdapter(reactiveAdapterRegistry, serverCodecConfigurer, + formattingConversionService, validator).getWebBindingInitializer(); verify(webFluxConfigurer).configureHttpMessageCodecs(codecsConfigurer.capture()); verify(webFluxConfigurer).getValidator(); @@ -107,7 +115,7 @@ public void resourceHandlerMapping() throws Exception { return null; }).when(webFluxConfigurer).addResourceHandlers(any(ResourceHandlerRegistry.class)); - delegatingConfig.resourceHandlerMapping(); + delegatingConfig.resourceHandlerMapping(delegatingConfig.resourceUrlProvider()); verify(webFluxConfigurer).addResourceHandlers(any(ResourceHandlerRegistry.class)); verify(webFluxConfigurer).configurePathMatching(any(PathMatchConfigurer.class)); } @@ -115,7 +123,10 @@ public void resourceHandlerMapping() throws Exception { @Test public void responseBodyResultHandler() throws Exception { delegatingConfig.setConfigurers(Collections.singletonList(webFluxConfigurer)); - delegatingConfig.responseBodyResultHandler(); + delegatingConfig.responseBodyResultHandler( + delegatingConfig.webFluxAdapterRegistry(), + delegatingConfig.serverCodecConfigurer(), + delegatingConfig.webFluxContentTypeResolver()); verify(webFluxConfigurer).configureHttpMessageCodecs(codecsConfigurer.capture()); verify(webFluxConfigurer).configureContentTypeResolver(any(RequestedContentTypeResolverBuilder.class)); @@ -124,7 +135,8 @@ public void responseBodyResultHandler() throws Exception { @Test public void viewResolutionResultHandler() throws Exception { delegatingConfig.setConfigurers(Collections.singletonList(webFluxConfigurer)); - delegatingConfig.viewResolutionResultHandler(); + delegatingConfig.viewResolutionResultHandler(delegatingConfig.webFluxAdapterRegistry(), + delegatingConfig.webFluxContentTypeResolver()); verify(webFluxConfigurer).configureViewResolvers(any(ViewResolverRegistry.class)); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java index 79272309e718..bcdc65c5b374 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index 292e31cdb102..a785da4f9ca4 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -195,7 +195,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader); jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader); jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) && - ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader); + ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader); jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader); jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader); jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader); @@ -273,11 +273,14 @@ public final ServletContext getServletContext() { * requests to annotated controllers. */ @Bean - public RequestMappingHandlerMapping requestMappingHandlerMapping() { + public RequestMappingHandlerMapping requestMappingHandlerMapping( + ContentNegotiationManager mvcContentNegotiationManager, + FormattingConversionService mvcConversionService, + ResourceUrlProvider mvcResourceUrlProvider) { RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); mapping.setOrder(0); - mapping.setInterceptors(getInterceptors()); - mapping.setContentNegotiationManager(mvcContentNegotiationManager()); + mapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); + mapping.setContentNegotiationManager(mvcContentNegotiationManager); mapping.setCorsConfigurations(getCorsConfigurations()); PathMatchConfigurer configurer = getPathMatchConfigurer(); @@ -325,12 +328,14 @@ protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() { * {@link HandlerMapping} instances with. *

This method cannot be overridden; use {@link #addInterceptors} instead. */ - protected final Object[] getInterceptors() { + protected final Object[] getInterceptors( + FormattingConversionService mvcConversionService, + ResourceUrlProvider mvcResourceUrlProvider) { if (this.interceptors == null) { InterceptorRegistry registry = new InterceptorRegistry(); addInterceptors(registry); - registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService())); - registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); + registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService)); + registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider)); this.interceptors = registry.getInterceptors(); } return this.interceptors.toArray(); @@ -441,7 +446,10 @@ protected void configureContentNegotiation(ContentNegotiationConfigurer configur */ @Bean @Nullable - public HandlerMapping viewControllerHandlerMapping() { + public HandlerMapping viewControllerHandlerMapping(PathMatcher mvcPathMatcher, + UrlPathHelper mvcUrlPathHelper, + FormattingConversionService mvcConversionService, + ResourceUrlProvider mvcResourceUrlProvider) { ViewControllerRegistry registry = new ViewControllerRegistry(this.applicationContext); addViewControllers(registry); @@ -449,9 +457,9 @@ public HandlerMapping viewControllerHandlerMapping() { if (handlerMapping == null) { return null; } - handlerMapping.setPathMatcher(mvcPathMatcher()); - handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); - handlerMapping.setInterceptors(getInterceptors()); + handlerMapping.setPathMatcher(mvcPathMatcher); + handlerMapping.setUrlPathHelper(mvcUrlPathHelper); + handlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); handlerMapping.setCorsConfigurations(getCorsConfigurations()); return handlerMapping; } @@ -468,10 +476,11 @@ protected void addViewControllers(ViewControllerRegistry registry) { * paths to controller bean names. */ @Bean - public BeanNameUrlHandlerMapping beanNameHandlerMapping() { + public BeanNameUrlHandlerMapping beanNameHandlerMapping(FormattingConversionService mvcConversionService, + ResourceUrlProvider mvcResourceUrlProvider) { BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping(); mapping.setOrder(2); - mapping.setInterceptors(getInterceptors()); + mapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); mapping.setCorsConfigurations(getCorsConfigurations()); return mapping; } @@ -488,10 +497,11 @@ public BeanNameUrlHandlerMapping beanNameHandlerMapping() { * @since 5.2 */ @Bean - public RouterFunctionMapping routerFunctionMapping() { + public RouterFunctionMapping routerFunctionMapping(FormattingConversionService mvcConversionService, + ResourceUrlProvider mvcResourceUrlProvider) { RouterFunctionMapping mapping = new RouterFunctionMapping(); mapping.setOrder(3); - mapping.setInterceptors(getInterceptors()); + mapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); mapping.setCorsConfigurations(getCorsConfigurations()); mapping.setMessageConverters(getMessageConverters()); return mapping; @@ -504,21 +514,25 @@ public RouterFunctionMapping routerFunctionMapping() { */ @Bean @Nullable - public HandlerMapping resourceHandlerMapping() { + public HandlerMapping resourceHandlerMapping(UrlPathHelper mvcUrlPathHelper, + PathMatcher mvcPathMatcher, + ContentNegotiationManager mvcContentNegotiationManager, + FormattingConversionService mvcConversionService, + ResourceUrlProvider mvcResourceUrlProvider) { Assert.state(this.applicationContext != null, "No ApplicationContext set"); Assert.state(this.servletContext != null, "No ServletContext set"); ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext, - this.servletContext, mvcContentNegotiationManager(), mvcUrlPathHelper()); + this.servletContext, mvcContentNegotiationManager, mvcUrlPathHelper); addResourceHandlers(registry); AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); if (handlerMapping == null) { return null; } - handlerMapping.setPathMatcher(mvcPathMatcher()); - handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); - handlerMapping.setInterceptors(getInterceptors()); + handlerMapping.setPathMatcher(mvcPathMatcher); + handlerMapping.setUrlPathHelper(mvcUrlPathHelper); + handlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); handlerMapping.setCorsConfigurations(getCorsConfigurations()); return handlerMapping; } @@ -580,11 +594,14 @@ protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer c * */ @Bean - public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { + public RequestMappingHandlerAdapter requestMappingHandlerAdapter( + ContentNegotiationManager mvcContentNegotiationManager, + FormattingConversionService mvcConversionService, + Validator mvcValidator) { RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter(); - adapter.setContentNegotiationManager(mvcContentNegotiationManager()); + adapter.setContentNegotiationManager(mvcContentNegotiationManager); adapter.setMessageConverters(getMessageConverters()); - adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer()); + adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(mvcConversionService, mvcValidator)); adapter.setCustomArgumentResolvers(getArgumentResolvers()); adapter.setCustomReturnValueHandlers(getReturnValueHandlers()); @@ -630,10 +647,12 @@ public HandlerFunctionAdapter handlerFunctionAdapter() { * Return the {@link ConfigurableWebBindingInitializer} to use for * initializing all {@link WebDataBinder} instances. */ - protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() { + protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer( + FormattingConversionService mvcConversionService, + Validator mvcValidator) { ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); - initializer.setConversionService(mvcConversionService()); - initializer.setValidator(mvcValidator()); + initializer.setConversionService(mvcConversionService); + initializer.setValidator(mvcValidator); MessageCodesResolver messageCodesResolver = getMessageCodesResolver(); if (messageCodesResolver != null) { initializer.setMessageCodesResolver(messageCodesResolver); @@ -879,9 +898,11 @@ else if (jsonbPresent) { * @since 4.0 */ @Bean - public CompositeUriComponentsContributor mvcUriComponentsContributor() { + public CompositeUriComponentsContributor mvcUriComponentsContributor( + FormattingConversionService mvcConversionService, + RequestMappingHandlerAdapter requestMappingHandlerAdapter) { return new CompositeUriComponentsContributor( - requestMappingHandlerAdapter().getArgumentResolvers(), mvcConversionService()); + requestMappingHandlerAdapter.getArgumentResolvers(), mvcConversionService); } /** @@ -911,11 +932,12 @@ public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() { * which allows for providing a list of resolvers. */ @Bean - public HandlerExceptionResolver handlerExceptionResolver() { + public HandlerExceptionResolver handlerExceptionResolver( + ContentNegotiationManager mvcContentNegotiationManager) { List exceptionResolvers = new ArrayList<>(); configureHandlerExceptionResolvers(exceptionResolvers); if (exceptionResolvers.isEmpty()) { - addDefaultHandlerExceptionResolvers(exceptionResolvers); + addDefaultHandlerExceptionResolvers(exceptionResolvers, mvcContentNegotiationManager); } extendHandlerExceptionResolvers(exceptionResolvers); HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite(); @@ -958,9 +980,10 @@ protected void extendHandlerExceptionResolvers(List ex *

  • {@link DefaultHandlerExceptionResolver} for resolving known Spring exception types * */ - protected final void addDefaultHandlerExceptionResolvers(List exceptionResolvers) { + protected final void addDefaultHandlerExceptionResolvers(List exceptionResolvers, + ContentNegotiationManager mvcContentNegotiationManager) { ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver(); - exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager()); + exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager); exceptionHandlerResolver.setMessageConverters(getMessageConverters()); exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers()); exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers()); @@ -1003,9 +1026,9 @@ protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResol * @since 4.1 */ @Bean - public ViewResolver mvcViewResolver() { + public ViewResolver mvcViewResolver(ContentNegotiationManager mvcContentNegotiationManager) { ViewResolverRegistry registry = new ViewResolverRegistry( - mvcContentNegotiationManager(), this.applicationContext); + mvcContentNegotiationManager, this.applicationContext); configureViewResolvers(registry); if (registry.getViewResolvers().isEmpty() && this.applicationContext != null) { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java index fe97abca84b8..bb15ba6afa28 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java @@ -97,7 +97,9 @@ public void setUp() { @Test public void requestMappingHandlerAdapter() throws Exception { delegatingConfig.setConfigurers(Collections.singletonList(webMvcConfigurer)); - RequestMappingHandlerAdapter adapter = this.delegatingConfig.requestMappingHandlerAdapter(); + RequestMappingHandlerAdapter adapter = this.delegatingConfig.requestMappingHandlerAdapter( + this.delegatingConfig.mvcContentNegotiationManager(), this.delegatingConfig.mvcConversionService(), + this.delegatingConfig.mvcValidator()); ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) adapter.getWebBindingInitializer(); @@ -128,6 +130,7 @@ public void configureMessageConverters() { public void configureMessageConverters(List> converters) { converters.add(stringConverter); } + @Override public void extendMessageConverters(List> converters) { converters.add(0, customConverter); @@ -136,7 +139,9 @@ public void extendMessageConverters(List> converters) { delegatingConfig = new DelegatingWebMvcConfiguration(); delegatingConfig.setConfigurers(configurers); - RequestMappingHandlerAdapter adapter = delegatingConfig.requestMappingHandlerAdapter(); + RequestMappingHandlerAdapter adapter = delegatingConfig.requestMappingHandlerAdapter( + this.delegatingConfig.mvcContentNegotiationManager(), this.delegatingConfig.mvcConversionService(), + this.delegatingConfig.mvcValidator()); assertEquals("Only one custom converter should be registered", 2, adapter.getMessageConverters().size()); assertSame(customConverter, adapter.getMessageConverters().get(0)); assertSame(stringConverter, adapter.getMessageConverters().get(1)); @@ -165,7 +170,7 @@ public void getCustomMessageCodesResolver() { @Test public void handlerExceptionResolver() throws Exception { delegatingConfig.setConfigurers(Collections.singletonList(webMvcConfigurer)); - delegatingConfig.handlerExceptionResolver(); + delegatingConfig.handlerExceptionResolver(delegatingConfig.mvcContentNegotiationManager()); verify(webMvcConfigurer).configureMessageConverters(converters.capture()); verify(webMvcConfigurer).configureContentNegotiation(contentNegotiationConfigurer.capture()); @@ -190,7 +195,8 @@ public void configureHandlerExceptionResolvers(List ex delegatingConfig.setConfigurers(configurers); HandlerExceptionResolverComposite composite = - (HandlerExceptionResolverComposite) delegatingConfig.handlerExceptionResolver(); + (HandlerExceptionResolverComposite) delegatingConfig + .handlerExceptionResolver(delegatingConfig.mvcContentNegotiationManager()); assertEquals("Only one custom converter is expected", 1, composite.getExceptionResolvers().size()); } @@ -211,7 +217,9 @@ public void configurePathMatch(PathMatchConfigurer configurer) { }); delegatingConfig.setConfigurers(configurers); - RequestMappingHandlerMapping handlerMapping = delegatingConfig.requestMappingHandlerMapping(); + RequestMappingHandlerMapping handlerMapping = delegatingConfig.requestMappingHandlerMapping( + delegatingConfig.mvcContentNegotiationManager(), delegatingConfig.mvcConversionService(), + delegatingConfig.mvcResourceUrlProvider()); assertNotNull(handlerMapping); assertEquals("PathMatchConfigurer should configure RegisteredSuffixPatternMatch", true, handlerMapping.useRegisteredSuffixPatternMatch()); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java index 71b6617efc0b..f037784ba68c 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java @@ -130,7 +130,9 @@ public void setUp() { @Test public void handlerMappings() throws Exception { - RequestMappingHandlerMapping rmHandlerMapping = this.config.requestMappingHandlerMapping(); + RequestMappingHandlerMapping rmHandlerMapping = this.config.requestMappingHandlerMapping( + this.config.mvcContentNegotiationManager(), + this.config.mvcConversionService(), this.config.mvcResourceUrlProvider()); rmHandlerMapping.setApplicationContext(this.context); rmHandlerMapping.afterPropertiesSet(); assertEquals(TestPathHelper.class, rmHandlerMapping.getUrlPathHelper().getClass()); @@ -152,7 +154,9 @@ public void handlerMappings() throws Exception { .getKey(); assertEquals(Collections.singleton("/api/user/{id}"), info.getPatternsCondition().getPatterns()); - AbstractHandlerMapping handlerMapping = (AbstractHandlerMapping) this.config.viewControllerHandlerMapping(); + AbstractHandlerMapping handlerMapping = (AbstractHandlerMapping) this.config.viewControllerHandlerMapping( + this.config.mvcPathMatcher(), this.config.mvcUrlPathHelper(), + this.config.mvcConversionService(), this.config.mvcResourceUrlProvider()); handlerMapping.setApplicationContext(this.context); assertNotNull(handlerMapping); assertEquals(1, handlerMapping.getOrder()); @@ -168,7 +172,10 @@ public void handlerMappings() throws Exception { assertNotNull(chain); assertNotNull(chain.getHandler()); - handlerMapping = (AbstractHandlerMapping) this.config.resourceHandlerMapping(); + handlerMapping = (AbstractHandlerMapping) this.config.resourceHandlerMapping( + this.config.mvcUrlPathHelper(), this.config.mvcPathMatcher(), + this.config.mvcContentNegotiationManager(), this.config.mvcConversionService(), + this.config.mvcResourceUrlProvider()); handlerMapping.setApplicationContext(this.context); assertNotNull(handlerMapping); assertEquals(Integer.MAX_VALUE - 1, handlerMapping.getOrder()); @@ -195,7 +202,9 @@ public void handlerMappings() throws Exception { @SuppressWarnings("unchecked") @Test public void requestMappingHandlerAdapter() throws Exception { - RequestMappingHandlerAdapter adapter = this.config.requestMappingHandlerAdapter(); + RequestMappingHandlerAdapter adapter = this.config.requestMappingHandlerAdapter( + this.config.mvcContentNegotiationManager(), this.config.mvcConversionService(), + this.config.mvcValidator()); // ConversionService String actual = this.config.mvcConversionService().convert(new TestBean(), String.class); @@ -215,11 +224,11 @@ public void requestMappingHandlerAdapter() throws Exception { // Custom argument resolvers and return value handlers List argResolvers = - (List) fieldAccessor.getPropertyValue("customArgumentResolvers"); + (List) fieldAccessor.getPropertyValue("customArgumentResolvers"); assertEquals(1, argResolvers.size()); List handlers = - (List) fieldAccessor.getPropertyValue("customReturnValueHandlers"); + (List) fieldAccessor.getPropertyValue("customReturnValueHandlers"); assertEquals(1, handlers.size()); // Async support options @@ -239,7 +248,9 @@ public void requestMappingHandlerAdapter() throws Exception { @Test public void webBindingInitializer() throws Exception { - RequestMappingHandlerAdapter adapter = this.config.requestMappingHandlerAdapter(); + RequestMappingHandlerAdapter adapter = this.config.requestMappingHandlerAdapter( + this.config.mvcContentNegotiationManager(), this.config.mvcConversionService(), + this.config.mvcValidator()); ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) adapter.getWebBindingInitializer(); @@ -259,7 +270,9 @@ public void contentNegotiation() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo.json"); NativeWebRequest webRequest = new ServletWebRequest(request); - RequestMappingHandlerMapping mapping = this.config.requestMappingHandlerMapping(); + RequestMappingHandlerMapping mapping = this.config.requestMappingHandlerMapping( + this.config.mvcContentNegotiationManager(), this.config.mvcConversionService(), + this.config.mvcResourceUrlProvider()); ContentNegotiationManager manager = mapping.getContentNegotiationManager(); assertEquals(Collections.singletonList(APPLICATION_JSON), manager.resolveMediaTypes(webRequest)); @@ -278,7 +291,10 @@ public void contentNegotiation() throws Exception { assertEquals(Collections.singletonList(APPLICATION_JSON), manager.resolveMediaTypes(webRequest)); request.setRequestURI("/resources/foo.gif"); - SimpleUrlHandlerMapping handlerMapping = (SimpleUrlHandlerMapping) this.config.resourceHandlerMapping(); + SimpleUrlHandlerMapping handlerMapping = (SimpleUrlHandlerMapping) this.config.resourceHandlerMapping( + this.config.mvcUrlPathHelper(), this.config.mvcPathMatcher(), + this.config.mvcContentNegotiationManager(), this.config.mvcConversionService(), + this.config.mvcResourceUrlProvider()); handlerMapping.setApplicationContext(this.context); HandlerExecutionChain chain = handlerMapping.getHandler(request); assertNotNull(chain); @@ -290,7 +306,7 @@ public void contentNegotiation() throws Exception { @Test public void exceptionResolvers() throws Exception { List resolvers = ((HandlerExceptionResolverComposite) - this.config.handlerExceptionResolver()).getExceptionResolvers(); + this.config.handlerExceptionResolver(null)).getExceptionResolvers(); assertEquals(2, resolvers.size()); assertEquals(ResponseStatusExceptionResolver.class, resolvers.get(0).getClass()); @@ -300,7 +316,8 @@ public void exceptionResolvers() throws Exception { @SuppressWarnings("unchecked") @Test public void viewResolvers() throws Exception { - ViewResolverComposite viewResolver = (ViewResolverComposite) this.config.mvcViewResolver(); + ViewResolverComposite viewResolver = (ViewResolverComposite) this.config.mvcViewResolver( + this.config.mvcContentNegotiationManager()); assertEquals(Ordered.HIGHEST_PRECEDENCE, viewResolver.getOrder()); List viewResolvers = viewResolver.getViewResolvers(); @@ -310,12 +327,12 @@ public void viewResolvers() throws Exception { assertFalse((Boolean) accessor.getPropertyValue("useNotAcceptableStatusCode")); assertNotNull(accessor.getPropertyValue("contentNegotiationManager")); - List defaultViews = (List)accessor.getPropertyValue("defaultViews"); + List defaultViews = (List) accessor.getPropertyValue("defaultViews"); assertNotNull(defaultViews); assertEquals(1, defaultViews.size()); assertEquals(MappingJackson2JsonView.class, defaultViews.get(0).getClass()); - viewResolvers = (List)accessor.getPropertyValue("viewResolvers"); + viewResolvers = (List) accessor.getPropertyValue("viewResolvers"); assertNotNull(viewResolvers); assertEquals(1, viewResolvers.size()); assertEquals(InternalResourceViewResolver.class, viewResolvers.get(0).getClass()); @@ -376,6 +393,7 @@ public Validator getValidator() { public void validate(@Nullable Object target, Errors errors) { errors.reject("invalid"); } + @Override public boolean supports(Class clazz) { return true; @@ -391,8 +409,10 @@ public void configureContentNegotiation(ContentNegotiationConfigurer configurer) @Override public void configureAsyncSupport(AsyncSupportConfigurer configurer) { configurer.setDefaultTimeout(2500).setTaskExecutor(new ConcurrentTaskExecutor()) - .registerCallableInterceptors(new CallableProcessingInterceptor() { }) - .registerDeferredResultInterceptors(new DeferredResultProcessingInterceptor() {}); + .registerCallableInterceptors(new CallableProcessingInterceptor() { + }) + .registerDeferredResultInterceptors(new DeferredResultProcessingInterceptor() { + }); } @Override @@ -433,7 +453,7 @@ public MessageCodesResolver getMessageCodesResolver() { return new DefaultMessageCodesResolver() { @Override public String[] resolveMessageCodes(String errorCode, String objectName) { - return new String[] { "custom." + errorCode }; + return new String[] {"custom." + errorCode}; } }; } @@ -468,9 +488,11 @@ public void addCorsMappings(CorsRegistry registry) { } - private class TestPathHelper extends UrlPathHelper {} + private class TestPathHelper extends UrlPathHelper { + } - private class TestPathMatcher extends AntPathMatcher {} + private class TestPathMatcher extends AntPathMatcher { + } @RestController