前言
使用spring mvc的时候,我们可以通过继承Controller或者注解的方式进行配置,那如何正确的找到真正处理的对象呢,这也就是本章我们要了解的内容 - HandlerMapping.
此图来源: https://www.cnblogs.com/fangjian0423/p/springMVC-directory-summary.html
源码分析
HandlerMapping在spring mvc中所处的位置
正如我们所知道的,DispatcherServlet是spring mvc的入口方法,我们看一下源码:
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
//其他代码忽略
}
通过以上代码,可以看出来,当项目启动时,会对初始化很多策略,其中initHandlerMappings(context)是对于HandlerMapping的初始化。
在看HandlerMapping在DispatcherServlet的使用位置:
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//其他代码忽略
try {
doDispatch(request, response);
}
finally {
//其他代码忽略
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//去掉了try catch以及一些验证的代码
//其他代码忽略
mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
}
}
上述代码中,mappedHandler = getHandler(processedRequest);是我们关注的,这个代码通过请求(HttpServeltRequest)找到对应的HandlerMapping,从而通过HandlerMapping拿到处理器Handler。
后边的逻辑为,根据Handler找到不同的适配器处理器(HandlerAdapter),处理后进行视图解析最后返回给用户。
HandlerMapping初始化
HandlerMapping
public interface HandlerMapping {
//常量忽略
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
通过HandlerMapping可以获取具体的HandlerExecutionChain(一个Handler的包装类型)
public class HandlerExecutionChain {
private final Object handler;
private HandlerInterceptor[] interceptors;
private List<HandlerInterceptor> interceptorList;
private int interceptorIndex = -1;
public HandlerExecutionChain(Object handler) {
this(handler, (HandlerInterceptor[]) null);
}
public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {
if (handler instanceof HandlerExecutionChain) {
HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
this.handler = originalChain.getHandler();
this.interceptorList = new ArrayList<HandlerInterceptor>();
CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
}
else {
this.handler = handler;
this.interceptors = interceptors;
}
}
public Object getHandler() {
return this.handler;
}
//其他代码忽略
其中Handler为真正的处理器 (比如,如果你继承了Controller,那么一个Controller就是一个Handler,如果你使用了注解,那么一个方法就是一个Handler)。
那么其他的interceptors或者interceptorList则是Spring mvc的拦截器,这些拦截器可以在Handler进行处理业务逻辑的前后,进行前置处理或者后置处理。
所以HandlerExecutionChain是一个带有拦截器的Handler包装类。
initHandlerMappings
public class DispatcherServlet extends FrameworkServlet {
private boolean detectAllHandlerMappings = true;//是否查询所有的HandlerMappings,如果此配置为false,那么必须找到名称为handlerMapping的HandlerMapping.
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// 在上下文中查找所有的HandlerMapping
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// 对所有的HandlerMapping进行排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
//忽略
}
//经过前两步,都没有HandlerMappings 那么启动一个默认的策略。
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
//忽略
}
}
}
也就是说我们可以注册HandlerMapping,也可以使用默认策略,这步理论上讲,应该是不会报错的。
当我们启动spring boot的时候,我们看到如下:
经过排序后,整个HandlerMapping的顺序如下:
- = {SimpleUrlHandlerMapping@7073}
- = {RequestMappingHandlerMapping@7063}
- = {BeanNameUrlHandlerMapping@7067}
- = {SimpleUrlHandlerMapping@7069}
- = {WebMvcConfigurationSupport$EmptyHandlerMapping@7065}
- = {WebMvcConfigurationSupport$EmptyHandlerMapping@7071}
- = {WebMvcAutoConfiguration$WelcomePageHandlerMapping@7075}
使用案例
@Bean
public SimpleUrlHandlerMapping mySimpleUrlHandlerMapping(){
SimpleUrlHandlerMapping simpleUrlHandlerMapping = new SimpleUrlHandlerMapping();
simpleUrlHandlerMapping.setUrlMap(ImmutableMap.of("/lrwin",new TestController()));
simpleUrlHandlerMapping.setOrder(Integer.MIN_VALUE);
return simpleUrlHandlerMapping;
}
public class TestController extends AbstractController{
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView();
System.out.println("aaa");
return modelAndView;
}
}
当访问http://localhost:8080/lrwin的时候,可以看到控制台输出了aaa.
SimpleUrlHandlerMapping的主要作用是可以给Controller取个访问路径别名,然后进行访问,可以找到相关联的Controller。
@Component("/lrwinx")
public class TestController extends AbstractController{
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView();
System.out.println("aaa");
return modelAndView;
}
}
当访问http://localhost:8080/lrwinx的时候,同样可以看到aaa
上边使用的HandlerMapping为BeanNameUrlHandlerMapping,它可以通过bean的名称来查询响应的Controller.
上边所述的Controller都是处理器(Handler).
还记得HandlerExecutionChain是Handler的一种包装类型吧。 HandlerMapping接口定义了返回这样的类型。
除了上述的两个实例以外,还有一种是注解的方式,注解方式中的每一个带有@RequestMapping的方法都是一个Handler. 这个复杂度比较高,我们最后再说。
类图层次
AbstractHandlerMapping
看代码:
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
@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);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
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;
}
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
}
子类只需要告诉谁是Handler就可以,当然Handler可以为处理器,也可以是处理器的封装类型HandlerExecutionChain。
AbstractUrlHandlerMapping
因为此方法是根据URL查询Handler的类,所以它的重点方法是lookupHandler.
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// handlerMap存储了了urlPath对应的Handler--是一个Map类型。此handlerMap在本类方法registerHandler中进行注册的。
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// 如果获取到的Handler是一个String类型,那么就要考虑它有可能是一个beanName
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
//构建一个带有基础信息的HandlerExecutionChain(Handler的封装类型)
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// 如果不是直接匹配的,那么是不是有可能是AntPathMatcher匹配的,当然,AntPathMatcher这种匹配,有可能会有多个,所以使用了List进行了临时存储
List<String> matchingPatterns = new ArrayList<String>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern +"/");
}
}
}
//经过AntPathMatcher排序方式,获取一个最佳的匹配路径(也就是匹配路径List排序后的第一个)
String bestPatternMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
Collections.sort(matchingPatterns, patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
bestPatternMatch = matchingPatterns.get(0);
}
//再通过最佳路径去halerMap中去查找对应的Handler
if (bestPatternMatch != null) {
handler = this.handlerMap.get(bestPatternMatch);
if (handler == null) {
Assert.isTrue(bestPatternMatch.endsWith("/"));
handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1));
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isDebugEnabled()) {
logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
为了更深入的了解,我们关注一下排序算法:
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
Collections.sort(matchingPatterns, patternComparator);
getPathMatcher()是一个AntPathMatcher. 看一下getPatternComparator这个方法:
@Override
public Comparator<String> getPatternComparator(String path) {
return new AntPatternComparator(path);
}
AntPatternComparator 会对符合ant表达式的url进行排序。
排序顺序:
- if it's null or a capture all pattern (i.e. it is equal to "/**")
- if the other pattern is an actual match
- if it's a catch-all pattern (i.e. it ends with "**"
- if it's got more "*" than the other pattern
- if it's got more "{foo}" than the other pattern
- if it's shorter than the other pattern
再来看一下,如何注册Handler到handlerMap中的。
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
//一群验证和闲杂逻辑
this.handlerMap.put(urlPath, resolvedHandler);
}
这样的话,我们得知,AbstractUrlHandlerMapping其实已经完成了注册Handler,和通过URL查找可用Handler的逻辑,它的子类只需要在合适的时机,调用注册方法就可以。我们来看他的子类:SimpleUrlHandlerMapping
SimpleUrlHandlerMapping
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
private final Map<String, Object> urlMap = new LinkedHashMap<String, Object>();
//可以使用 properties配置,然后添加到urlMap中
public void setMappings(Properties mappings) {
CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
}
//设置urlMap中
public void setUrlMap(Map<String, ?> urlMap) {
this.urlMap.putAll(urlMap);
}
public Map<String, ?> getUrlMap() {
return this.urlMap;
}
//初始化时,注册所有的Handler
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}
//调用父类注册方法registerHandler的逻辑
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
String url = entry.getKey();
Object handler = entry.getValue();
if (!url.startsWith("/")) {
url = "/" + url;
}
if (handler instanceof String) {
handler = ((String) handler).trim();
}
registerHandler(url, handler);
}
}
}
}
小结
- HandlerMapping
定义了需要返回handler的包装类型HandlerExecutionChain的接口 : getHandler
2.AbstractHandlerMapping
封装了getHandler,并且封装了拦截器。子类需要提供一下Handler
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
3.AbstractUrlHandlerMapping
实现了getHandlerInternal,提供了如何通过URL查找Handler的方法,核心逻辑方法:
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
提供了注册方法,子类需要调用注册方法:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException
4.SimpleUrlHandlerMapping
仅仅是解析Properties或者Map数据,然后在初始化的时候调用父类的registerHandler进行注册。
牛逼的注解HanlderMapping
AbstractHandlerMethodMapping
这是一个以方法为Handler的抽象类:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception
看一下HandlerMethod:
public class HandlerMethod {
private final Object bean;//类对象
private final Class<?> beanType;//对象的Class
private final Method method;//具体方法
private final Method bridgedMethod;//桥接方法(桥接方法以后再将)
private final MethodParameter[] parameters;//方法参数
private final HandlerMethod resolvedFromHandlerMethod;//其他Handler的方法
//...
}
这个HandlerMethod就是一个方法的描述类。
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//...
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//...
}
核心方法lookupHandlerMethod:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
//找到根据url找到所有对应的Match(这个对象是HandlerMethod的包装类型,它包含HandlerMethod和他的mapping)
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);
}
//从Match集合中,找到最佳的一个
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 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
对于AbstractHandlerMethodMapping,有几个概念:
mapping:处理方法的mapping(后边会讲到,是一个RequestMappingInfo)
handler:处理器(只一个类)
method:执行方法(类的执行方法)
HandlerMethod: 可以理解成一个handler和method的一个封装。
Match: mapping和HandlerMethod的一个封装。
1.根据 url找到所有的mapping:
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
2.封装成找到对应的Match:
T match = getMatchingMapping(mapping, request);
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
这个要由子类实现
3.加入集合,然后找到最佳Match
RequestMappingInfoHandlerMapping
RequestMappingInfo:
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
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;
//....
}
这个RequestMappingInfo类,其实和@RequestMapping里的属性是一一对应的。