spring源码阅读4,请求如何匹配

前文提要

根据前文
我们知道所有的http请求都会到
DispatcherServlet#doDispatch内,执行下面两个逻辑

  • 构造HandlerExecutionChain,根据请求的路径找到HandlerMethod(带有Method反射属性,也就是对应Controller中的方法),然后匹配路径对应的拦截器,有了HandlerMethod和拦截器构造个HandlerExecutionChain对象。HandlerExecutionChain对象的获取是通过HandlerMapping接口提供的方法中得到。
  • 构造HandlerAdapter,有了HandlerExecutionChain之后,通过HandlerAdapter对象进行处理得到ModelAndView对象,HandlerMethod内部handle的时候,使用各种HandlerMethodArgumentResolver实现类处理HandlerMethod的参数,使用各种HandlerMethodReturnValueHandler实现类处理返回值。 最终返回值被处理成ModelAndView对象,这期间发生的异常会被HandlerExceptionResolver接口实现类进行处理

但是如何根据我们在Controller中定义的RequestMapping()来找到具体的处理方法呢? 本文主要介绍这个问题。

相关接口和实现

首先是RequestMappingInfo,它保存了匹配的各种条件,如Header条件,Pattern条件等根据名字其实也不难看出,它的继承关系如下

spring源码阅读4,请求如何匹配_第1张图片
image.png

org.springframework.web.servlet.mvc.condition.RequestCondition定义了一个接口,用于匹配请求

MappingRegistry用于保存所有的匹配信息,是AbstractHandlerMethodMapping的一个内部类
这个类设计上比较关键,受限于蝙蝠,这里先不展开

HandlerMethod类,用于存储方法的相关信息

spring源码阅读4,请求如何匹配_第2张图片
image.png

初始化RequestMappingHandlerMapping

开启,将会注册两个bean。RequestMappingHandlerMappingRequetMappingHandlerAdapter
用于分配请求
RequestMappingHandlerMapping的继承关系如下

spring源码阅读4,请求如何匹配_第3张图片
image.png

继承了 InitializingBean说明创建后会初始化,实现 HandlerMapping可以获取 HandlerExecutionChain,继承了 ApplicationObjectSupport可以方便的获取上下文对象
先看 RequestMappingHandlerMapping初始化,调用了 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods,方法如下。

获取所有的bean 找到所有被RequesMapping修饰的方法和类,调用detectHandlerMethods

protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}

//获取所有的bean
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));

for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class beanType = null;
//获取bean的类型
try {
beanType = getApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
//isHandler 判断类上是否有Controller或者RequestMapping注解
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}

detectHandlerMethods 对被修饰的方法创建一个map用于存储方法和具体匹配关系的映射,并且调用registerHandlerMethod把这些映射关系注册到MappingRegistry的一个实例中,这个实例在org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#mappingRegistry

protected void detectHandlerMethods(final Object handler) {
Class handlerType = (handler instanceof String ?
getApplicationContext().getType((String) handler) : handler.getClass());
final Class userType = ClassUtils.getUserClass(handlerType);

//key是Method,值是一个泛型,在webmvc中是指org.springframework.web.servlet.mvc.method.RequestMappingInfo
Map methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup() {
@Override
public T inspect(Method method) {
try {
//是抽象方法,具体由RequestMappingHandlerMapping实现
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
}
});

if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
for (Map.Entry entry : methods.entrySet()) {
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue();
registerHandlerMethod(handler, invocableMethod, mapping);
}
}

getMappingForMethod中定义了如何根据一个RequestMapping方法得到一个存储匹配信息的RequestMappingInfo,这里类的设计的比较巧妙,受限于篇幅,先按住不表。

从http请求到HandlerExecutionChain

回忆下我们说DispatcherServlet初始化的部分,DispatcherServlet最后的步骤是经由onRefresh调用initStrategies,其中initHandlerMappings用于初始化HandleMapping

private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;

if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map 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 {
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.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}

如果是需要找到所有HandleMapping的子类开启,则在所有bean定义中找到所有HandleMapping的实现,并且作为元素放入org.springframework.web.servlet.DispatcherServlet#handlerMappings
否则将从bean中找到名字为handlerMapping作为唯一HandleMapping

一般情况下 需要找到所有HandleMapping的子类开启

调用org.springframework.web.servlet.DispatcherServlet#getHandler获取具体的handler

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}

也就是说通常情况下,getHandler最终会调用org.springframework.web.servlet.handler.RequestMappingHandlerMapping#getHandler
这个方法定义在其父类AbstractHandlerMapping中,这个方法分主要内容是获取通过getHandlerInternal获取handler并且组成HandlerExecutionChain

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
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 = getApplicationContext().getBean(handlerName);
}

HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}

getHandlerInternal是实际获取的handler,定义在AbstractHandlerMethodMapping,获取读取锁后调用lookupHandlerMethod查找handler后释放锁

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}

看看核心匹配方法lookupHandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List matches = new ArrayList();
List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}

if (!matches.isEmpty()) {
Comparator comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
lookupPath + "] : " + matches);
}
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}

返回最佳匹配,修饰的方法就找到了

你可能感兴趣的:(spring源码阅读4,请求如何匹配)