Skip to content

Commit 9779ca1

Browse files
committed
SPR-7976 Add MvcInterceptors features.
1 parent 5a5fff5 commit 9779ca1

File tree

4 files changed

+371
-32
lines changed

4 files changed

+371
-32
lines changed

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

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,59 +18,54 @@
1818

1919
import java.util.List;
2020

21-
import org.springframework.beans.factory.config.BeanDefinition;
2221
import org.springframework.beans.factory.config.BeanDefinitionHolder;
23-
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
24-
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
25-
import org.springframework.beans.factory.support.RootBeanDefinition;
26-
import org.springframework.beans.factory.xml.BeanDefinitionParser;
2722
import org.springframework.beans.factory.xml.ParserContext;
23+
import org.springframework.context.config.AbstractSpecificationBeanDefinitionParser;
24+
import org.springframework.context.config.FeatureSpecification;
2825
import org.springframework.util.xml.DomUtils;
2926
import org.springframework.web.servlet.handler.MappedInterceptor;
3027
import org.w3c.dom.Element;
3128

3229
/**
33-
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a {@code interceptors} element to register
34-
* a set of {@link MappedInterceptor} definitions.
30+
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses
31+
* a {@code interceptors} element to register set of {@link MappedInterceptor}
32+
* definitions.
3533
*
3634
* @author Keith Donald
35+
* @author Rossen Stoyanchev
36+
*
3737
* @since 3.0
3838
*/
39-
class InterceptorsBeanDefinitionParser implements BeanDefinitionParser {
39+
class InterceptorsBeanDefinitionParser extends AbstractSpecificationBeanDefinitionParser {
40+
41+
/**
42+
* Parses the {@code <mvc:interceptors/>} tag.
43+
*/
44+
public FeatureSpecification doParse(Element element, ParserContext parserContext) {
45+
MvcInterceptors mvcInterceptors = new MvcInterceptors();
4046

41-
public BeanDefinition parse(Element element, ParserContext parserContext) {
42-
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
43-
parserContext.pushContainingComponent(compDefinition);
44-
4547
List<Element> interceptors = DomUtils.getChildElementsByTagName(element, new String[] { "bean", "interceptor" });
4648
for (Element interceptor : interceptors) {
47-
RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
48-
mappedInterceptorDef.setSource(parserContext.extractSource(interceptor));
49-
mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
50-
String[] pathPatterns;
51-
BeanDefinitionHolder interceptorDef;
5249
if ("interceptor".equals(interceptor.getLocalName())) {
5350
List<Element> paths = DomUtils.getChildElementsByTagName(interceptor, "mapping");
54-
pathPatterns = new String[paths.size()];
51+
String[] pathPatterns = new String[paths.size()];
5552
for (int i = 0; i < paths.size(); i++) {
5653
pathPatterns[i] = paths.get(i).getAttribute("path");
5754
}
58-
Element interceptorBean = DomUtils.getChildElementByTagName(interceptor, "bean");
59-
interceptorDef = parserContext.getDelegate().parseBeanDefinitionElement(interceptorBean);
60-
interceptorDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(interceptorBean, interceptorDef);
55+
Element beanElement = DomUtils.getChildElementByTagName(interceptor, "bean");
56+
mvcInterceptors.interceptor(pathPatterns, parseBeanElement(parserContext, beanElement));
6157
} else {
62-
pathPatterns = null;
63-
interceptorDef = parserContext.getDelegate().parseBeanDefinitionElement(interceptor);
64-
interceptorDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(interceptor, interceptorDef);
58+
mvcInterceptors.interceptor(null, parseBeanElement(parserContext, interceptor));
6559
}
66-
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, pathPatterns);
67-
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, interceptorDef);
68-
String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedInterceptorDef);
69-
parserContext.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));
7060
}
71-
72-
parserContext.popAndRegisterContainingComponent();
73-
return null;
61+
62+
return mvcInterceptors;
7463
}
75-
64+
65+
private BeanDefinitionHolder parseBeanElement(ParserContext parserContext, Element interceptor) {
66+
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(interceptor);
67+
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(interceptor, beanDef);
68+
return beanDef;
69+
}
70+
7671
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright 2002-2011 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+
package org.springframework.web.servlet.config;
17+
18+
import java.util.Collections;
19+
import java.util.LinkedHashMap;
20+
import java.util.Map;
21+
22+
import org.springframework.beans.factory.config.BeanDefinitionHolder;
23+
import org.springframework.beans.factory.parsing.ProblemCollector;
24+
import org.springframework.context.config.AbstractFeatureSpecification;
25+
import org.springframework.context.config.FeatureSpecificationExecutor;
26+
import org.springframework.util.StringUtils;
27+
import org.springframework.web.context.request.WebRequestInterceptor;
28+
import org.springframework.web.servlet.HandlerInterceptor;
29+
import org.springframework.web.servlet.handler.MappedInterceptor;
30+
31+
/**
32+
* Specifies the Spring MVC "interceptors" container feature. The feature
33+
* registers one or more {@link MappedInterceptor} bean definitions. A
34+
* MappedInterceptor encapsulates an interceptor and one or more (optional)
35+
* path patterns to which the interceptor is mapped. The interceptor can be
36+
* of type {@link HandlerInterceptor} or {@link WebRequestInterceptor}.
37+
* An interceptor can also be provided without path patterns in which case
38+
* it applies globally to all handler invocations.
39+
*
40+
* @author Rossen Stoyanchev
41+
* @since 3.1
42+
*/
43+
public class MvcInterceptors extends AbstractFeatureSpecification {
44+
45+
private static final Class<? extends FeatureSpecificationExecutor> EXECUTOR_TYPE = MvcInterceptorsExecutor.class;
46+
47+
private Map<Object, String[]> interceptorMappings = new LinkedHashMap<Object, String[]>();
48+
49+
/**
50+
* Creates an MvcInterceptors instance.
51+
*/
52+
public MvcInterceptors() {
53+
super(EXECUTOR_TYPE);
54+
}
55+
56+
/**
57+
* Add one or more {@link HandlerInterceptor HandlerInterceptors} that should
58+
* intercept all handler invocations.
59+
*
60+
* @param interceptors one or more interceptors
61+
*/
62+
public MvcInterceptors globalInterceptors(HandlerInterceptor... interceptors) {
63+
addInterceptorMappings(null, interceptors);
64+
return this;
65+
}
66+
67+
/**
68+
* Add one or more {@link WebRequestInterceptor WebRequestInterceptors} that should
69+
* intercept all handler invocations.
70+
*
71+
* @param interceptors one or more interceptors
72+
*/
73+
public MvcInterceptors globalInterceptors(WebRequestInterceptor... interceptors) {
74+
addInterceptorMappings(null, interceptors);
75+
return this;
76+
}
77+
78+
/**
79+
* Add one or more interceptors by bean name that should intercept all handler
80+
* invocations.
81+
*
82+
* @param interceptors interceptor bean names
83+
*/
84+
public MvcInterceptors globalInterceptors(String... interceptors) {
85+
addInterceptorMappings(null, interceptors);
86+
return this;
87+
}
88+
89+
/**
90+
* Add one or more {@link HandlerInterceptor HandlerInterceptors} and map
91+
* them to the specified path patterns.
92+
*
93+
* @param pathPatterns the pathPatterns to map the interceptor to
94+
* @param interceptors the interceptors
95+
*/
96+
public MvcInterceptors mappedInterceptors(String[] pathPatterns, HandlerInterceptor... interceptors) {
97+
addInterceptorMappings(pathPatterns, interceptors);
98+
return this;
99+
}
100+
101+
/**
102+
* Add one or more {@link WebRequestInterceptor WebRequestInterceptors} and
103+
* map them to the specified path patterns.
104+
*
105+
* @param pathPatterns the pathPatterns to map the interceptor to
106+
* @param interceptors the interceptors
107+
*/
108+
public MvcInterceptors mappedInterceptors(String[] pathPatterns, WebRequestInterceptor... interceptors) {
109+
addInterceptorMappings(pathPatterns, interceptors);
110+
return this;
111+
}
112+
113+
/**
114+
* Add one or more interceptors by bean name and map them to the specified
115+
* path patterns.
116+
*
117+
* @param pathPatterns the pathPatterns to map to
118+
* @param interceptors the interceptors
119+
*/
120+
public MvcInterceptors mappedInterceptors(String[] pathPatterns, String... interceptors) {
121+
addInterceptorMappings(pathPatterns, interceptors);
122+
return this;
123+
}
124+
125+
void interceptor(String[] pathPatterns, BeanDefinitionHolder interceptor) {
126+
addInterceptorMappings(pathPatterns, new Object[] { interceptor });
127+
}
128+
129+
Map<Object, String[]> interceptorMappings() {
130+
return Collections.unmodifiableMap(interceptorMappings);
131+
}
132+
133+
private void addInterceptorMappings(String[] pathPatterns, Object[] interceptors) {
134+
for (Object interceptor : interceptors) {
135+
interceptorMappings.put(interceptor, pathPatterns);
136+
}
137+
}
138+
139+
@Override
140+
protected void doValidate(ProblemCollector problems) {
141+
if (interceptorMappings.size() == 0) {
142+
problems.error("No interceptors defined.");
143+
}
144+
for (Object interceptor : interceptorMappings.keySet()) {
145+
if (interceptor == null) {
146+
problems.error("Null interceptor provided.");
147+
}
148+
if (interceptorMappings.get(interceptor) != null) {
149+
for (String pattern : interceptorMappings.get(interceptor)) {
150+
if (!StringUtils.hasText(pattern)) {
151+
problems.error("Empty path pattern specified for " + interceptor);
152+
}
153+
}
154+
}
155+
}
156+
}
157+
158+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2002-2011 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+
package org.springframework.web.servlet.config;
17+
18+
import org.springframework.beans.factory.config.BeanDefinition;
19+
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
20+
import org.springframework.beans.factory.parsing.ComponentRegistrar;
21+
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
22+
import org.springframework.beans.factory.support.RootBeanDefinition;
23+
import org.springframework.context.config.AbstractSpecificationExecutor;
24+
import org.springframework.context.config.SpecificationContext;
25+
import org.springframework.web.servlet.handler.MappedInterceptor;
26+
27+
/**
28+
* Executes {@link MvcInterceptors} specification, creating and registering
29+
* bean definitions as appropriate based on the configuration within.
30+
*
31+
* @author Keith Donald
32+
* @author Rossen Stoyanchev
33+
*
34+
* @since 3.1
35+
*/
36+
final class MvcInterceptorsExecutor extends AbstractSpecificationExecutor<MvcInterceptors> {
37+
38+
@Override
39+
protected void doExecute(MvcInterceptors spec, SpecificationContext specContext) {
40+
ComponentRegistrar registrar = specContext.getRegistrar();
41+
Object source = spec.source();
42+
43+
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(spec.sourceName(), source);
44+
45+
for (Object interceptor : spec.interceptorMappings().keySet()) {
46+
RootBeanDefinition beanDef = new RootBeanDefinition(MappedInterceptor.class);
47+
beanDef.setSource(source);
48+
beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
49+
beanDef.getConstructorArgumentValues().addIndexedArgumentValue(0,
50+
spec.interceptorMappings().get(interceptor));
51+
beanDef.getConstructorArgumentValues().addIndexedArgumentValue(1, interceptor);
52+
53+
String beanName = registrar.registerWithGeneratedName(beanDef);
54+
compDefinition.addNestedComponent(new BeanComponentDefinition(beanDef, beanName));
55+
}
56+
57+
registrar.registerComponent(compDefinition);
58+
}
59+
60+
}

0 commit comments

Comments
 (0)