20
20
import java .lang .reflect .AnnotatedElement ;
21
21
import java .lang .reflect .Method ;
22
22
import java .lang .reflect .Parameter ;
23
+ import java .util .ArrayList ;
23
24
import java .util .Collections ;
24
25
import java .util .LinkedHashMap ;
25
26
import java .util .List ;
34
35
import org .springframework .core .annotation .MergedAnnotations ;
35
36
import org .springframework .core .annotation .MergedAnnotations .SearchStrategy ;
36
37
import org .springframework .core .annotation .RepeatableContainers ;
38
+ import org .springframework .lang .NonNull ;
37
39
import org .springframework .lang .Nullable ;
38
40
import org .springframework .stereotype .Controller ;
39
41
import org .springframework .util .Assert ;
@@ -152,42 +154,59 @@ protected boolean isHandler(Class<?> beanType) {
152
154
153
155
/**
154
156
* Uses type-level and method-level {@link RequestMapping @RequestMapping}
155
- * and {@link HttpExchange @HttpExchange} annotations to create the
156
- * {@link RequestMappingInfo}.
157
- * @return the created {@code RequestMappingInfo}, or {@code null} if the method
157
+ * and {@link HttpExchange @HttpExchange} annotations to create the list
158
+ * of {@link RequestMappingInfo}.
159
+ * @return the created list of {@code RequestMappingInfo}, or an empty list if the method
158
160
* does not have a {@code @RequestMapping} or {@code @HttpExchange} annotation
159
161
* @see #getCustomMethodCondition(Method)
160
162
* @see #getCustomTypeCondition(Class)
161
163
*/
162
164
@ Override
163
- @ Nullable
164
- protected RequestMappingInfo getMappingForMethod (Method method , Class <?> handlerType ) {
165
- RequestMappingInfo info = createRequestMappingInfo (method );
166
- if (info != null ) {
167
- RequestMappingInfo typeInfo = createRequestMappingInfo (handlerType );
168
- if (typeInfo != null ) {
169
- info = typeInfo .combine (info );
165
+ @ NonNull
166
+ protected List <RequestMappingInfo > getListMappingsForMethod (Method method , Class <?> handlerType ) {
167
+ List <RequestMappingInfo > result = new ArrayList <>();
168
+ List <RequestMappingInfo > infos = buildListOfRequestMappingInfo (method );
169
+ if (!infos .isEmpty ()) {
170
+ List <RequestMappingInfo > typeInfos = buildListOfRequestMappingInfo (handlerType );
171
+ if (!typeInfos .isEmpty ()) {
172
+ List <RequestMappingInfo > requestMappingInfos = new ArrayList <>();
173
+ for (RequestMappingInfo info : infos ) {
174
+ for (RequestMappingInfo typeInfo : typeInfos ) {
175
+ requestMappingInfos .add (typeInfo .combine (info ));
176
+ }
177
+ }
178
+ infos = requestMappingInfos ;
170
179
}
171
- if (info .getPatternsCondition ().isEmptyPathMapping ()) {
172
- info = info .mutate ().paths ("" , "/" ).options (this .config ).build ();
180
+ for (RequestMappingInfo info : infos ) {
181
+ if (info .getPatternsCondition ().isEmptyPathMapping ()) {
182
+ info = info .mutate ().paths ("" , "/" ).options (this .config ).build ();
183
+ }
184
+
185
+ result .add (info );
173
186
}
174
- for (Map .Entry <String , Predicate <Class <?>>> entry : this .pathPrefixes .entrySet ()) {
175
- if (entry .getValue ().test (handlerType )) {
176
- String prefix = entry .getKey ();
177
- if (this .embeddedValueResolver != null ) {
178
- prefix = this .embeddedValueResolver .resolveStringValue (prefix );
187
+
188
+ for (int idx = 0 ; idx < result .size (); idx ++) {
189
+ RequestMappingInfo info = result .get (idx );
190
+
191
+ for (Map .Entry <String , Predicate <Class <?>>> entry : this .pathPrefixes .entrySet ()) {
192
+ if (entry .getValue ().test (handlerType )) {
193
+ String prefix = entry .getKey ();
194
+ if (this .embeddedValueResolver != null ) {
195
+ prefix = this .embeddedValueResolver .resolveStringValue (prefix );
196
+ }
197
+ info = RequestMappingInfo .paths (prefix ).options (this .config ).build ().combine (info );
198
+ result .set (idx , info );
199
+ break ;
179
200
}
180
- info = RequestMappingInfo .paths (prefix ).options (this .config ).build ().combine (info );
181
- break ;
182
201
}
183
202
}
184
203
}
185
- return info ;
204
+ return Collections . unmodifiableList ( result ) ;
186
205
}
187
206
188
- @ Nullable
189
- private RequestMappingInfo createRequestMappingInfo (AnnotatedElement element ) {
190
- RequestMappingInfo requestMappingInfo = null ;
207
+ @ NonNull
208
+ private List < RequestMappingInfo > buildListOfRequestMappingInfo (AnnotatedElement element ) {
209
+ List < RequestMappingInfo > requestMappingInfos = new ArrayList <>() ;
191
210
RequestCondition <?> customCondition = (element instanceof Class <?> clazz ?
192
211
getCustomTypeCondition (clazz ) : getCustomMethodCondition ((Method ) element ));
193
212
@@ -200,22 +219,25 @@ private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
200
219
logger .warn ("Multiple @RequestMapping annotations found on %s, but only the first will be used: %s"
201
220
.formatted (element , requestMappings ));
202
221
}
203
- requestMappingInfo = createRequestMappingInfo ((RequestMapping ) requestMappings .get (0 ).annotation , customCondition );
222
+
223
+ for (AnnotationDescriptor requestMapping : requestMappings ) {
224
+ requestMappingInfos .add (createRequestMappingInfo ((RequestMapping ) requestMapping .annotation , customCondition ));
225
+ }
204
226
}
205
227
206
228
List <AnnotationDescriptor > httpExchanges = descriptors .stream ()
207
229
.filter (desc -> desc .annotation instanceof HttpExchange ).toList ();
208
230
if (!httpExchanges .isEmpty ()) {
209
- Assert .state (requestMappingInfo == null ,
231
+ Assert .state (requestMappings . isEmpty () ,
210
232
() -> "%s is annotated with @RequestMapping and @HttpExchange annotations, but only one is allowed: %s"
211
233
.formatted (element , Stream .of (requestMappings , httpExchanges ).flatMap (List ::stream ).toList ()));
212
- Assert . state ( httpExchanges . size () == 1 ,
213
- () -> "Multiple @HttpExchange annotations found on %s, but only one is allowed: %s"
214
- . formatted ( element , httpExchanges ));
215
- requestMappingInfo = createRequestMappingInfo (( HttpExchange ) httpExchanges . get ( 0 ). annotation , customCondition );
234
+
235
+ for ( AnnotationDescriptor httpExchange : httpExchanges ) {
236
+ requestMappingInfos . add ( createRequestMappingInfo (( HttpExchange ) httpExchange . annotation , customCondition ));
237
+ }
216
238
}
217
239
218
- return requestMappingInfo ;
240
+ return Collections . unmodifiableList ( requestMappingInfos ) ;
219
241
}
220
242
221
243
/**
0 commit comments