1
1
/*
2
- * Copyright 2002-2009 the original author or authors.
2
+ * Copyright 2002-2010 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
37
37
import org .springframework .web .servlet .handler .AbstractDetectingUrlHandlerMapping ;
38
38
39
39
/**
40
- * Implementation of the {@link org.springframework.web.servlet.HandlerMapping} interface that maps handlers based on
41
- * HTTP paths expressed through the {@link RequestMapping} annotation at the type or method level.
40
+ * Implementation of the {@link org.springframework.web.servlet.HandlerMapping}
41
+ * interface that maps handlers based on HTTP paths expressed through the
42
+ * {@link RequestMapping} annotation at the type or method level.
42
43
*
43
- * <p>Registered by default in {@link org.springframework.web.servlet.DispatcherServlet} on Java 5+. <b>NOTE:</b> If you
44
- * define custom HandlerMapping beans in your DispatcherServlet context, you need to add a
45
- * DefaultAnnotationHandlerMapping bean explicitly, since custom HandlerMapping beans replace the default mapping
46
- * strategies. Defining a DefaultAnnotationHandlerMapping also allows for registering custom interceptors:
44
+ * <p>Registered by default in {@link org.springframework.web.servlet.DispatcherServlet}
45
+ * on Java 5+. <b>NOTE:</b> If you define custom HandlerMapping beans in your
46
+ * DispatcherServlet context, you need to add a DefaultAnnotationHandlerMapping bean
47
+ * explicitly, since custom HandlerMapping beans replace the default mapping strategies.
48
+ * Defining a DefaultAnnotationHandlerMapping also allows for registering custom
49
+ * interceptors:
47
50
*
48
- * <pre class="code"> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
49
- * <property name="interceptors"> ... </property> </bean></pre>
51
+ * <pre class="code">
52
+ * <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
53
+ * <property name="interceptors">
54
+ * ...
55
+ * </property>
56
+ * </bean></pre>
50
57
*
51
- * Annotated controllers are usually marked with the {@link Controller} stereotype at the type level. This is not
52
- * strictly necessary when {@link RequestMapping} is applied at the type level (since such a handler usually implements
53
- * the {@link org.springframework.web.servlet.mvc.Controller} interface). However, {@link Controller} is required for
54
- * detecting {@link RequestMapping} annotations at the method level if {@link RequestMapping} is not present at the type
55
- * level.
58
+ * Annotated controllers are usually marked with the {@link Controller} stereotype
59
+ * at the type level. This is not strictly necessary when {@link RequestMapping} is
60
+ * applied at the type level (since such a handler usually implements the
61
+ * {@link org.springframework.web.servlet.mvc.Controller} interface). However,
62
+ * {@link Controller} is required for detecting {@link RequestMapping} annotations
63
+ * at the method level if {@link RequestMapping} is not present at the type level.
56
64
*
57
- * <p><b>NOTE:</b> Method-level mappings are only allowed to narrow the mapping expressed at the class level (if any).
58
- * HTTP paths need to uniquely map onto specific handler beans, with any given HTTP path only allowed to be mapped onto
59
- * one specific handler bean (not spread across multiple handler beans). It is strongly recommended to co-locate related
60
- * handler methods into the same bean.
65
+ * <p><b>NOTE:</b> Method-level mappings are only allowed to narrow the mapping
66
+ * expressed at the class level (if any). HTTP paths need to uniquely map onto
67
+ * specific handler beans, with any given HTTP path only allowed to be mapped
68
+ * onto one specific handler bean (not spread across multiple handler beans).
69
+ * It is strongly recommended to co-locate related handler methods into the same bean.
61
70
*
62
- * <p>The {@link AnnotationMethodHandlerAdapter} is responsible for processing annotated handler methods, as mapped by
63
- * this HandlerMapping. For {@link RequestMapping} at the type level, specific HandlerAdapters such as {@link
64
- * org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter} apply.
71
+ * <p>The {@link AnnotationMethodHandlerAdapter} is responsible for processing
72
+ * annotated handler methods, as mapped by this HandlerMapping. For
73
+ * {@link RequestMapping} at the type level, specific HandlerAdapters such as
74
+ * {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter} apply.
65
75
*
66
76
* @author Juergen Hoeller
67
77
* @author Arjen Poutsma
78
+ * @since 2.5
68
79
* @see RequestMapping
69
80
* @see AnnotationMethodHandlerAdapter
70
- * @since 2.5
71
81
*/
72
82
public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping {
73
83
74
84
private boolean useDefaultSuffixPattern = true ;
75
85
76
86
private final Map <Class , RequestMapping > cachedMappings = new HashMap <Class , RequestMapping >();
77
87
88
+
78
89
/**
79
- * Set whether to register paths using the default suffix pattern as well: i.e. whether "/users" should be registered
80
- * as "/users.*" and "/users/" too. <p>Default is "true". Turn this convention off if you intend to interpret your
81
- * <code>@RequestMapping</code> paths strictly. <p>Note that paths which include a ".xxx" suffix or end with "/"
82
- * already will not be transformed using the default suffix pattern in any case.
90
+ * Set whether to register paths using the default suffix pattern as well:
91
+ * i.e. whether "/users" should be registered as "/users.*" and "/users/" too.
92
+ * <p>Default is "true". Turn this convention off if you intend to interpret
93
+ * your <code>@RequestMapping</code> paths strictly.
94
+ * <p>Note that paths which include a ".xxx" suffix or end with "/" already will not be
95
+ * transformed using the default suffix pattern in any case.
83
96
*/
84
97
public void setUseDefaultSuffixPattern (boolean useDefaultSuffixPattern ) {
85
98
this .useDefaultSuffixPattern = useDefaultSuffixPattern ;
86
99
}
87
100
101
+
88
102
/**
89
- * Checks for presence of the {@link org.springframework.web.bind.annotation.RequestMapping} annotation on the handler
90
- * class and on any of its methods.
103
+ * Checks for presence of the {@link org.springframework.web.bind.annotation.RequestMapping}
104
+ * annotation on the handler class and on any of its methods.
91
105
*/
92
106
@ Override
93
107
protected String [] determineUrlsForHandler (String beanName ) {
@@ -101,7 +115,7 @@ protected String[] determineUrlsForHandler(String beanName) {
101
115
String [] typeLevelPatterns = mapping .value ();
102
116
if (typeLevelPatterns .length > 0 ) {
103
117
// @RequestMapping specifies paths at type level
104
- String [] methodLevelPatterns = determineUrlsForHandlerMethods (handlerType );
118
+ String [] methodLevelPatterns = determineUrlsForHandlerMethods (handlerType , true );
105
119
for (String typeLevelPattern : typeLevelPatterns ) {
106
120
if (!typeLevelPattern .startsWith ("/" )) {
107
121
typeLevelPattern = "/" + typeLevelPattern ;
@@ -110,7 +124,8 @@ protected String[] determineUrlsForHandler(String beanName) {
110
124
for (String methodLevelPattern : methodLevelPatterns ) {
111
125
if (methodLevelPattern == null ) {
112
126
hasEmptyMethodLevelMappings = true ;
113
- } else {
127
+ }
128
+ else {
114
129
String combinedPattern = getPathMatcher ().combine (typeLevelPattern , methodLevelPattern );
115
130
addUrlsForPath (urls , combinedPattern );
116
131
}
@@ -124,12 +139,12 @@ protected String[] determineUrlsForHandler(String beanName) {
124
139
}
125
140
else {
126
141
// actual paths specified by @RequestMapping at method level
127
- return determineUrlsForHandlerMethods (handlerType );
142
+ return determineUrlsForHandlerMethods (handlerType , false );
128
143
}
129
144
}
130
145
else if (AnnotationUtils .findAnnotation (handlerType , Controller .class ) != null ) {
131
146
// @RequestMapping to be introspected at method level
132
- return determineUrlsForHandlerMethods (handlerType );
147
+ return determineUrlsForHandlerMethods (handlerType , false );
133
148
}
134
149
else {
135
150
return null ;
@@ -138,13 +153,17 @@ else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null)
138
153
139
154
/**
140
155
* Derive URL mappings from the handler's method-level mappings.
141
- *
142
- * <p>The returned array may contain {@code null}, indicating an empty {@link RequestMapping} value.
143
- *
144
156
* @param handlerType the handler type to introspect
157
+ * @param indicateEmpty whether the returned array should contain
158
+ * <code>null</code> in case of an empty {@link RequestMapping} value.
145
159
* @return the array of mapped URLs
146
160
*/
147
- protected String [] determineUrlsForHandlerMethods (Class <?> handlerType ) {
161
+ protected String [] determineUrlsForHandlerMethods (Class <?> handlerType , final boolean indicateEmpty ) {
162
+ String [] subclassResult = determineUrlsForHandlerMethods (handlerType );
163
+ if (subclassResult != null ) {
164
+ return subclassResult ;
165
+ }
166
+
148
167
final Set <String > urls = new LinkedHashSet <String >();
149
168
Class <?>[] handlerTypes =
150
169
Proxy .isProxyClass (handlerType ) ? handlerType .getInterfaces () : new Class <?>[]{handlerType };
@@ -158,7 +177,8 @@ public void doWith(Method method) {
158
177
for (String mappedPattern : mappedPatterns ) {
159
178
addUrlsForPath (urls , mappedPattern );
160
179
}
161
- } else {
180
+ }
181
+ else if (indicateEmpty ) {
162
182
// empty method-level RequestMapping
163
183
urls .add (null );
164
184
}
@@ -169,9 +189,17 @@ public void doWith(Method method) {
169
189
return StringUtils .toStringArray (urls );
170
190
}
171
191
192
+ /**
193
+ * Derive URL mappings from the handler's method-level mappings.
194
+ * @param handlerType the handler type to introspect
195
+ * @return the array of mapped URLs
196
+ */
197
+ protected String [] determineUrlsForHandlerMethods (Class <?> handlerType ) {
198
+ return null ;
199
+ }
200
+
172
201
/**
173
202
* Add URLs and/or URL patterns for the given path.
174
- *
175
203
* @param urls the Set of URLs for the current bean
176
204
* @param path the currently introspected path
177
205
*/
@@ -183,9 +211,9 @@ protected void addUrlsForPath(Set<String> urls, String path) {
183
211
}
184
212
}
185
213
214
+
186
215
/**
187
216
* Validate the given annotated handler against the current request.
188
- *
189
217
* @see #validateMapping
190
218
*/
191
219
@ Override
@@ -200,9 +228,8 @@ protected void validateHandler(Object handler, HttpServletRequest request) throw
200
228
}
201
229
202
230
/**
203
- * Validate the given type-level mapping metadata against the current request, checking HTTP request method and
204
- * parameter conditions.
205
- *
231
+ * Validate the given type-level mapping metadata against the current request,
232
+ * checking HTTP request method and parameter conditions.
206
233
* @param mapping the mapping metadata to validate
207
234
* @param request current HTTP request
208
235
* @throws Exception if validation failed
@@ -224,9 +251,9 @@ protected void validateMapping(RequestMapping mapping, HttpServletRequest reques
224
251
225
252
String [] mappedHeaders = mapping .headers ();
226
253
if (!ServletAnnotationMappingUtils .checkHeaders (mappedHeaders , request )) {
227
- throw new ServletRequestBindingException (
228
- "Header conditions \" " + StringUtils .arrayToDelimitedString (mappedHeaders , ", " ) +
229
- "\" not met for actual request" );
254
+ throw new ServletRequestBindingException ("Header conditions \" " +
255
+ StringUtils .arrayToDelimitedString (mappedHeaders , ", " ) +
256
+ "\" not met for actual request" );
230
257
}
231
258
}
232
259
0 commit comments