springMVC的请求映射
上一次分析了一下springMVC的大致流程,这次细分一下,对请求映射进行分析。
先从DispatcherServlet中的getHandler()方法分析
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;
}
可以看到这是遍历所有的handlerMappings然后第一个返回不是NULL的handler既是,那handlerMappings包含了那些实现类呢?我们来看看initHandlerMappings这个方法
private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;//detectAllHandlerMappings 默认是trueif (this.detectAllHandlerMappings) {// 从ApplicationContext 里面获取HandlerMapping的实现类Map<String, HandlerMapping> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);//根据Order对HandlerMappings进行排序if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());// We keep HandlerMappings in sorted order.AnnotationAwareOrderComparator.sort(this.handlerMappings);}}else {try {//获取bean name是handlerMapping的HandlerMapping实现类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.}}//若HandlerMappings为空,则设置默认的在DispatcherServlet同级目录下的DispatcherServlet.properties里面设置的org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterif (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);if (logger.isDebugEnabled()) {logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");}}
}
所以若我们想使用自定义的HandlerMapping 可在springMVC的xml配置文件中加上一个HandlerMapping的实现类,若是使用的是 这个进行配置的,其默认会加载RequestMappingHandlerMapping这个实现类(具体在org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser见),而可以在web.xml中将detectAllHandlerMappings 设置为false,然后在springMVC.xml中配置一个name为handlerMapping的实现类。如下
// web.xml
<servlet><servlet-name>myweb</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:mvc.xml</param-value></init-param><!-- 不加载所有的HandlerMapping的实现类的bean --><init-param><param-name>detectAllHandlerMappings</param-name><param-value>false</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>//mvc.xml
<bean name="handlerMapping" class="xxx具体实现类">
继续看handlerMapping是怎么根据request获取到HandlerExecutionChain这个包含拦截器、具体处理的controller的,在AbstractHandlerMapping中以下方法
@Override
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);}//根据定义的Interceptors过滤出需要执行的拦截器,聚合成HandlerExecutionChain这个执行链HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);//CORS跨域请求if (CorsUtils.isCorsRequest(request)) {CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);executionChain = getCorsHandlerExecutionChain(request, executionChain, config);}return executionChain;
}
看getHandlerInternal(request) 这个方法,这个方法是个抽象方法,所以其实现在它的子类中,可以看出这个是一个模板模式,其在抽象类中定义出方法的骨架,而后某些方法交给子类实现,这个方法有两个实现类,AbstractHandlerMethodMapping,AbstractUrlHandlerMapping 这两个具体实现类,我们先看AbstractHandlerMethodMapping 这个类实现的
/*** Look up a handler method for the given request.*/
@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 {//获取handlerMethodHandlerMethod 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();}
}
从这里可以看出 其最后处理的是一个HandlerMethod类,我们来看看这个类的结构
public class HandlerMethod {private final Object bean;private final BeanFactory beanFactory;private final Class<?> beanType;private final Method method;private final Method bridgedMethod;private final MethodParameter[] parameters;private final HandlerMethod resolvedFromHandlerMethod;
}
可以看出其是一个聚合的类,里面包含了bean,method,故而其可以一个方法对应一个请求。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {List<Match> matches = new ArrayList<Match>();//根据请求路径匹配List<T> 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<Match> 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 + "}");}}返回handlerMethodhandleMatch(bestMatch.mapping, lookupPath, request);return bestMatch.handlerMethod;}else {return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);}
}
AbstractHandlerMethodMapping 这个抽象类实现了InitializingBean接口,所以我们可以看看afterPropertiesSet()方法,其在实例化进行的初始化操作,initHandlerMethods()方法,可以看出其在初始化时,就初始化了HandlerMethod这个。
protected void initHandlerMethods() {// 获取所有的BeanString[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :getApplicationContext().getBeanNamesForType(Object.class));for (String name : beanNames) {//判断这个bean是否包含Controller,和RequestMapping这两个注解if (!name.startsWith(SCOPED_TARGET_NAME_PREFIX) && isHandler(getApplicationContext().getType(name))) {//将其注册到MappingRegistrydetectHandlerMethods(name);}}//空方法handlerMethodsInitialized(getHandlerMethods());
}