前言
使用spring mvc的時(shí)候,我們可以通過繼承Controller或者注解的方式進(jìn)行配置俏竞,那如何正確的找到真正處理的對象呢,這也就是本章我們要了解的內(nèi)容 - 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);
}
//其他代碼忽略
}
通過以上代碼刀诬,可以看出來,當(dāng)項(xiàng)目啟動(dòng)時(shí)邪财,會(huì)對初始化很多策略陕壹,其中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以及一些驗(yàn)證的代碼
//其他代碼忽略
mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
}
}
上述代碼中树埠,mappedHandler = getHandler(processedRequest);是我們關(guān)注的糠馆,這個(gè)代碼通過請求(HttpServeltRequest)找到對應(yīng)的HandlerMapping,從而通過HandlerMapping拿到處理器Handler。
后邊的邏輯為弥奸,根據(jù)Handler找到不同的適配器處理器(HandlerAdapter)榨惠,處理后進(jìn)行視圖解析最后返回給用戶奋早。
HandlerMapping初始化
HandlerMapping
public interface HandlerMapping {
//常量忽略
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
通過HandlerMapping可以獲取具體的HandlerExecutionChain(一個(gè)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,那么一個(gè)Controller就是一個(gè)Handler,如果你使用了注解,那么一個(gè)方法就是一個(gè)Handler)耽装。
那么其他的interceptors或者interceptorList則是Spring mvc的攔截器愤炸,這些攔截器可以在Handler進(jìn)行處理業(yè)務(wù)邏輯的前后,進(jìn)行前置處理或者后置處理掉奄。
所以HandlerExecutionChain是一個(gè)帶有攔截器的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進(jìn)行排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
//忽略
}
//經(jīng)過前兩步凤薛,都沒有HandlerMappings 那么啟動(dòng)一個(gè)默認(rèn)的策略。
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
//忽略
}
}
}
也就是說我們可以注冊HandlerMapping,也可以使用默認(rèn)策略诞仓,這步理論上講缤苫,應(yīng)該是不會(huì)報(bào)錯(cuò)的。
當(dāng)我們啟動(dòng)spring boot的時(shí)候墅拭,我們看到如下:
經(jīng)過排序后活玲,整個(gè)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;
}
}
當(dāng)訪問http://localhost:8080/lrwin的時(shí)候,可以看到控制臺(tái)輸出了aaa.
SimpleUrlHandlerMapping的主要作用是可以給Controller取個(gè)訪問路徑別名谍婉,然后進(jìn)行訪問舒憾,可以找到相關(guān)聯(lián)的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;
}
}
當(dāng)訪問http://localhost:8080/lrwinx的時(shí)候穗熬,同樣可以看到aaa
上邊使用的HandlerMapping為BeanNameUrlHandlerMapping镀迂,它可以通過bean的名稱來查詢響應(yīng)的Controller.
上邊所述的Controller都是處理器(Handler).
還記得HandlerExecutionChain是Handler的一種包裝類型吧。 HandlerMapping接口定義了返回這樣的類型唤蔗。
除了上述的兩個(gè)實(shí)例以外探遵,還有一種是注解的方式,注解方式中的每一個(gè)帶有@RequestMapping的方法都是一個(gè)Handler. 這個(gè)復(fù)雜度比較高措译,我們最后再說别凤。
類圖層次
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就可以,當(dāng)然Handler可以為處理器领虹,也可以是處理器的封裝類型HandlerExecutionChain规哪。
AbstractUrlHandlerMapping
因?yàn)榇朔椒ㄊ歉鶕?jù)URL查詢Handler的類,所以它的重點(diǎn)方法是lookupHandler.
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// handlerMap存儲(chǔ)了了urlPath對應(yīng)的Handler--是一個(gè)Map類型塌衰。此handlerMap在本類方法registerHandler中進(jìn)行注冊的诉稍。
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// 如果獲取到的Handler是一個(gè)String類型,那么就要考慮它有可能是一個(gè)beanName
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
//構(gòu)建一個(gè)帶有基礎(chǔ)信息的HandlerExecutionChain(Handler的封裝類型)
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// 如果不是直接匹配的最疆,那么是不是有可能是AntPathMatcher匹配的杯巨,當(dāng)然,AntPathMatcher這種匹配努酸,有可能會(huì)有多個(gè)服爷,所以使用了List進(jìn)行了臨時(shí)存儲(chǔ)
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 +"/");
}
}
}
//經(jīng)過AntPathMatcher排序方式,獲取一個(gè)最佳的匹配路徑(也就是匹配路徑List排序后的第一個(gè))
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中去查找對應(yīng)的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;
}
為了更深入的了解获诈,我們關(guān)注一下排序算法:
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
Collections.sort(matchingPatterns, patternComparator);
getPathMatcher()是一個(gè)AntPathMatcher. 看一下getPatternComparator這個(gè)方法:
@Override
public Comparator<String> getPatternComparator(String path) {
return new AntPatternComparator(path);
}
AntPatternComparator 會(huì)對符合ant表達(dá)式的url進(jìn)行排序仍源。
排序順序:
- 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 {
//一群驗(yàn)證和閑雜邏輯
this.handlerMap.put(urlPath, resolvedHandler);
}
這樣的話笼踩,我們得知,AbstractUrlHandlerMapping其實(shí)已經(jīng)完成了注冊Handler,和通過URL查找可用Handler的邏輯亡嫌,它的子類只需要在合適的時(shí)機(jī)嚎于,調(diào)用注冊方法就可以掘而。我們來看他的子類: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);
}
//設(shè)置urlMap中
public void setUrlMap(Map<String, ?> urlMap) {
this.urlMap.putAll(urlMap);
}
public Map<String, ?> getUrlMap() {
return this.urlMap;
}
//初始化時(shí)于购,注冊所有的Handler
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}
//調(diào)用父類注冊方法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);
}
}
}
}
小結(jié)
- HandlerMapping
定義了需要返回handler的包裝類型HandlerExecutionChain的接口 : getHandler
2.AbstractHandlerMapping
封裝了getHandler,并且封裝了攔截器袍睡。子類需要提供一下Handler
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
3.AbstractUrlHandlerMapping
實(shí)現(xiàn)了getHandlerInternal,提供了如何通過URL查找Handler的方法肋僧,核心邏輯方法:
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
提供了注冊方法,子類需要調(diào)用注冊方法:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException
4.SimpleUrlHandlerMapping
僅僅是解析Properties或者M(jìn)ap數(shù)據(jù)女蜈,然后在初始化的時(shí)候調(diào)用父類的registerHandler進(jìn)行注冊。
牛逼的注解HanlderMapping
AbstractHandlerMethodMapping
這是一個(gè)以方法為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;//方法參數(shù)
private final HandlerMethod resolvedFromHandlerMethod;//其他Handler的方法
//...
}
這個(gè)HandlerMethod就是一個(gè)方法的描述類色瘩。
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//...
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//...
}
核心方法lookupHandlerMethod:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
//找到根據(jù)url找到所有對應(yīng)的Match(這個(gè)對象是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集合中,找到最佳的一個(gè)
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,有幾個(gè)概念:
mapping:處理方法的mapping(后邊會(huì)講到居兆,是一個(gè)RequestMappingInfo)
handler:處理器(只一個(gè)類)
method:執(zhí)行方法(類的執(zhí)行方法)
HandlerMethod: 可以理解成一個(gè)handler和method的一個(gè)封裝覆山。
Match: mapping和HandlerMethod的一個(gè)封裝。
1.根據(jù) url找到所有的mapping:
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
2.封裝成找到對應(yīng)的Match:
T match = getMatchingMapping(mapping, request);
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
這個(gè)要由子類實(shí)現(xiàn)
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;
//....
}
這個(gè)RequestMappingInfo類泥栖,其實(shí)和@RequestMapping里的屬性是一一對應(yīng)的簇宽。