Skip to content

Commit cf5db83

Browse files
committed
Replace MvcUrls with MvcUriComponentsBuilder
Issue: SPR-8826
1 parent e7f89f8 commit cf5db83

File tree

13 files changed

+723
-820
lines changed

13 files changed

+723
-820
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.method.support;
18+
19+
import org.springframework.core.MethodParameter;
20+
import org.springframework.core.convert.ConversionService;
21+
import org.springframework.format.support.DefaultFormattingConversionService;
22+
import org.springframework.util.Assert;
23+
import org.springframework.web.util.UriComponentsBuilder;
24+
25+
import java.util.ArrayList;
26+
import java.util.Collection;
27+
import java.util.List;
28+
import java.util.Map;
29+
30+
/**
31+
* A {@link UriComponentsContributor} containing a list of other contributors
32+
* to delegate and also encapsulating a specific {@link ConversionService} to
33+
* use for formatting method argument values to Strings.
34+
*
35+
* @author Rossen Stoyanchev
36+
* @since 4.0
37+
*/
38+
public class CompositeUriComponentsContributor implements UriComponentsContributor {
39+
40+
private final List<UriComponentsContributor> contributors = new ArrayList<UriComponentsContributor>();
41+
42+
private final ConversionService conversionService;
43+
44+
45+
/**
46+
* Create an instance from a collection of {@link UriComponentsContributor}s or
47+
* {@link HandlerMethodArgumentResolver}s. Since both of these tend to be implemented
48+
* by the same class, the most convenient option is to obtain the configured
49+
* {@code HandlerMethodArgumentResolvers} in {@code RequestMappingHandlerAdapter} and
50+
* provide that to this constructor.
51+
*
52+
* @param contributors a collection of {@link UriComponentsContributor}
53+
* or {@link HandlerMethodArgumentResolver}s.
54+
*/
55+
public CompositeUriComponentsContributor(Collection<?> contributors) {
56+
this(contributors, null);
57+
}
58+
59+
/**
60+
* Create an instance from a collection of {@link UriComponentsContributor}s or
61+
* {@link HandlerMethodArgumentResolver}s. Since both of these tend to be implemented
62+
* by the same class, the most convenient option is to obtain the configured
63+
* {@code HandlerMethodArgumentResolvers} in the {@code RequestMappingHandlerAdapter}
64+
* and provide that to this constructor.
65+
* <p>
66+
* If the {@link ConversionService} argument is {@code null},
67+
* {@link org.springframework.format.support.DefaultFormattingConversionService}
68+
* will be used by default.
69+
*
70+
* @param contributors a collection of {@link UriComponentsContributor}
71+
* or {@link HandlerMethodArgumentResolver}s.
72+
* @param conversionService a ConversionService to use when method argument values
73+
* need to be formatted as Strings before being added to the URI
74+
*/
75+
public CompositeUriComponentsContributor(Collection<?> contributors, ConversionService conversionService) {
76+
77+
Assert.notNull(contributors, "'uriComponentsContributors' must not be null");
78+
79+
for (Object contributor : contributors) {
80+
if (contributor instanceof UriComponentsContributor) {
81+
this.contributors.add((UriComponentsContributor) contributor);
82+
}
83+
}
84+
85+
this.conversionService = (conversionService != null) ?
86+
conversionService : new DefaultFormattingConversionService();
87+
}
88+
89+
90+
public boolean hasContributors() {
91+
return this.contributors.isEmpty();
92+
}
93+
94+
@Override
95+
public boolean supportsParameter(MethodParameter parameter) {
96+
for (UriComponentsContributor c : this.contributors) {
97+
if (c.supportsParameter(parameter)) {
98+
return true;
99+
}
100+
}
101+
return false;
102+
}
103+
104+
@Override
105+
public void contributeMethodArgument(MethodParameter parameter, Object value,
106+
UriComponentsBuilder builder, Map<String, Object> uriVariables, ConversionService conversionService) {
107+
108+
for (UriComponentsContributor c : this.contributors) {
109+
if (c.supportsParameter(parameter)) {
110+
c.contributeMethodArgument(parameter, value, builder, uriVariables, conversionService);
111+
break;
112+
}
113+
}
114+
}
115+
116+
/**
117+
* An overloaded method that uses the ConversionService created at construction.
118+
*/
119+
public void contributeMethodArgument(MethodParameter parameter, Object value, UriComponentsBuilder builder,
120+
Map<String, Object> uriVariables) {
121+
122+
this.contributeMethodArgument(parameter, value, builder, uriVariables, this.conversionService);
123+
}
124+
125+
}

spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,17 @@ public UriComponentsBuilder queryParam(String name, Object... values) {
555555
return this;
556556
}
557557

558+
/**
559+
* Adds the given query parameters.
560+
* @param params the params
561+
* @return this UriComponentsBuilder
562+
*/
563+
public UriComponentsBuilder queryParams(MultiValueMap<String, String> params) {
564+
Assert.notNull(params, "'params' must not be null");
565+
this.queryParams.putAll(params);
566+
return this;
567+
}
568+
558569
/**
559570
* Sets the query parameter values overriding all existing query values for
560571
* the same parameter. If no values are given, the query parameter is

spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.List;
2020
import java.util.Properties;
2121

22+
import org.springframework.beans.factory.FactoryBean;
23+
import org.springframework.beans.factory.InitializingBean;
2224
import org.springframework.beans.factory.config.BeanDefinition;
2325
import org.springframework.beans.factory.config.BeanDefinitionHolder;
2426
import org.springframework.beans.factory.config.RuntimeBeanReference;
@@ -28,6 +30,7 @@
2830
import org.springframework.beans.factory.support.RootBeanDefinition;
2931
import org.springframework.beans.factory.xml.BeanDefinitionParser;
3032
import org.springframework.beans.factory.xml.ParserContext;
33+
import org.springframework.core.convert.ConversionService;
3134
import org.springframework.format.support.DefaultFormattingConversionService;
3235
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
3336
import org.springframework.http.MediaType;
@@ -52,6 +55,7 @@
5255
import org.springframework.web.bind.annotation.ResponseStatus;
5356
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
5457
import org.springframework.web.bind.support.WebArgumentResolver;
58+
import org.springframework.web.method.support.CompositeUriComponentsContributor;
5559
import org.springframework.web.servlet.HandlerAdapter;
5660
import org.springframework.web.servlet.HandlerExceptionResolver;
5761
import org.springframework.web.servlet.HandlerMapping;
@@ -67,6 +71,7 @@
6771
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
6872
import org.springframework.web.servlet.mvc.method.annotation.ServletWebArgumentResolverAdapter;
6973
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
74+
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
7075
import org.w3c.dom.Element;
7176

7277
/**
@@ -207,12 +212,12 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
207212
handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
208213
String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);
209214

210-
String mvcUrlsName = "mvcUrls";
211-
RootBeanDefinition mvcUrlsDef = new RootBeanDefinition(DefaultMvcUrlsFactoryBean.class);
212-
mvcUrlsDef.setSource(source);
213-
mvcUrlsDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
214-
mvcUrlsDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
215-
parserContext.getReaderContext().getRegistry().registerBeanDefinition(mvcUrlsName, mvcUrlsDef);
215+
String uriCompContribName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
216+
RootBeanDefinition uriCompContribDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
217+
uriCompContribDef.setSource(source);
218+
uriCompContribDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
219+
uriCompContribDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
220+
parserContext.getReaderContext().getRegistry().registerBeanDefinition(uriCompContribName, uriCompContribDef);
216221

217222
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
218223
csInterceptorDef.setSource(source);
@@ -249,7 +254,7 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
249254

250255
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, methodMappingName));
251256
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, handlerAdapterName));
252-
parserContext.registerComponent(new BeanComponentDefinition(mvcUrlsDef, mvcUrlsName));
257+
parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
253258
parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
254259
parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
255260
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
@@ -484,4 +489,50 @@ private ManagedList<BeanDefinitionHolder> wrapWebArgumentResolverBeanDefs(List<B
484489
return result;
485490
}
486491

492+
493+
/**
494+
* A FactoryBean for a CompositeUriComponentsContributor that obtains the
495+
* HandlerMethodArgumentResolver's configured in RequestMappingHandlerAdapter
496+
* after it is fully initialized.
497+
*/
498+
private static class CompositeUriComponentsContributorFactoryBean
499+
implements InitializingBean, FactoryBean<CompositeUriComponentsContributor> {
500+
501+
private RequestMappingHandlerAdapter handlerAdapter;
502+
503+
private ConversionService conversionService;
504+
505+
private CompositeUriComponentsContributor uriComponentsContributor;
506+
507+
508+
public void setHandlerAdapter(RequestMappingHandlerAdapter handlerAdapter) {
509+
this.handlerAdapter = handlerAdapter;
510+
}
511+
512+
public void setConversionService(ConversionService conversionService) {
513+
this.conversionService = conversionService;
514+
}
515+
516+
@Override
517+
public void afterPropertiesSet() throws Exception {
518+
this.uriComponentsContributor = new CompositeUriComponentsContributor(
519+
this.handlerAdapter.getArgumentResolvers(), this.conversionService);
520+
}
521+
522+
@Override
523+
public CompositeUriComponentsContributor getObject() throws Exception {
524+
return this.uriComponentsContributor;
525+
}
526+
527+
@Override
528+
public Class<?> getObjectType() {
529+
return CompositeUriComponentsContributor.class;
530+
}
531+
532+
@Override
533+
public boolean isSingleton() {
534+
return true;
535+
}
536+
}
537+
487538
}

spring-webmvc/src/main/java/org/springframework/web/servlet/config/DefaultMvcUrlsFactoryBean.java

Lines changed: 0 additions & 89 deletions
This file was deleted.

spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import org.springframework.web.bind.annotation.ResponseStatus;
6262
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
6363
import org.springframework.web.context.ServletContextAware;
64+
import org.springframework.web.method.support.CompositeUriComponentsContributor;
6465
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
6566
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
6667
import org.springframework.web.servlet.HandlerAdapter;
@@ -78,8 +79,6 @@
7879
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
7980
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
8081
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
81-
import org.springframework.web.servlet.mvc.support.DefaultMvcUrls;
82-
import org.springframework.web.servlet.mvc.support.MvcUrls;
8382

8483
/**
8584
* This is the main class providing the configuration behind the MVC Java config.
@@ -566,12 +565,13 @@ public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
566565
}
567566

568567
/**
569-
* Return an instance of {@link MvcUrls} for injection into controllers to create
570-
* URLs by referencing controllers and controller methods.
568+
* Return an instance of {@link CompositeUriComponentsContributor} for use with
569+
* {@link org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder}.
571570
*/
572571
@Bean
573-
public MvcUrls mvcUrls() {
574-
return new DefaultMvcUrls(requestMappingHandlerAdapter().getArgumentResolvers(), mvcConversionService());
572+
public CompositeUriComponentsContributor mvcUriComponentsContributor() {
573+
return new CompositeUriComponentsContributor(
574+
requestMappingHandlerAdapter().getArgumentResolvers(), mvcConversionService());
575575
}
576576

577577
/**

0 commit comments

Comments
 (0)