docs/Spring全家桶/SpringMVC源码分析/RequestMapping初始化流程.md
ǰУǷ DispatcherServlet ʼ̣Ľ RequestMapping ʼ̡˵ RequestMapping ʼֱ̣˵ spring @RequestMaping עĹ̡
@EnableWebMvcspring mvc ֮ springmvc demo @EnableWebMvc ע һᵽspring ͨ @EnableWebMvc ע mvc ܣͨ @Import עΪĿ DelegatingWebMvcConfiguration.classͨ @Bean עķ spring mvc
public RequestMappingHandlerMapping requestMappingHandlerMapping(...)public PathMatcher mvcPathMatcher()public UrlPathHelper mvcUrlPathHelper()ôУ @RequestMaping עص RequestMappingHandlerMapping.
RequestMappingHandlerMapping#afterPropertiesSetRequestMappingHandlerMapping Ǵ WebMvcConfigurationSupport У
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
// bean
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
// ãһƪᵽgetXxx()ȡ
PathMatchConfigurer configurer = getPathMatchConfigurer();
Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
UrlPathHelper pathHelper = configurer.getUrlPathHelper();
if (pathHelper != null) {
mapping.setUrlPathHelper(pathHelper);
}
PathMatcher pathMatcher = configurer.getPathMatcher();
if (pathMatcher != null) {
mapping.setPathMatcher(pathMatcher);
}
Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
if (pathPrefixes != null) {
mapping.setPathPrefixes(pathPrefixes);
}
return mapping;
}
//
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
RequestMappingHandlerMapping ģǴһȻ˸ԡ spring bean ڣ̶ RequestMappingHandlerMapping#afterPropertiesSet
@Override
public void afterPropertiesSet() {
// һЩ
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
// øķ
super.afterPropertiesSet();
}
һЩԣȻٵø afterPropertiesSet()ȥ
AbstractHandlerMethodMapping#afterPropertiesSet
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
// getCandidateBeanNames()ȡbeanbeanName
// Ȼ bean
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// bean ¿
processCandidateBean(beanName);
}
}
// һ־ûʲô
handlerMethodsInitialized(getHandlerMethods());
}
spring ڴʱȡ bean beanNameȻ beanName а AbstractHandlerMethodMapping#processCandidateBean
// beanľ
protected void processCandidateBean(String beanName) {
// ȡ beanName Ӧ beanType
// 1\. cglibbeanType Ϊ Xxx$$EnhancerBySpringCGLIB
// 2\. jdk̬beanType Ϊ com.sum.proxy.$Proxy
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
...
}
// isHandler: beanTypeǷ @Controller @RequestMapping ע
if (beanType != null && isHandler(beanType)) {
// handlerMethods
detectHandlerMethods(beanName);
}
}
DZȽϼҪǻȡ beanName Ӧ beanTypeȻжǷ @Controller/@RequestMapping ע⣬֮͵ AbstractHandlerMethodMapping#detectHandlerMethods һ
isHandler(Class) Ҫ˵£
ʶ @Controllerͬʶ @RestControllerע @Controller ע⣬ʶע⣺
// @Controller
@Controller
// ʡע
public @interface XxxController {
...
}
beanName Ӧ bean cglib beanbeanType Ϊ Xxx$$EnhancerBySpringCGLIBʶ丸 (ҲĿ) ϵ @Controller/@ReestMapping;
beanName Ӧ bean jdk ̬ beanbeanType Ϊ com.sum.proxy.$Proxyʶ丸ӿϵ @Controller/@RequestMapping;
beanType com.sum.proxy.$Proxy(jdk ̬)** ʶĿϵ @Controller/@RequestMapping ** ģ
ע @Controller/@RequestMapping Ҫʵ jdk ̬Ҫ @Controller/@RequestMapping ڽӿڼӿڵķϡ
AbstractHandlerMethodMapping#detectHandlerMethods beanType DZע @Controller/@RequestMapping ӿˡ
AbstractHandlerMethodMapping#detectHandlerMethodsAbstractHandlerMethodMapping#detectHandlerMethods ݣ
// handler
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// 1\. cglibõ丸࣬ҲĿ
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 2\. ᴦ userTypeuserTypeIJObjectи༰ userType нӿڵķ
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
// 3\. ÿ @RequestMapping RequestMappingInfo
// @RequestMapping Ϣװö
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
// ﴦϵ @RequestMapping ע
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
...
}
});
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 4\. ォhandlermappingmethod
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
¼£
isHandler(Class) ˵֪spring ʶ jdk ̬Ӧϵ @Controller/@RequestMapping ע⣬˲ִеuserType``userType IJ Object и༰ userType нӿڵķ@RequestMapping RequestMappingInfo @RequestMapping ϢװöУhandler``mapping method 浽 Map СdetectHandlerMethods ĴУ userType``userType иࣨ Object userType нӿڵķ£
MethodIntrospector#selectMethods(Class, MethodIntrospector.MetadataLookup)
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final
MetadataLookup<T> metadataLookup) {
final Map<Method, T> methodMap = new LinkedHashMap<>();
Set<Class<?>> handlerTypes = new LinkedHashSet<>();
Class<?> specificHandlerType = null;
// jdk̬
if (!Proxy.isProxyClass(targetType)) {
// cglib࣬ȡĸ class
specificHandlerType = ClassUtils.getUserClass(targetType);
handlerTypes.add(specificHandlerType);
}
// ȡнӿڣӿڵĸӿ.
handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null
? specificHandlerType : currentHandlerType);
// currentHandlerTypecurrentHandlerTypeIJObjectиࡢ
// currentHandlerTypeнӿڵķ
// aopʱõҲ
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod ||
metadataLookup.inspect(bridgedMethod) == null) {
methodMap.put(specificMethod, result);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
RequestMappingInfoÿ @RequestMapping RequestMappingInfo @RequestMapping ϢװöУ
RequestMappingHandlerMapping#getMappingForMethod
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// ϵ @RequestMapping
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// ϵ @RequestMapping
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// ϲϵ @RequestMapping
// @RequestMapping("/test")ϵ @RequestMapping("/hello")
// ϲĽΪ /test/hello
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// ȡ @RequestMapping ע
RequestMapping requestMapping = AnnotatedElementUtils
.findMergedAnnotation(element, RequestMapping.class);
// ʵΪ
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
// RequestMappingInfo RequestMapping @RequestMapping ע
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
// ʵǽ @RequestMapping עװΪRequestMappingInfo
RequestMappingInfo.Builder builder = RequestMappingInfo
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
// Խ @RequestMapping ע
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
return builder.options(this.config).build();
}
Ǿ @RequestMapping RequestMappingInfoת
RequestMappingInfo ʲô
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
// ṩ˺ܶԣӦ @RequestMapping
@Nullable
private final String name;
private final PatternsRequestCondition patternsCondition;
private final RequestMethodsRequestCondition methodsCondition;
private final ParamsRequestCondition paramsCondition;
private final HeadersRequestCondition headersCondition;
private final ConsumesRequestCondition consumesCondition;
private final ProducesRequestCondition producesCondition;
private final RequestConditionHolder customConditionHolder;
// 췽
public RequestMappingInfo(@Nullable String name, @Nullable PatternsRequestCondition patterns,
@Nullable RequestMethodsRequestCondition methods, @Nullable ParamsRequestCondition params,
@Nullable HeadersRequestCondition headers, @Nullable ConsumesRequestCondition consumes,
@Nullable ProducesRequestCondition produces, @Nullable RequestCondition<?> custom) {
this.name = (StringUtils.hasText(name) ? name : null);
this.patternsCondition = (patterns != null ? patterns : new PatternsRequestCondition());
this.methodsCondition = (methods != null ? methods : new RequestMethodsRequestCondition());
this.paramsCondition = (params != null ? params : new ParamsRequestCondition());
this.headersCondition = (headers != null ? headers : new HeadersRequestCondition());
this.consumesCondition = (consumes != null ? consumes : new ConsumesRequestCondition());
this.producesCondition = (produces != null ? produces : new ProducesRequestCondition());
this.customConditionHolder = new RequestConditionHolder(custom);
}
// builder ģʽǰʹbuilderRequestMappingInfo
private static class DefaultBuilder implements Builder {
// ʡ
...
// ʹbuilder()
@Override
public RequestMappingInfo build() {
ContentNegotiationManager manager = this.options.getContentNegotiationManager();
PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
this.options.getFileExtensions());
// RequestMappingInfo 췽
return new RequestMappingInfo(this.mappingName, patternsCondition,
new RequestMethodsRequestCondition(this.methods),
new ParamsRequestCondition(this.params),
new HeadersRequestCondition(this.headers),
new ConsumesRequestCondition(this.consumes, this.headers),
new ProducesRequestCondition(this.produces, this.headers, manager),
this.customCondition);
}
}
// ʡ
...
}
װ @RequestMapping ϢǽӿϢעᵽ springmvc ˣ
RequestMappingHandlerMapping#registerHandlerMethod
@Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
// øķע
super.registerHandlerMethod(handler, method, mapping);
updateConsumesCondition(mapping, method);
}
// @RequestBody ע
private void updateConsumesCondition(RequestMappingInfo info, Method method) {
ConsumesRequestCondition condition = info.getConsumesCondition();
if (!condition.isEmpty()) {
for (Parameter parameter : method.getParameters()) {
// @RequestBody ע⣬ BodyRequired ֵ
MergedAnnotation<RequestBody> annot = MergedAnnotations.from(parameter)
.get(RequestBody.class);
if (annot.isPresent()) {
condition.setBodyRequired(annot.getBoolean("required"));
break;
}
}
}
}
գ־ע AbstractHandlerMethodMapping#registerHandlerMethod ɵģշˡڷǰһõ Map<Method, T> methods:
ԿӦ T RequestMappingInfo ˡ
AbstractHandlerMethodMapping#registerHandlerMethodAbstractHandlerMethodMapping#registerHandlerMethod 룺
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping
implements InitializingBean {
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
// ʡ˺ö
...
class MappingRegistry {
// Ϣȫmapmapping, handlerMethod, directUrls, nameϢ
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
// е mapping map/test/hello/test/{name}
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
// ȷurl map /test/hello
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
// ʡ˺ö
...
public void register(T mapping, Object handler, Method method) {
...
// ȡдд
this.readWriteLock.writeLock().lock();
try {
// 1\. ȡ handlerMethodʵǽhandler method װһ
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
// 2\. mappingLookup УΪ LinkedHashMap
// springmvcһҪmap
this.mappingLookup.put(mapping, handlerMethod);
// 3\. ȡurlurlLookupΪMultiValueMapmap ͬһkeyжvalue
// springmvcһҪmap
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// 4\. mapping, handlerMethod, directUrls, nameȷװregistry
// registry ΪHashMapspringmvc нӿϢȫһmap
this.registry.put(mapping, new MappingRegistration<>(mapping,
handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
}
}
Կע AbstractHandlerMethodMapping.MappingRegistry#register ɵġǾһע
HandlerMethodش£
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
if (handler instanceof String) {
return new HandlerMethod((String) handler,
obtainApplicationContext().getAutowireCapableBeanFactory(), method);
}
return new HandlerMethod(handler, method);
}
ηǼص HandlerMethod Ĺ췽
public class HandlerMethod {
// ṩ˷dz
protected final Log logger = LogFactory.getLog(getClass());
private final Object bean;
@Nullable
private final BeanFactory beanFactory;
private final Class<?> beanType;
private final Method method;
private final Method bridgedMethod;
private final MethodParameter[] parameters;
@Nullable
private HttpStatus responseStatus;
@Nullable
private String responseStatusReason;
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
private final String description;
// 췽
public HandlerMethod(Object bean, Method method) {
Assert.notNull(bean, "Bean is required");
Assert.notNull(method, "Method is required");
this.bean = bean;
this.beanFactory = null;
this.beanType = ClassUtils.getUserClass(bean);
this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = initMethodParameters();
evaluateResponseStatus();
this.description = initDescription(this.beanType, this.method);
}
// ʡ
...
}
ԿHandlerMethod зdzԣ췽ҲǸֵѡɴ˿ɿHandlerMethod Ƕ handler method һװ
springmvc ʹУСĶͬ requestMapping쳣
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map
'xxxController' method xxxMethod to /xxx/xxx: There is already 'xxxControllter'
bean method xxxMethod mapped.
쳣֤ mapping ʱظ mapping ģ£
// е mapping map/test/hello/test/{name}
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
private void validateMethodMapping(HandlerMethod handlerMethod, T mapping) {
// ҵѴڵ method
HandlerMethod existingHandlerMethod = this.mappingLookup.get(mapping);
// ѴڵhandlerMethodΪգҲڵǰ handlerMethod
if (existingHandlerMethod != null && !existingHandlerMethod.equals(handlerMethod)) {
throw new IllegalStateException(
"Ambiguous mapping. Cannot map '" + handlerMethod.getBean() + "' method \n" +
handlerMethod + "\nto " + mapping + ": There is already '" +
existingHandlerMethod.getBean() + "' bean method\n" +
existingHandlerMethod + " mapped.");
}
}
Ǹ mapping mappingLookup в HandlerMethodҵҵ handlerMethod ǵǰ handlerMethodʾظͱ쳣ˡ
ֱж HandlerMethod RequestMappingInfo жȵģ
HandlerMethod#equals
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof HandlerMethod)) {
return false;
}
HandlerMethod otherMethod = (HandlerMethod) other;
return (this.bean.equals(otherMethod.bean) && this.method.equals(otherMethod.method));
}
RequestMappingInfo#equals
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof RequestMappingInfo)) {
return false;
}
RequestMappingInfo otherInfo = (RequestMappingInfo) other;
return (this.patternsCondition.equals(otherInfo.patternsCondition) &&
this.methodsCondition.equals(otherInfo.methodsCondition) &&
this.paramsCondition.equals(otherInfo.paramsCondition) &&
this.headersCondition.equals(otherInfo.headersCondition) &&
this.consumesCondition.equals(otherInfo.consumesCondition) &&
this.producesCondition.equals(otherInfo.producesCondition) &&
this.customConditionHolder.equals(otherInfo.customConditionHolder));
}
RequestMappingInfo Ҫͬжȣ @RequestMappingõ RequestMappingInfo ȣ
// @RequestMappingȻ·ǡ/helloֵ֧ͬ
// ˵õ RequestMappingInfo
@RequestMapping(path = "/hello")
public String hello1() {
...
}
@RequestMapping(path = "/hello", method = RequestMethod.GET)
public String hello2() {
...
}
@RequestMapping(path = "/hello", method = RequestMethod.POST)
public String hello3() {
...
}
directUrlsspringmvc У url :
ȷ url
@RequestMapping("/hello")
public String hello() {
...
}
ȷ url
@RequestMapping("/{name}")
public String hello(@PathVariable("name") String name) {
...
}
springmvc ṩרŵ urlLookup ȷ urlṹ£
MultiValueMap<String, LinkedList<RequestMappingInfo>>
springmvc λȡȷ url :
List<String> directUrls = getDirectUrls(mapping);
/**
* AbstractHandlerMethodMapping.MappingRegistry#getDirectUrls
* ȡȷurl
*/
private List<String> getDirectUrls(T mapping) {
List<String> urls = new ArrayList<>(1);
// RequestMappingInfoȡе MappingPathPattern
for (String path : getMappingPathPatterns(mapping)) {
// жϵõ MappingPathPattern ǷΪȷurl
if (!getPathMatcher().isPattern(path)) {
urls.add(path);
}
}
return urls;
}
/**
* RequestMappingInfoHandlerMapping#getMappingPathPatterns
* ȡ patterns, RequestMappingInfo ȡ
* ʵϾ @RequestMapping е path() ֵ
*/
@Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
return info.getPatternsCondition().getPatterns();
}
/**
* AntPathMatcher#isPattern
* жǷΪȷ url
* ֻҪ *֮һͬʱ{}Ͳȷurl
*/
@Override
public boolean isPattern(@Nullable String path) {
if (path == null) {
return false;
}
boolean uriVar = false;
for (int i = 0; i < path.length(); i++) {
char c = path.charAt(i);
if (c == '*' || c == '?') {
return true;
}
if (c == '{') {
uriVar = true;
continue;
}
if (c == '}' && uriVar) {
return true;
}
}
return false;
}
£
mapping pathҲ @RequestMapping path() ֵpathһжǷΪȷ url (ֻҪ *``? ֮һͬʱ {``}Ͳȷ url).עӿϢͱȽϼˣʵ map ݣ map
urlLookupȷ url mapΪ MultiValueMap<String, LinkedList<RequestMappingInfo>>``key Ϊȷ url /test/hello``value Ϊ LinkedList<RequestMappingInfo>
mappingLookupе mapping mapе @RequestMapping Ӧ RequestMappingInfo ҵ /test/hello``/test/{name} Ӧ RequestMappingInfoΪ Map<RequestMappingInfo, HandlerMethod>
registryϢȫ mapΪ Map<RequestMappingInfo, MappingRegistration<RequestMappingInfo>>е RequestMappingInfokey Ϊ RequestMappingInfovalue Ϊ MappingRegistration<RequestMappingInfo> MappingRegistration Ϊ mapping, handlerMethod, directUrls, name İװ࣬Ҳ˵ MappingRegistration mapping, handlerMethod, directUrls, name Ϣ
Щ map ע൱ˣǼص Map#put Ͳ˵ˡ
ķ spring @RequestMapping ע̣ⲿ RequestMappingHandlerMapping#afterPropertiesSet У£
beanName 2beanName Ӧ beanTypeжǷ @Controller/@RequestMapping ע⣻@Controller/@RequestMapping beanTypeҵ @RequestMapping עķ @RequestMapping עװΪ RequestMappingInfoһõĽΪһ mapMap<Method, RequestMappingInfo>beanName``beanType Map<Method, RequestMappingInfo> עᵽ springmvc УȽҪ mapMultiValueMap<String, LinkedList<RequestMappingInfo>>Map<RequestMappingInfo, HandlerMethod>(HandlerMethod Ϊ Method İװ)Map<RequestMappingInfo, MappingRegistration<RequestMappingInfo>>(MappingRegistration Ϊ RequestMappingInfo, HandlerMethod, directUrls, beanName İװ)ܵ˵RequestMapping ĴرȽҲǵ˺öβҵ
ԭӣhttps://my.oschina.net/funcy/blog/4715079 ߸ˮƽд֮ӭָԭףҵתϵȨҵתע