关键类图
关键类和类成员
// 本期重点核心类,关于泛型T在代码分析会有讲解
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
// 内部维护 URL 和 controller类和方法
private final MappingRegistry mappingRegistry = new MappingRegistry();
// 映射注册类
class MappingRegistry {
// 地址映射
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
// 关键,此处的pathLookup 就是通过url地址查找对应的映射属性的
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
}
// 映射实体此处维护的是直达Controller中的方法
static class MappingRegistration<T> {
// 留个疑问, 此处的泛型T 下文会有解释
private final T mapping;
// 内部维护着controller中标注的@RequestMapping方法
private final HandlerMethod handlerMethod;
// url地址
private final Set<String> directPaths;
}
}
简单概述下类结构
RequestMappingHandlerMapping
继承关系
- 继承RequestMappingInfoHandlerMapping
- 间接继承AbstractHandlerMethodMapping<RequestMappingInfo>
解惑上文中的泛型T
- 看完继承关系后可以确定 T --> RequestMappingInfo
RequestMappingInfo
- 简单概述就是,在URL请求中要满足下方的复合条件
- 更为直观的理解是,在具体选中哪种URL映射关系的匹配条件。
/**
* Request mapping information. A composite for the following conditions:
* <ol>
* <li>{@link PathPatternsRequestCondition} with parsed {@code PathPatterns} or
* {@link PatternsRequestCondition} with String patterns via {@code PathMatcher}
* <li>{@link RequestMethodsRequestCondition}
* <li>{@link ParamsRequestCondition}
* <li>{@link HeadersRequestCondition}
* <li>{@link ConsumesRequestCondition}
* <li>{@link ProducesRequestCondition}
* <li>{@code RequestCondition} (optional, custom request condition)
* </ol>
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1
*/
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
@Nullable
private final String name;
@Nullable
private final PathPatternsRequestCondition pathPatternsCondition;
@Nullable
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;
private final int hashCode;
private final BuilderConfiguration options;
}
数据结构分析
举个例子,我的controller类
@RestController
@RequestMapping("/user-manager")
public class UserController {
@GetMapping("/user")
public UserVO user(Long userId) {
return new UserVO();
}
}
对应的关系入下
上述算是整个URL映射的一个数据结构分析,下边真正开始源码解析
初始化一
org.springframework.beans.factory.InitializingBean接口
- 解释下这个接口的能力
- 在spring生命周期中 大体分为:实例化 -> 属性赋值 -> 初始化 -> 使用 —> bean销毁
- 而InitializingBean接口就是在bean属性复制后,可以让我们对bean进行一个自定义的处理换句话说,也可以理解为在spirng属性复制后,我们做一个自己想要的init处理。
- 此处AbstractHandlerMethodMapping实现了这个接口,也就是借助于初始化的回调做了自定义的初始化。
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
// 初始化所有的handlerMethods
protected void initHandlerMethods() {
// 循环执行所有满足条件的beanName
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// 关键方法入口
processCandidateBean(beanName);
}
}
// 算是个后置处理此处,AbstractHandlerMethodMapping一个方法,只打印了日志
// RequestMappingHandlerMapping 也未做实现,直接跳过
handlerMethodsInitialized(getHandlerMethods());
}
}
// 处理复合条件的bean
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// 重点关注下 isHandler方法
if (beanType != null && isHandler(beanType)) {
// 处理满足条件的bean,源码下方会解释。
detectHandlerMethods(beanName);
}
}
// RequestMappingHandlerMapping 重写了AbstractHandlerMethodMapping的方法
// 看一眼就明白了。只要是类上标注了@Controller和@RequestMapping的注解的就返回true
// 由于AnnotatedElementUtils.hasAnnotation()方法buff的加持
// 例如@RestController也会被标记为返回为true的
// 有兴趣的小伙伴可以看看@RestController注解其实也是引用了@Controller
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
上述代码总结
- 借助于spring生命周期初始化的回调功能,可以知道RequestMappingHandlerMapping自定义初始化的方法入口。
- RequestMappingHandlerMapping重写了isHandler()方法,挑选候选的bean进行,进行进一步加工。
- AnnotatedElementUtils.hasAnnotation() get到了一个新的知识点,有兴趣的小伙伴可以看看学习下。
初始化一、进一步加工
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
protected void detectHandlerMethods(Object handler) {
// 获取bean类型
// 如果入参是String类型则通过obtainApplicationContext获取bean的类型,通过这种方式
// 如果该bean没有加载到spring容器中会先进行bean化,然后在获取class
// 否则直接获取class
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// 会不会疑惑?
// 明明上边已经获取了类的类型 Class<?>,为什么还要获取一遍?
// 此处简单说下,因为上边直接调用的Object的getClass方法,如果该bean被cglib代理了
// 那么上边获取的类的类型不是想要的类的类型,所以此处再次获取一遍
// 有兴趣的朋友可以看看ClassUtils的方法。
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 方法映射map集合
// 先说下数据结构,以免刚学习的朋友蒙蔽
// 还是举个我们项目中controller的例子
// map中的key:就是Controller方法中标注了@RequestMapping的方法(包涵派生注解比如@GetMapping等等)
// map中的value:就是最开始说的RequestMappingInfo
// 下方有贴图,可以看下 ①
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
// 获取mapping的映射方法 下边会有代码解释 ②
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
else if (mappingsLogger.isDebugEnabled()) {
mappingsLogger.debug(formatMappings(userType, methods));
}
// 将所有的methods经过处理后,进行注册
methods.forEach((method, mapping) -> {
// 此处简单解释下有兴趣的朋友可以看看
// 此处是将method的私有方法,经过反射后改成public
// 也就是说你的controller方法 标记了@getmapping,如果方法是私有的改成public。
// 为什么这么做?埋个伏笔。
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 注册 ④
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
// ②
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 创建RequestMappingInfo 这个也就是上文map<Method,T>的 T
// createRequestMappingInfo有一个关键的信息,下边会简单介绍下
RequestMappingInfo info = createRequestMappingInfo(method); ③
// 下边就是组装RequestMappingInfo,不再细说了,有兴趣的朋友可以看看
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;
}
}
// ③ 简单说下,就是只有方法上标注了@RequestMapping注解的才会创建 (派生注解也算,比如@GetMapping)
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
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);
}
// AbstractHandlerMethodMapping的方法,注册 ④
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
// AbstractHandlerMethodMapping.MappingRegistry#register
// 嗨,终于走到最后一个方法了,该方法走完后也就呼应了上文的数据结构图。
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
// 此处的Method是jdk的,现在讲Method维护到HandlerMethod中
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// 此处简单说下
// 咱们在开发controller的时候 有时候一不小心写了两个一模一样的接口,
// 此处的作用就是校验重复的接口
validateMethodMapping(handlerMethod, mapping);
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
// 将url地址维护到pathLookup
// 举个例子 path 就是开发中的 user/list 这种
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
// 基于name的映射 此处略过
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
// cors 略过 其实就是有个注解@CrossOrigin 可以标注到类和方法上,有兴趣的可以自己看下
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
this.corsLookup.put(handlerMethod, corsConfig);
}
// 放入map集合中,上边有数据结构关系图
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
} finally {
this.readWriteLock.writeLock().unlock();
}
}
代码①
总结下
- 获取controller类和类中的所有满足条件的方法
- 将URL提取出来放入MappingRegistry.pathLookup中,pathLookup是一个LinkedMultiValueMap类型的,key:url,value就是RequestMappingInfo
- 最后将RequestMappingInfo当做key,放入MappingRegistry.registry的hashmap中,value就是MappingRegistration(RequestMappingInfo,handlerMethod,directPaths...)
初始化二-DispatcherServlet
- DispatcherServlet在第一次访问时会触发九大组件的init方法,此处不细说,仅仅说下handlerMapping初始化做了什么
// org.springframework.web.servlet.DispatcherServlet#initHandlerMappings
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// 默认是true
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 找出所有实现了HandlerMapping的接口,我们此次分析的RequestMappingHandlerMapping也在其中
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
// 因为做了下排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
// 如果detectAllHandlerMappings是false,则进行bean加载,效果是一样的。
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
for (HandlerMapping mapping : this.handlerMappings) {
// 循环遍历mapping,只要有一个返回true就可以
// 我们本次分析的RequestMappingHandlerMapping 此处返回的就是true
// 至于是怎么设置的,有兴趣的朋友可以看下这个类
// org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerMapping
// 在一开始加载的时候就设置好了。
if (mapping.usesPathPatterns()) {
// 这个布尔值的作用,其实就是判断是否将HttpServletRequest的getRequestURI的url地址缓存起来
this.parseRequestPath = true;
break;
}
}
}
mvc请求流程之HandlerMapping
数据结构
在mvc请求中匹配controller方法时起关键作用的一个类Match
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.Match
doDispatch不是本期的重点,简单看下关于handlerMapping的关键方法
// DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// ① 通过HttpServletRequest获取HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
// 如果找不到,则进行noHandlerFound处理
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// HandlerInterceptor前置处理器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// HandlerInterceptor 后置处理器
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// HandlerInterceptor执行after方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
// ① 简单来说就是遍历所有实现了HandlerMapping接口的实现类。
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
// 此处我们只需关心实现类为RequestMappingHandlerMapping即可
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
获取HandlerExecutionChain
- 通过getHandlerInternal方法获取HandlerMethod
- 然后调用getHandlerExecutionChain方法获取chain
// org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获取handler,返回体是,HandlerMethod ②
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// Ensure presence of cached lookupPath for interceptors and others
if (!ServletRequestPathUtils.hasCachedPath(request)) {
initLookupPath(request);
}
// 生成HandlerExecutionChain
// HandlerExecutionChain属性中包涵HandlerInterceptor的实现类,包括我们自定义的
// 前提是满足规则的,此处不细讲
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
logger.debug("Mapped to " + executionChain.getHandler());
}
// cors不做细讲
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
组装HandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 从上边代码可以看出handler的类型是HandlerMethod
// 所以此处new HandlerExecutionChain(handler) 新建了一个HandlerExecutionChain
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
// 将满足条件的HandlerInterceptor实现类加入HandlerExecutionChain中
// HandlerInterceptor不在本次分析中略过
// 不过此处可以看出,HandlerInterceptor是在此处加入进去的
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(request)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
获取HandlerMethod
// org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
// ②
@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 拿到请求的URL 比如 user/list
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
// 通过url和request拿到HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
// 数据结构上边有贴图,最好看下数据结构
List<Match> matches = new ArrayList<>();
// 此处通过lookupPath也就是url地址获取RequestMappingInfo集合
// 源码就一行代码就是return this.pathLookup.get(urlPath);
// 前边初始化一的时候 我们提过的pathLookup 会维护一个键值对。
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
// 将满足要求的RequestMappingInfo放入matches集合中
//RequestMappingInfo前文中提到过,这个info是一些匹配的规则
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
// 恕小弟无能无法复现满足matches大于1的
if (matches.size() > 1) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
for (Match match : matches) {
if (match.hasCorsConfig()) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
}
}
else {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.getHandlerMethod().getMethod();
Method m2 = secondBestMatch.getHandlerMethod().getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
// 最后MappingRegistration中维护的HandlerMethod返回
return bestMatch.getHandlerMethod();
}
else {
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}
关于handlerMapping分析到此结束
整体数据结构如下
如有不足支出请支出,文章将持续更改
作者的疑问点
- matches什么时候大于1
- HandlerExecutionChain为什么每一次都new,缓存起来应该可以
- 我的理解动态植入HandlerInterceptor的缘故,比如白名单等问题