HandlerMapping的作用便是根据http请求找到对应Controller的某个执行方法,springmvc默认的策略会提供3个实例,分别为RequestMappingHandlerMapping、BeanNameUrlHandlerMapping、RouterFunctionMapping。现在的应用开发中,最常用的便是RequestMappingHandlerMapping,因此,本文来分析下RequestMappingHandlerMapping几个重要的属性
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
PathMatchConfigurer pathConfig = getPathMatchConfigurer();
if (pathConfig.getPatternParser() != null) {
mapping.setPatternParser(pathConfig.getPatternParser());
}
else {
mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());
Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
}
Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
if (pathConfig.getPathPrefixes() != null) {
mapping.setPathPrefixes(pathConfig.getPathPrefixes());
}
return mapping;
}
首先就是设置order,值越小,越先执行。然后就是interceptor,这个也在应用程序中几乎是必用的,来看下springmvc如何添加的。
protected final Object[] getInterceptors(
FormattingConversionService mvcConversionService,
ResourceUrlProvider mvcResourceUrlProvider) {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
应用开发中,只需要实现addInterceptors方法,往InterceptorRegistry添加拦截器HandlerInterceptor,在内部会转化为InterceptorRegistration(registry维护这一个registration列表)。registration包含了拦截器对象和拦截器要拦截的路径信息。最后在真正的返回拦截器列表时,又会转化为MappedInterceptor(当有设置路径信息的情况下),没有路径信息返回的是handlerInterceptor对象本身。
当完成RequestMappingHandlerMapping对象的创建后,因为该对象还实现了InitializingBean接口,因此在spring bean的生命周期中还会调用afterPropertiesSet方法,这个方法是RequestMappingHandlerMapping的核心,因为在这个方法中要完成http请求路径和对应controller方法映射关系的逻辑处理
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
}
这个方法中,循环处理容器的beanName列表,然后取出beanName对应的Class对象,判断该class对象是否为Handler,判断标准就是看类是否有注解了Controller或者RequestMapping,若有,则会处理该Class对象的Method方法
protected void detectHandlerMethods(Object handler) {
Class> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class> userType = ClassUtils.getUserClass(handlerType);
Map methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
//....中间日志省略
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
这个方法主要作用是找到method对象,还有对应的RequestMappingInfo对象,顾名思义,这个对象封装的是@RequestMapping注解的相关属性信息,然后再将这些对象关系注册
获取method对象及其RequestMappingInfo对象,主要为selectMethods方法,方法的两个入参,一个是Class对象,另一个是获取method对应RequestMappingInfo的方法。
public static Map selectMethods(Class> targetType, final MetadataLookup metadataLookup) {
final Map methodMap = new LinkedHashMap<>();
Set> handlerTypes = new LinkedHashSet<>();
Class> specificHandlerType = null;
//若当前Class对象是代理对象,那么拿到被代理类的Class对象
if (!Proxy.isProxyClass(targetType)) {
specificHandlerType = ClassUtils.getUserClass(targetType);
handlerTypes.add(specificHandlerType);
}
//获取接口的Class对象,并且循环处理每个Class
handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
for (Class> currentHandlerType : handlerTypes) {
final Class> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
//这里是因为方法可能是重写的,因此要拿到具体的方法对象
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
//拿到方法对应的RequestMappingInfo对象
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
//将method对象及对应的requestMappingInfo对象加入到map中,并返回
methodMap.put(specificMethod, result);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
上述这个方法主要逻辑就是拿到要处理的Class对象及所实现的接口的Class对象,然后循环处理每个Class对象下的method方法,若method方法符合MethodFilter((method -> !method.isBridge() && !method.isSynthetic())),那么获取method对象对应的RequestMappingInfo对象,然后添加到map中,处理完所有的Class对象后,map返回。
method对象如何获取到RequestMappingInfo
protected RequestMappingInfo getMappingForMethod(Method method, Class> handlerType) {
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
首先,method方法需要有@RequestMapping注解的,然后解析注解的相关属性信息,创建RequestMappingInfo对象,该对象内部有很多成员RequestCondition,condition对象最终维护@RequestMapping注解对应的属性值,如consumes对应ConsumesRequestCondition对象。
再接着,获取Class对象的RequestMappingInfo对象,然后,再合并method对象和Class对象的RequestMappingInfo对象
当获取到所有的映射关系后,将每个映射关系注册到handlermapping的registry中
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
Set directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
registry的几个成员如下,其中T为RequestMappingInfo,也就是handlerMapping的http请求和controller执行方法的关系维护在registry成员中。
private final Map> registry = new HashMap<>();
private final MultiValueMap pathLookup = new LinkedMultiValueMap<>();
private final Map> nameLookup = new ConcurrentHashMap<>();
private final Map corsLookup = new ConcurrentHashMap<>();
当对所有的handler(注解了RequestMapping或者Controller的类)的所有注解了RequestMapping的方法解析生成对应的映射关系,RequestMappingInfo--HandlerMethod后,RequestMappingHandlerMapping的初始化方法afterPropertiesSet就处理完了。