SpringMVC源碼之HandlerMapping處理器映射器解析

1. 什么是HandlerMapping
在Spring MVC中,HandlerMapping(處理器映射器)用于確定請(qǐng)求處理器對(duì)象。請(qǐng)求處理器可以是任何對(duì)象,只要它們使用了@Controller注解或注解@RequestMapping。
HandlerMapping負(fù)責(zé)將請(qǐng)求(url)映射到適當(dāng)?shù)奶幚砥鲗?duì)象(Controller)。
注:Handler即綁定了注解@RequestMapping或@Controller的類
HandlerMapping接口定義了一個(gè)方法:
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}getHandler方法用于查找處理器對(duì)象并返回處理程序的執(zhí)行鏈,HandlerExecutionChain包含了處理器對(duì)象和一系列攔截器,這些攔截器可以在處理程序執(zhí)行之前和之后執(zhí)行一些操作。
HandlerMapping接口繼承結(jié)構(gòu)體系

2. HandlerMapping
2.1 HandlerMapping初始化
Http請(qǐng)求由servlet容器分發(fā)到DispatcherServlet,在其中會(huì)進(jìn)行九大核心組件的初始化
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}這里我們主要關(guān)注 initHandlerMappings
public class DispatcherServlet extends FrameworkServlet {
private void initHandlerMappings(ApplicationContext context) {...}
}
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//private boolean detectAllHandlerMappings = true;
//該值默認(rèn)為 true,查詢所有的handlerMapping
// 如果設(shè)置為 false , Spring MVC就只會(huì)查找名為“handlerMapping”的bean,并作為當(dāng)前系統(tǒng)的唯一的HandlerMapping
if (this.detectAllHandlerMappings) {
//在ApplicationContext中查找所有handler映射,包括父類上下文。
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
//存在Handler
if (!matchingBeans.isEmpty()) {
//將獲取的 HandlerMapping 轉(zhuǎn)換成集合并排序
this.handlerMappings = new ArrayList(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
//沒(méi)有找到
try {
//查找名為“handlerMapping”的bean,并作為當(dāng)前系統(tǒng)的唯一的HandlerMapping,確保至少有一個(gè)HandlerMapping
HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException var4) {
}
}
this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
}
Iterator var6 = this.handlerMappings.iterator();
//迭代HandlerMapping
while(var6.hasNext()) {
HandlerMapping mapping = (HandlerMapping)var6.next();
//判斷HandlerMapping實(shí)例是否啟用了解析的PathPatterns
//如果啟用則DispatcherServlet會(huì)自動(dòng)解析RequestPath,以便在HandlerMappings,HandlerInterceptors和其他組件中訪問(wèn)
if (mapping.usesPathPatterns()) {
this.parseRequestPath = true;
break;
}
}
}
Spring MVC框架中有多個(gè)HandlerMapping實(shí)現(xiàn),每個(gè)實(shí)現(xiàn)都可以使用不同的策略來(lái)確定請(qǐng)求處理器對(duì)象。
最常見(jiàn)的HandlerMapping實(shí)現(xiàn)是RequestMappingHandlerMapping和SimpleUrlHandlerMapping。
2.2 getHandler解析
getHandler方法根據(jù)請(qǐng)求找到對(duì)應(yīng)的處理器對(duì)象, 在 DispatcherServlet 類中, doDispatch() 方法調(diào)用 getHandler() 方法得到 HandlerExecutionChain 對(duì)象。
注: HandlerExecutionChain 是一個(gè)類,用于封裝處理器對(duì)象和攔截器列表。它是 HandlerMapping 的 getHandler 方法的返回值。
HandlerExecutionChain 的作用是在請(qǐng)求處理流程中,提供一種責(zé)任鏈模式(看圖),讓攔截器可以在處理器執(zhí)行前后進(jìn)行一些額外的操作,例如驗(yàn)證、日志、事務(wù)等。
拿到這個(gè)對(duì)象后,DispatcherServlet會(huì)調(diào)用它的applyPreHandle方法,執(zhí)行所有攔截器的preHandle方法。如果都返回true,則繼續(xù)調(diào)用處理器的方法;否則,返回響應(yīng)或者跳轉(zhuǎn)到其他頁(yè)面。
處理器執(zhí)行完畢后,DispatcherServlet會(huì)調(diào)用它的applyPostHandle方法,執(zhí)行所有攔截器的postHandle方法。這些方法可以對(duì)模型和視圖進(jìn)行修改或增強(qiáng)。
最后,在渲染視圖之后,DispatcherServlet會(huì)調(diào)用它的triggerAfterCompletion方法,執(zhí)行所有攔截器的afterCompletion方法。這些方法可以進(jìn)行一些清理工作或異常處理

源碼解析:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
mappedHandler = this.getHandler(processedRequest);
}//返回一個(gè)HandlerExecutionChain對(duì)象
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
Iterator var2 = this.handlerMappings.iterator();
//遍歷所有的初始化的HandlerMapping列表
while(var2.hasNext()) {
HandlerMapping mapping = (HandlerMapping)var2.next();
//獲取一個(gè)HandlerExecutionChain對(duì)象,它包含了處理請(qǐng)求的handler對(duì)象和任何配置的攔截器
//接口方法
HandlerExecutionChain handler = mapping.getHandler(request);
//匹配到就進(jìn)行返回
if (handler != null) {
return handler;
}
}
}
return null;
}實(shí)現(xiàn)類為 AbstractHandlerMapping
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware {
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {……};
}
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 根據(jù)請(qǐng)求從緩存中查找handler,如果沒(méi)有則調(diào)用子類實(shí)現(xiàn)的getHandlerInternal()方法
該方法在本類中有定義,是一個(gè)protected型的抽象方法
Object handler = this.getHandlerInternal(request);
// 沒(méi)有找到,則會(huì)使用getDefaultHandler()方法獲取默認(rèn)的處理器
if (handler == null) {
handler = this.getDefaultHandler();
}
if (handler == null) {
return null;
} else {
//檢查handler對(duì)象是否是一個(gè)字符串類型。如果是的話,就從應(yīng)用上下文中根據(jù)字符串名稱獲取對(duì)應(yīng)的bean作為handler對(duì)象
if (handler instanceof String) {
String handlerName = (String)handler;
handler = this.obtainApplicationContext().getBean(handlerName);
}
//如果請(qǐng)求對(duì)象中沒(méi)有緩存的路徑信息,就調(diào)用initLookupPath方法來(lái)解析請(qǐng)求的路徑信息,并且保存在請(qǐng)求對(duì)象的一個(gè)屬性中
if (!ServletRequestPathUtils.hasCachedPath(request)) {
this.initLookupPath(request);
}
//根據(jù)handler對(duì)象和請(qǐng)求創(chuàng)建一個(gè)HandlerExecutionChain對(duì)象,包含處理器對(duì)象和攔截器列表
HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
return executionChain;
}
}到此為止就獲得了executionChain對(duì)象實(shí)例了,也就是第一張圖的第4步。
3. getHandlerInternal()子類實(shí)現(xiàn)
getHandlerInternal() 方法在 AbstractUrlHandlerMapping 類和 AbstractHandlerMethodMapping 類中均有實(shí)現(xiàn), 均繼承于AbstractHandlerMapping類
3.1 AbstractUrlHandlerMapping與AbstractHandlerMethodMapping的區(qū)別
AbstractUrlHandlerMapping 是基于URL映射的 HandlerMapping ,它支持字面匹配和模式匹配,如"/test/*“,”/test/"等, 它返回的Handler是一個(gè)類級(jí)別的對(duì)象**,例如一個(gè)Controller類或一個(gè)Bean對(duì)象
AbstractHandlerMethodMapping 是基于方法級(jí)別的 HandlerMapping ,它支持使用@RequestMapping注解來(lái)指定請(qǐng)求路徑和方法。它返回的Handler是一個(gè)方法級(jí)別的對(duì)象,例如一個(gè)Controller類中的某個(gè)方法或一個(gè)Bean對(duì)象中的某個(gè)方法
示例:
AbstractUrlHandlerMapping 的一個(gè)子類是 SimpleUrlHandlerMapping ,它可以在XML配置文件中定義URL和Handler的映射關(guān)系
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/test.do">testController</prop>
</props>
</property>
</bean>
<bean id="testController" class="com.example.TestController"/>AbstractHandlerMethodMapping 的一個(gè)子類是 RequestMappingHandlerMapping ,它可以在Java類中使用@RequestMapping注解來(lái)定義URL和方法的映射關(guān)系
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping("/do")
public String doSomething() {
// ...
}
}這兩種方式都可以實(shí)現(xiàn)URL和Handler的綁定,但是后者更靈活和簡(jiǎn)潔
3.2 AbstractUrlHandlerMapping
//根據(jù)用戶請(qǐng)求信息中的URL查找handler
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//初始化請(qǐng)求的查找路徑,也就是去掉上下文路徑和后綴名的URL路徑
String lookupPath = this.initLookupPath(request);
Object handler;
//是否使用路徑模式來(lái)選擇不同的查找handler的方法
//
if (this.usesPathPatterns()) {
//獲取請(qǐng)求的RequestPath對(duì)象,它封裝了請(qǐng)求的URL路徑、上下文路徑、后綴名等信息。
RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
//根據(jù)RequestPath對(duì)象、查找路徑和請(qǐng)求對(duì)象,調(diào)用lookupHandler()方法來(lái)查找匹配的handler
handler = this.lookupHandler(path, lookupPath, request);
} else {
handler = this.lookupHandler(lookupPath, request);
}
//沒(méi)有找到匹配的handler時(shí),嘗試獲取根handler或者默認(rèn)handler,并進(jìn)行驗(yàn)證和包裝。
if (handler == null) {
Object rawHandler = null;
if (StringUtils.matchesCharacter(lookupPath, '/')) {
rawHandler = this.getRootHandler();
}
if (rawHandler == null) {
rawHandler = this.getDefaultHandler();
}
if (rawHandler != null) {
if (rawHandler instanceof String) {
String handlerName = (String)rawHandler;
rawHandler = this.obtainApplicationContext().getBean(handlerName);
}
this.validateHandler(rawHandler, request);
handler = this.buildPathExposingHandler(rawHandler, lookupPath, lookupPath, (Map)null);
}
}
//返回獲取到的handler對(duì)象
return handler;
}
3.3 AbstractHandlerMethodMapping
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//初始化請(qǐng)求的查找路徑,也就是去掉上下文路徑和后綴名的URL路徑
String lookupPath = this.initLookupPath(request);
//獲取映射注冊(cè)表的讀鎖,以保證線程安全
this.mappingRegistry.acquireReadLock();
HandlerMethod var4;
//獲取讀鎖后,嘗試查找匹配的handler方法,并在最后釋放讀鎖
try {
//根據(jù)查找路徑和請(qǐng)求對(duì)象,在映射注冊(cè)表中查找匹配的handler方法
HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
//判斷是否找到了handler方法
//如果找到了,則調(diào)用createWithResolvedBean()方法來(lái)創(chuàng)建一個(gè)新的HandlerMethod實(shí)例,并解析其關(guān)聯(lián)的bean對(duì)象;
//如果沒(méi)有找到,則返回null。
var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
} finally {
this.mappingRegistry.releaseReadLock();
}
//返回獲取到的handler對(duì)象
return var4;
}下面簡(jiǎn)單介紹一下AbstractUrlHandlerMapping與AbstractHandlerMethodMapping的常用子類實(shí)現(xiàn)
4. RequestMappingHandlerMapping
RequestMappingHandlerMapping 是Spring MVC中最常用的HandlerMapping實(shí)現(xiàn)之一。
它使用 @RequestMapping 注解來(lái)確定請(qǐng)求處理器對(duì)象。
下面是一個(gè)簡(jiǎn)單的控制器示例:
@Controller
@RequestMapping("/user")
public class UserController {
@GetMapping("/{id}")
public String getUser(@PathVariable int id, Model model) {
User user = userRepository.findById(id);
model.addAttribute("user", user);
return "user";
}
}在這個(gè)示例中, UserController 類被@Controller注解標(biāo)記為一個(gè)控制器,并且所有請(qǐng)求路徑以/user開(kāi)頭。
@GetMapping注解用于處理HTTP GET請(qǐng)求,{id}是一個(gè)路徑變量,它將匹配任何非空字符串,并將其解析為一個(gè)整數(shù)。
getUser方法將通過(guò)id獲取用戶對(duì)象并將其添加到模型中,最后返回一個(gè)視圖名為"user"的字符串。
RequestMappingHandlerMapping 會(huì)掃描所有@Controller注解標(biāo)記的類,并將所有帶有@RequestMapping注解的方法添加到HandlerMapping中。
在請(qǐng)求到達(dá)服務(wù)器時(shí), RequestMappingHandlerMapping 將匹配請(qǐng)求路徑和請(qǐng)求方法,并將請(qǐng)求映射到適當(dāng)?shù)奶幚砥鞣椒ā?/p>
4.1 加載過(guò)程
- RequestMappingHandlerMapping 實(shí)現(xiàn)了接口 InitializingBean ,在bean加載完成后會(huì)自動(dòng)調(diào)用 afterPropertiesSet 方法,在此方法中調(diào)用了initHandlerMethods()來(lái)實(shí)現(xiàn)初始化
- initHandlerMethods() 會(huì)遍歷所有bean,如果bean實(shí)現(xiàn)帶有注解@Controller或者@RequestMapping 則進(jìn)一步調(diào)用 detectHandlerMethods 處理,處理邏輯大致就是根據(jù)@RequestMapping配置的信息,把解析結(jié)果封裝成 RequestMappingInfo 對(duì)象,也就是說(shuō) RequestMappingInfo 對(duì)象是用來(lái)裝載方法的匹配相關(guān)信息,每個(gè)匹配的方法都會(huì)對(duì)應(yīng)一個(gè) RequestMappingInfo 對(duì)象,然后注冊(cè)到 MappingRegistry 中
具體流程:
- 當(dāng)Spring容器啟動(dòng)時(shí),它會(huì)掃描所有帶有@Controller或@RestController注解的類,并將它們作為處理器對(duì)象注冊(cè)到RequestMappingHandlerMapping中。
- RequestMappingHandlerMapping會(huì)遍歷每個(gè)處理器對(duì)象中的所有方法,并使用getMappingForMethod()方法來(lái)獲取每個(gè)方法上定義或繼承的@RequestMapping注解,然后將這些注解轉(zhuǎn)換為RequestMappingInfo對(duì)象,包含了請(qǐng)求路徑、請(qǐng)求方法、請(qǐng)求參數(shù)、請(qǐng)求頭等信息。
- RequestMappingHandlerMapping會(huì)將每個(gè)RequestMappingInfo對(duì)象和對(duì)應(yīng)的處理器方法封裝成一個(gè)HandlerMethod對(duì)象,并將這些對(duì)象存儲(chǔ)在一個(gè)Map結(jié)構(gòu)中,以便于后續(xù)查找。
- 當(dāng)一個(gè)HTTP請(qǐng)求到達(dá)DispatcherServlet時(shí),它會(huì)調(diào)用RequestMappingHandlerMapping的getHandler()方法來(lái)根據(jù)請(qǐng)求URI找到匹配的處理器方法。
- RequestMappingHandlerMapping會(huì)遍歷Map中的所有鍵值對(duì)(即每個(gè)RequestMappingInfo和HandlerMethod),并使用PathMatcher或PathPatternParser來(lái)判斷請(qǐng)求URI是否與RequestMappingInfo中定義的路徑匹配。如果匹配,就返回對(duì)應(yīng)的HandlerMethod;如果不匹配,就繼續(xù)查找下一個(gè)鍵值對(duì)。
- 如果找到了匹配的HandlerMethod,DispatcherServlet就會(huì)根據(jù)其類型選擇合適的HandlerAdapter來(lái)執(zhí)行它,并返回響應(yīng)結(jié)果;如果沒(méi)有找到匹配的HandlerMethod,DispatcherServlet就會(huì)拋出異?;蛘叻祷?04錯(cuò)誤頁(yè)面。
步驟1的注冊(cè)過(guò)程
public void afterPropertiesSet() {
//……
super.afterPropertiesSet();
}//父類AbstractHandlerMethodMapping 的方法
public void afterPropertiesSet() {
this.initHandlerMethods();
}
//掃描容器中的bean,檢測(cè)和注冊(cè)handler方法
protected void initHandlerMethods() {
//獲取候選的bean名稱數(shù)組
String[] var1 = this.getCandidateBeanNames();
int var2 = var1.length;
//遍歷
for(int var3 = 0; var3 < var2; ++var3) {
String beanName = var1[var3];
//用于過(guò)濾掉以"scopedTarget."開(kāi)頭的bean名稱的。
//這些bean名稱是由Spring創(chuàng)建的代理對(duì)象,用于支持不同作用域的bean
//例如session或request。這些代理對(duì)象不是真正的handler,所以要排除掉。
if (!beanName.startsWith("scopedTarget.")) {
//處理候選的bean,判斷候選的bean是否是一個(gè)handler,也就是是否有@Controller注解或者@RequestMapping注解
//如果是的話,就調(diào)用detectHandlerMethods方法,用于檢測(cè)和注冊(cè)handler方法
this.processCandidateBean(beanName);
}
}
//初始化handler方法, 對(duì)所有handler方法進(jìn)行排序和日志輸出
this.handlerMethodsInitialized(this.getHandlerMethods());
}
//檢測(cè)和注冊(cè)handler方法
protected void detectHandlerMethods(Object handler) {
//存儲(chǔ)handler對(duì)象的類信息
//如果handler是一個(gè)字符串,那么就使用應(yīng)用程序上下文來(lái)獲取對(duì)應(yīng)的類類型;否則就直接使用handler.getClass()方法來(lái)獲取類類型。
Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
if (handlerType != null) {
//存儲(chǔ)handlerType去除代理和增強(qiáng)后的原始類類型。這是為了避免AOP對(duì)方法檢測(cè)造成干擾。
Class<?> userType = ClassUtils.getUserClass(handlerType);
//存儲(chǔ)userType中所有帶有映射注解(如@RequestMapping)的方法及其對(duì)應(yīng)的映射信息
Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
try {
//獲取每個(gè)方法上定義或繼承的映射信息
return this.getMappingForMethod(method, userType);
} catch (Throwable var4) {
throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
}
});
//遍歷methods中每個(gè)鍵值對(duì)(即每個(gè)方法及其映射信息)
methods.forEach((method, mapping) -> {
//存儲(chǔ)經(jīng)過(guò)AopUtils.selectInvocableMethod()方法處理后可以被調(diào)用(即沒(méi)有被final修飾)
//且與userType匹配(即沒(méi)有被覆蓋) 的原始或橋接(即泛型擦除后生成) 方法
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//注冊(cè)到處理器映射
this.registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}注: RequestMappingInfo 類,主要用來(lái)記錄方法上 @RequestMapping() 注解里面的參數(shù),針對(duì) RequestMappingHandlerMapping 映射器來(lái)使用。
步驟2: RequestMappingHandlerMapping 會(huì)遍歷每個(gè)處理器對(duì)象中的所有方法,并使用 getMappingForMethod() 方法來(lái)獲取每個(gè)方法上定義或繼承的@RequestMapping注解,然后將這些注解轉(zhuǎn)換為 RequestMappingInfo 對(duì)象,包含了請(qǐng)求路徑、請(qǐng)求方法、請(qǐng)求參數(shù)、請(qǐng)求頭等信息。
//根據(jù)處理器類和方法上的@RequestMapping注解來(lái)創(chuàng)建一個(gè)RequestMappingInfo對(duì)象(封裝了請(qǐng)求映射的信息,如請(qǐng)求路徑、請(qǐng)求方法、請(qǐng)求參數(shù)、請(qǐng)求頭等)
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//獲取方法上定義或繼承的@RequestMapping注解,并將其轉(zhuǎn)換為一個(gè)RequestMappingInfo對(duì)象
RequestMappingInfo info = this.createRequestMappingInfo(method);
if (info != null) {
//獲取處理器類上定義或繼承的@RequestMapping注解,并將其轉(zhuǎn)換為另一個(gè)RequestMappingInfo對(duì)象
RequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType);
if (typeInfo != null) {
//合并兩個(gè)RequestMappingInfo對(duì)象
info = typeInfo.combine(info);
}
//獲取處理器類上定義或繼承的@PathPrefix注解,并將其轉(zhuǎn)換為一個(gè)字符串前綴
String prefix = this.getPathPrefix(handlerType);
if (prefix != null) {
//根據(jù)指定的路徑前綴和配置選項(xiàng)來(lái)構(gòu)建一個(gè)新的請(qǐng)求映射信息,并且與原有信息進(jìn)行合并
info = RequestMappingInfo.paths(new String[]{prefix}).options(this.config).build().combine(info);
}
}
return info;
}步驟3: RequestMappingHandlerMapping 會(huì)將每個(gè) RequestMappingInfo 對(duì)象和對(duì)應(yīng)的處理器方法封裝成一個(gè) HandlerMethod 對(duì)象,并將這些對(duì)象存儲(chǔ)在一個(gè)Map結(jié)構(gòu)中,以便于后續(xù)查找。
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
super.registerHandlerMethod(handler, method, mapping);
this.updateConsumesCondition(mapping, method);
}
//AbstractHandlerMethodMapping類中的方法
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
public void register(T mapping, Object handler, Method method) {
try {
// 創(chuàng)建HandlerMethod對(duì)象
HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method);
this.validateMethodMapping(handlerMethod, mapping);
//獲取匹配條件對(duì)應(yīng)的直接路徑,添加到pathLookup中
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
Iterator var6 = directPaths.iterator();
while(var6.hasNext()) {
String path = (String)var6.next();
this.pathLookup.add(path, mapping);
}
//如果有命名策略,獲取handler方法的名稱,添加到nameLookup中
String name = null;
if (AbstractHandlerMethodMapping.this.getNamingStrategy() != null) {
name = AbstractHandlerMethodMapping.this.getNamingStrategy().getName(handlerMethod, mapping);
this.addMappingName(name, handlerMethod);
}
//將匹配條件和MappingRegistration對(duì)象(封裝了handler方法、直接路徑、名稱、跨域配置等信息)添加到registry中
this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration(mapping, handlerMethod, directPaths, name, corsConfig != null));
} finally {
}
}步驟4:當(dāng)一個(gè)HTTP請(qǐng)求到達(dá) DispatcherServlet 時(shí),它會(huì)調(diào)用 RequestMappingHandlerMapping 的 getHandler() 方法來(lái)根據(jù)請(qǐng)求URI找到匹配的處理器方法。(即3.3節(jié)提到的 AbstractHandlerMethodMapping 父類中的 getHandler() 方法)
步驟5: RequestMappingHandlerMapping 會(huì)遍歷Map中的所有鍵值對(duì)(即每個(gè) RequestMappingInfo和HandlerMethod ),并使用 PathMatcher 或 PathPatternParser 來(lái)判斷請(qǐng)求URI是否與 RequestMappingInfo 中定義的路徑匹配。如果匹配,就返回對(duì)應(yīng)的 HandlerMethod ;如果不匹配,就繼續(xù)查找下一個(gè)鍵值對(duì)。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
// 創(chuàng)建一個(gè)空的Match列表,用來(lái)存放匹配的RequestMappingInfo和HandlerMethod對(duì)象
List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();
// 從mappingRegistry中獲取直接路徑匹配(即沒(méi)有通配符或變量)的RequestMappingInfo列表
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
// 調(diào)用addMatchingMappings方法,判斷直接路徑匹配的RequestMappingInfo是否與請(qǐng)求條件匹配
//如果匹配,就添加到matches列表中
this.addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// 如果matches列表為空,說(shuō)明沒(méi)有直接路徑匹配的RequestMappingInfo與請(qǐng)求條件匹配
//那么就從mappingRegistry中獲取所有注冊(cè)過(guò)的RequestMappingInfo,
//并調(diào)用addMatchingMappings方法判斷是否與請(qǐng)求條件匹配
//如果匹配,就添加到matches列表中
this.addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (matches.isEmpty()) {
// 如果matches列表仍然為空,說(shuō)明沒(méi)有任何一個(gè)RequestMappingInfo與請(qǐng)求條件匹配
//那么就調(diào)用handleNoMatch方法處理沒(méi)有匹配結(jié)果的情況,并返回null或拋出異常
return this.handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
} else {
// 如果matches列表不為空,說(shuō)明至少有一個(gè)RequestMappingInfo與請(qǐng)求條件匹配
// 那么就從matches列表中獲取第一個(gè)Match對(duì)象(即最先添加進(jìn)去的),作為最佳匹配結(jié)果
AbstractHandlerMethodMapping<T>.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
if (matches.size() > 1) {
// 如果matches列表中有多個(gè)Match對(duì)象(即有多個(gè)RequestMappingInfo與請(qǐng)求條件匹配)
//那么就需要進(jìn)行排序和篩選
// 首先創(chuàng)建一個(gè)比較器comparator,根據(jù)getMappingComparator方法返回的比較器對(duì)每個(gè)Match對(duì)象進(jìn)行比較(主要比較它們包含的RequestMappingInfo)
Comparator<AbstractHandlerMethodMapping<T>.Match> comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request));
// 然后對(duì)matches列表進(jìn)行排序,根據(jù)comparator比較器確定每個(gè)Match對(duì)象之間的優(yōu)先級(jí)和順序
matches.sort(comparator);
// 再次從matches列表中獲取第一個(gè)Match對(duì)象(即排序后最優(yōu)先級(jí)最高、順序最靠前、
//最符合請(qǐng)求條件、最具體化、最少參數(shù)化等等)作為最佳匹配結(jié)果
bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
this.handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.getHandlerMethod();
}
}步驟6:如果找到了匹配的 HandlerMethod , DispatcherServlet 就會(huì)根據(jù)其類型選擇合適的 HandlerAdapter 來(lái)執(zhí)行它,并返回響應(yīng)結(jié)果;如果沒(méi)有找到匹配的 HandlerMethod , DispatcherServlet 就會(huì)拋出異?;蛘叻祷?04錯(cuò)誤頁(yè)面。

5. SimpleUrlHandlerMapping
SimpleUrlHandlerMapping 是另一個(gè)常用的 HandlerMapping 實(shí)現(xiàn)。
它允許指定URL模式和Handler的映射關(guān)系。

下面是一個(gè)簡(jiǎn)單的SimpleUrlHandlerMapping示例:
public class MySimpleUrlHandlerMapping extends SimpleUrlHandlerMapping {
public MySimpleUrlHandlerMapping() {
Properties mappings = new Properties();
mappings.setProperty("/hello", "helloController");
setMappings(mappings);
}
}在這個(gè)示例中,我們創(chuàng)建了一個(gè)名為 MySimpleUrlHandlerMapping 的自定義 HandlerMapping 類。我們通過(guò)創(chuàng)建一個(gè) Properties 對(duì)象并將請(qǐng)求路徑"/hello"映射到控制器名為"helloController"來(lái)設(shè)置URL映射。
當(dāng)請(qǐng)求到達(dá)服務(wù)器時(shí), SimpleUrlHandlerMapping 將查找請(qǐng)求路徑并將其與已注冊(cè)的URL模式進(jìn)行匹配。如果找到匹配項(xiàng),則返回關(guān)聯(lián)的處理程序?qū)ο蟆?/p>
下面是一個(gè)使用SimpleUrlHandlerMapping的控制器示例:
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView("hello");
mav.addObject("message", "Hello, World!");
return mav;
}
}在這個(gè)示例中,我們創(chuàng)建了一個(gè)名為 HelloController 的控制器類,并實(shí)現(xiàn)了Controller接口。 handleRequest 方法將返回一個(gè)名為"hello"的視圖,并將一個(gè)名為"message"的字符串添加到模型中。
我們將 HelloController 添加到 MySimpleUrlHandlerMapping 中:
MySimpleUrlHandlerMapping handlerMapping = new MySimpleUrlHandlerMapping();
Map<String, Object> urlMap = new HashMap<>();
urlMap.put("/hello", new HelloController());
handlerMapping.setUrlMap(urlMap);在這個(gè)示例中,我們將 HelloController 添加到 MySimpleUrlHandlerMapping 的URL映射中,并將其注冊(cè)到Spring MVC應(yīng)用程序上下文中。
介紹一下這個(gè)類中的一個(gè)重要方法 registerHandlers() , 這個(gè)方法是在Spring框架中用來(lái)注冊(cè)URL映射器的,它可以將URL模式和請(qǐng)求處理器(handler)關(guān)聯(lián)起來(lái)
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
//不作操作
} else {
//遍歷urlMap中的每一對(duì)鍵值對(duì),鍵是URL模式,值是handler對(duì)象
urlMap.forEach((url, handler) -> {
//對(duì)于每一個(gè)URL模式,如果它沒(méi)有以斜杠(/)開(kāi)頭,則在前面加上一個(gè)斜杠
if (!url.startsWith("/")) {
url = "/" + url;
}
//對(duì)于每一個(gè)handler對(duì)象,如果它是一個(gè)字符串,則去掉字符串兩端的空白字符
if (handler instanceof String) {
handler = ((String)handler).trim();
}
//調(diào)用父類AbstractUrlHandlerMapping的registerHandler方法,將URL模式和handler對(duì)象注冊(cè)到內(nèi)部的handlerMap中
this.registerHandler(url, handler);
});
}
}6. 使用HandlerMapping
在Spring MVC中,使用 HandlerMapping 非常簡(jiǎn)單。在控制器中,您可以使用@Autowired注解來(lái)注入 HandlerMapping 實(shí)現(xiàn),并使用它來(lái)查找適當(dāng)?shù)奶幚沓绦驅(qū)ο蟆?/p>
@Controller
public class MyController {
@Autowired
private HandlerMapping handlerMapping;
@RequestMapping("/my/path")
public String handleRequest(HttpServletRequest request) throws Exception {
HandlerExecutionChain chain = handlerMapping.getHandler(request);
Object handler = chain.getHandler();
// ...
}
}在這個(gè)示例中,我們使用@Autowired注解將HandlerMapping注入到控制器中。在handleRequest方法中,我們使用HandlerMapping的getHandler方法來(lái)獲取請(qǐng)求的處理程序?qū)ο蟆N覀兛梢赃M(jìn)一步操作處理程序?qū)ο?,例如調(diào)用其方法或檢查其注解。
6.1 如何選擇合適的HandlerMapping
SpringMVC提供了多種HandlerMapping實(shí)現(xiàn)類,你可以根據(jù)不同的方式來(lái)配置URL和Handler的映射關(guān)系,例如:
- 使用XML配置文件來(lái)定義SimpleUrlHandlerMapping或BeanNameUrlHandlerMapping,這種方式比較傳統(tǒng),但是可以集中管理所有的映射關(guān)系,也可以自定義攔截器。
- 使用@RequestMapping注解來(lái)定義RequestMappingHandlerMapping,這種方式比較流行,但是需要在每個(gè)Controller類或方法上添加注解,也可以使用@PathVariable或@RequestParam等注解來(lái)獲取請(qǐng)求參數(shù)。
- 使用@ControllerAdvice注解來(lái)定義ExceptionHandlerExceptionResolver,這種方式可以用來(lái)統(tǒng)一處理異常情況,也可以使用@ExceptionHandler或@ResponseStatus等注解來(lái)自定義異常處理邏輯。
你可以根據(jù)你的喜好和需求來(lái)選擇一種或多種HandlerMapping實(shí)現(xiàn)類,并且可以通過(guò)設(shè)置order屬性來(lái)調(diào)整它們的優(yōu)先級(jí)
7. 總結(jié)
HandlerMapping是Spring MVC中非常重要的一個(gè)組件,它負(fù)責(zé)將請(qǐng)求映射到適當(dāng)?shù)奶幚沓绦驅(qū)ο蟆?/p>
RequestMappingHandlerMapping和SimpleUrlHandlerMapping是兩個(gè)常用的HandlerMapping實(shí)現(xiàn)。
RequestMappingHandlerMapping使用@RequestMapping注解來(lái)確定處理程序?qū)ο螅鳶impleUrlHandlerMapping使用URL模式來(lái)確定處理程序?qū)ο蟆?/p>
到此這篇關(guān)于SpringMVC源碼之HandlerMapping處理器映射器解析的文章就介紹到這了,更多相關(guān)SpringMVC的HandlerMapping內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot之解決多個(gè)定時(shí)任務(wù)阻塞的問(wèn)題
這篇文章主要介紹了SpringBoot之解決多個(gè)定時(shí)任務(wù)阻塞的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04
Java語(yǔ)言的11大特點(diǎn)(Java初學(xué)者必知)
Java是一種簡(jiǎn)單的,面向?qū)ο蟮?,分布式的,解釋型的,健壯安全的,結(jié)構(gòu)中立的,可移植的,性能優(yōu)異、多線程的靜態(tài)語(yǔ)言。這篇文章主要介紹了Java語(yǔ)言的11大特點(diǎn),需要的朋友可以參考下2020-07-07
java編程實(shí)現(xiàn)郵件定時(shí)發(fā)送的方法
這篇文章主要介紹了java編程實(shí)現(xiàn)郵件定時(shí)發(fā)送的方法,涉及Java基于定時(shí)器實(shí)現(xiàn)計(jì)劃任務(wù)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
SpringBoot?docker項(xiàng)目部署實(shí)戰(zhàn)
本文主要介紹了SpringBoot?docker項(xiàng)目部署實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
關(guān)于使用Lambda表達(dá)式簡(jiǎn)化Comparator的使用問(wèn)題
這篇文章主要介紹了關(guān)于使用Lambda表達(dá)式簡(jiǎn)化Comparator的使用問(wèn)題,文中圖文講解了Comparator對(duì)象的方法,需要的朋友可以參考下2023-04-04

