Skip to content

Commit de1d548

Browse files
author
Keith Donald
committed
SPR-6413 and SPR-6414 first cut: needs review, particularly compatibility note in AbstractHandlerMapping
1 parent 6c89946 commit de1d548

File tree

12 files changed

+301
-72
lines changed

12 files changed

+301
-72
lines changed

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java

+12-26
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,16 @@
1616

1717
package org.springframework.web.servlet.config;
1818

19-
import java.util.List;
20-
2119
import org.springframework.beans.factory.config.BeanDefinition;
2220
import org.springframework.beans.factory.config.RuntimeBeanReference;
2321
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
2422
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
25-
import org.springframework.beans.factory.support.ManagedList;
2623
import org.springframework.beans.factory.support.RootBeanDefinition;
2724
import org.springframework.beans.factory.xml.BeanDefinitionParser;
2825
import org.springframework.beans.factory.xml.ParserContext;
2926
import org.springframework.core.convert.ConversionService;
3027
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
3128
import org.springframework.util.ClassUtils;
32-
import org.springframework.util.xml.DomUtils;
3329
import org.springframework.validation.Validator;
3430
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
3531
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
@@ -56,44 +52,34 @@
5652
* @author Juergen Hoeller
5753
* @since 3.0
5854
*/
59-
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
55+
public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
6056

6157
private static final boolean jsr303Present = ClassUtils.isPresent(
62-
"javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
58+
"javax.validation.Validator", InterceptorsBeanDefinitionParser.class.getClassLoader());
6359

6460

6561
public BeanDefinition parse(Element element, ParserContext parserContext) {
6662
Object source = parserContext.extractSource(element);
6763

68-
RootBeanDefinition mappingDef = new RootBeanDefinition(DefaultAnnotationHandlerMapping.class);
69-
mappingDef.setSource(source);
70-
mappingDef.getPropertyValues().add("order", 0);
71-
String mappingName = parserContext.getReaderContext().registerWithGeneratedName(mappingDef);
64+
RootBeanDefinition annMappingDef = new RootBeanDefinition(DefaultAnnotationHandlerMapping.class);
65+
annMappingDef.setSource(source);
66+
annMappingDef.getPropertyValues().add("order", 0);
67+
String annMappingName = parserContext.getReaderContext().registerWithGeneratedName(annMappingDef);
7268

73-
Element interceptors = DomUtils.getChildElementByTagName(element, "interceptors");
74-
if (interceptors != null) {
75-
List<Element> beans = DomUtils.getChildElementsByTagName(interceptors, "bean");
76-
List<BeanDefinition> interceptorBeans = new ManagedList<BeanDefinition>(beans.size());
77-
for (Element bean : beans) {
78-
interceptorBeans.add(parserContext.getDelegate().parseBeanDefinitionElement(bean).getBeanDefinition());
79-
}
80-
mappingDef.getPropertyValues().add("interceptors", interceptorBeans);
81-
}
82-
8369
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
8470
bindingDef.setSource(source);
8571
bindingDef.getPropertyValues().add("conversionService", getConversionService(element, source, parserContext));
8672
bindingDef.getPropertyValues().add("validator", getValidator(element, source, parserContext));
8773

88-
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
89-
adapterDef.setSource(source);
90-
adapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
91-
String adapterName = parserContext.getReaderContext().registerWithGeneratedName(adapterDef);
74+
RootBeanDefinition annAdapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
75+
annAdapterDef.setSource(source);
76+
annAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
77+
String adapterName = parserContext.getReaderContext().registerWithGeneratedName(annAdapterDef);
9278

9379
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
9480
parserContext.pushContainingComponent(compDefinition);
95-
parserContext.registerComponent(new BeanComponentDefinition(mappingDef, mappingName));
96-
parserContext.registerComponent(new BeanComponentDefinition(adapterDef, adapterName));
81+
parserContext.registerComponent(new BeanComponentDefinition(annMappingDef, annMappingName));
82+
parserContext.registerComponent(new BeanComponentDefinition(annAdapterDef, adapterName));
9783
parserContext.popAndRegisterContainingComponent();
9884

9985
return null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2002-2009 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.servlet.config;
18+
19+
import java.util.List;
20+
21+
import org.springframework.beans.factory.config.BeanDefinition;
22+
import org.springframework.beans.factory.config.BeanDefinitionHolder;
23+
import org.springframework.beans.factory.xml.BeanDefinitionParser;
24+
import org.springframework.beans.factory.xml.ParserContext;
25+
import org.springframework.util.xml.DomUtils;
26+
import org.springframework.web.servlet.HandlerInterceptor;
27+
import org.w3c.dom.Element;
28+
29+
/**
30+
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses the {@code interceptors} element to configure
31+
* a set of global Spring MVC {@link HandlerInterceptor HandlerInterceptors}.
32+
* The set is expected to be configured by type on each registered HandlerMapping.
33+
*
34+
* @author Keith Donald
35+
* @since 3.0
36+
*/
37+
class InterceptorsBeanDefinitionParser implements BeanDefinitionParser {
38+
39+
public BeanDefinition parse(Element element, ParserContext parserContext) {
40+
List<Element> beans = DomUtils.getChildElementsByTagName(element, "bean");
41+
for (Element bean : beans) {
42+
BeanDefinitionHolder beanHolder = parserContext.getDelegate().parseBeanDefinitionElement(bean);
43+
parserContext.getDelegate().decorateBeanDefinitionIfRequired(bean, beanHolder);
44+
parserContext.getReaderContext().registerWithGeneratedName(beanHolder.getBeanDefinition());
45+
}
46+
return null;
47+
}
48+
49+
}

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/MvcNamespaceHandler.java

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public class MvcNamespaceHandler extends NamespaceHandlerSupport {
2929

3030
public void init() {
3131
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
32+
registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
33+
registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
3234
}
3335

3436
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2002-2009 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.servlet.config;
18+
19+
import java.util.Map;
20+
21+
import org.springframework.beans.factory.config.BeanDefinition;
22+
import org.springframework.beans.factory.support.ManagedMap;
23+
import org.springframework.beans.factory.support.RootBeanDefinition;
24+
import org.springframework.beans.factory.xml.BeanDefinitionParser;
25+
import org.springframework.beans.factory.xml.ParserContext;
26+
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
27+
import org.springframework.web.servlet.mvc.ParameterizableViewController;
28+
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
29+
import org.w3c.dom.Element;
30+
31+
/**
32+
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a {@code view-controller} element to register
33+
* a {@link ParameterizableViewController}. Will also register a {@link SimpleUrlHandlerMapping} for view controllers.
34+
*
35+
* @author Keith Donald
36+
* @since 3.0
37+
*/
38+
class ViewControllerBeanDefinitionParser implements BeanDefinitionParser {
39+
40+
private String handlerAdapterBeanName;
41+
42+
private String handlerMappingBeanName;
43+
44+
public BeanDefinition parse(Element element, ParserContext parserContext) {
45+
Object source = parserContext.extractSource(element);
46+
if (this.handlerAdapterBeanName == null) {
47+
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(SimpleControllerHandlerAdapter.class);
48+
handlerAdapterDef.setSource(source);
49+
this.handlerAdapterBeanName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);
50+
}
51+
RootBeanDefinition handlerMappingDef;
52+
if (this.handlerMappingBeanName == null) {
53+
handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
54+
handlerMappingDef.setSource(source);
55+
handlerMappingDef.getPropertyValues().add("order", "1");
56+
this.handlerMappingBeanName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);
57+
} else {
58+
handlerMappingDef = (RootBeanDefinition) parserContext.getReaderContext().getRegistry().getBeanDefinition(this.handlerMappingBeanName);
59+
}
60+
RootBeanDefinition viewControllerDef = new RootBeanDefinition(ParameterizableViewController.class);
61+
viewControllerDef.setSource(source);
62+
if (element.hasAttribute("view-name")) {
63+
viewControllerDef.getPropertyValues().add("viewName", element.getAttribute("view-name"));
64+
}
65+
Map<String, BeanDefinition> urlMap;
66+
if (handlerMappingDef.getPropertyValues().contains("urlMap")) {
67+
urlMap = (Map<String, BeanDefinition>) handlerMappingDef.getPropertyValues().getPropertyValue("urlMap").getValue();
68+
} else {
69+
urlMap = new ManagedMap<String, BeanDefinition>();
70+
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
71+
}
72+
urlMap.put(element.getAttribute("path"), viewControllerDef);
73+
return null;
74+
}
75+
76+
}

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java

+7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.ArrayList;
2020
import java.util.Arrays;
2121
import java.util.List;
22+
import java.util.Map;
2223

2324
import javax.servlet.http.HttpServletRequest;
2425

@@ -130,6 +131,12 @@ protected void extendInterceptors(List interceptors) {
130131
* @see #adaptInterceptor
131132
*/
132133
protected void initInterceptors() {
134+
// TODO consider impact on backwards compatibility here
135+
Map<String, HandlerInterceptor> globalInterceptors = getApplicationContext().getBeansOfType(HandlerInterceptor.class);
136+
if (globalInterceptors != null) {
137+
this.interceptors.addAll(globalInterceptors.values());
138+
}
139+
133140
if (!this.interceptors.isEmpty()) {
134141
this.adaptedInterceptors = new HandlerInterceptor[this.interceptors.size()];
135142
for (int i = 0; i < this.interceptors.size(); i++) {

org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/ParameterizableViewController.java

+3-11
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,15 @@
5656
* <td>viewName</td>
5757
* <td><i>null</i></td>
5858
* <td>the name of the view the viewResolver will use to forward to
59-
* (if this property is not set, an exception will be thrown during
60-
* initialization)</td>
59+
* (if this property is not set, a null view name will be returned
60+
* directing the caller to calculate the view name from the current request)</td>
6161
* </tr>
6262
* </table>
6363
* </p>
6464
*
6565
* @author Rod Johnson
6666
* @author Juergen Hoeller
67+
* @author Keith Donald
6768
*/
6869
public class ParameterizableViewController extends AbstractController {
6970

@@ -84,22 +85,13 @@ public String getViewName() {
8485
return this.viewName;
8586
}
8687

87-
@Override
88-
protected void initApplicationContext() {
89-
if (this.viewName == null) {
90-
throw new IllegalArgumentException("Property 'viewName' is required");
91-
}
92-
}
93-
94-
9588
/**
9689
* Return a ModelAndView object with the specified view name.
9790
* @see #getViewName()
9891
*/
9992
@Override
10093
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
10194
throws Exception {
102-
10395
return new ModelAndView(getViewName());
10496
}
10597

org.springframework.web.servlet/src/main/resources/org/springframework/web/servlet/config/spring-mvc-3.0.xsd

+42-16
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,6 @@
1616
]]></xsd:documentation>
1717
</xsd:annotation>
1818
<xsd:complexType>
19-
<xsd:sequence>
20-
<xsd:element name="interceptors" minOccurs="0">
21-
<xsd:annotation>
22-
<xsd:documentation source="java:org.springframework.web.servlet.HandlerInterceptor"><![CDATA[
23-
The ordered list of interceptors that intercept HTTP Servlet Requests mapped to Controllers.
24-
Interceptors allow a request to be pre/post processed before/after handling.
25-
Each inteceptor should be configured as an inner bean that implements either the org.springframework.web.servlet.HandlerInterceptor or org.springframework.web.context.request.WebRequestInterceptor interface.
26-
]]></xsd:documentation>
27-
</xsd:annotation>
28-
<xsd:complexType>
29-
<xsd:sequence>
30-
<xsd:element ref="beans:bean" maxOccurs="unbounded" />
31-
</xsd:sequence>
32-
</xsd:complexType>
33-
</xsd:element>
34-
</xsd:sequence>
3519
<xsd:attribute name="conversion-service" type="xsd:string">
3620
<xsd:annotation>
3721
<xsd:documentation source="java:org.springframework.core.convert.ConversionService"><![CDATA[
@@ -64,4 +48,46 @@
6448
</xsd:complexType>
6549
</xsd:element>
6650

51+
<xsd:element name="interceptors">
52+
<xsd:annotation>
53+
<xsd:documentation source="java:org.springframework.web.servlet.HandlerInterceptor"><![CDATA[
54+
The ordered set of interceptors that intercept HTTP Servlet Requests handled by Controllers.
55+
Interceptors allow a request to be pre/post processed before/after handling.
56+
Each inteceptor should be configured as an inner bean that implements either the org.springframework.web.servlet.HandlerInterceptor or org.springframework.web.context.request.WebRequestInterceptor interface.
57+
The interceptors in this set are automatically configured on each registered HandlerMapping.
58+
]]></xsd:documentation>
59+
</xsd:annotation>
60+
<xsd:complexType>
61+
<xsd:sequence>
62+
<xsd:element ref="beans:bean" maxOccurs="unbounded" />
63+
</xsd:sequence>
64+
</xsd:complexType>
65+
</xsd:element>
66+
67+
<xsd:element name="view-controller">
68+
<xsd:annotation>
69+
<xsd:documentation
70+
source="java:org.springframework.web.servlet.mvc.ParameterizableViewController"><![CDATA[
71+
Defines a simple Controller that selects a view to render the response.
72+
]]></xsd:documentation>
73+
</xsd:annotation>
74+
<xsd:complexType>
75+
<xsd:attribute name="path" type="xsd:string" use="required">
76+
<xsd:annotation>
77+
<xsd:documentation><![CDATA[
78+
The URL path the view is mapped to.
79+
]]></xsd:documentation>
80+
</xsd:annotation>
81+
</xsd:attribute>
82+
<xsd:attribute name="view-name" type="xsd:string">
83+
<xsd:annotation>
84+
<xsd:documentation><![CDATA[
85+
The name of the view to render. Optional.
86+
If not specified, the view name will be determined from the current HttpServletRequest by the DispatcherServlet's RequestToViewNameTranslator.
87+
]]></xsd:documentation>
88+
</xsd:annotation>
89+
</xsd:attribute>
90+
</xsd:complexType>
91+
</xsd:element>
92+
6793
</xsd:schema>

0 commit comments

Comments
 (0)