亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

 更新時(shí)間:2023年08月31日 09:07:29   作者:小熊不吃香菜  
這篇文章主要介紹了SpringMVC源碼之HandlerMapping處理器映射器解析,在Spring?MVC中,HandlerMapping處理器映射器用于確定請求處理器對象,請求處理器可以是任何對象,只要它們使用了@Controller注解或注解@RequestMapping,需要的朋友可以參考下

在這里插入圖片描述

1. 什么是HandlerMapping

在Spring MVC中,HandlerMapping(處理器映射器)用于確定請求處理器對象。請求處理器可以是任何對象,只要它們使用了@Controller注解或注解@RequestMapping。

HandlerMapping負(fù)責(zé)將請求(url)映射到適當(dāng)?shù)奶幚砥鲗ο螅–ontroller)。

注:Handler即綁定了注解@RequestMapping或@Controller的類

HandlerMapping接口定義了一個(gè)方法:

public interface HandlerMapping {
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

getHandler方法用于查找處理器對象并返回處理程序的執(zhí)行鏈,HandlerExecutionChain包含了處理器對象和一系列攔截器,這些攔截器可以在處理程序執(zhí)行之前和之后執(zhí)行一些操作。

HandlerMapping接口繼承結(jié)構(gòu)體系

請?zhí)砑訄D片描述

2. HandlerMapping

2.1 HandlerMapping初始化

Http請求由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 {
            //沒有找到
            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和其他組件中訪問
            if (mapping.usesPathPatterns()) {
                this.parseRequestPath = true;
                break;
            }
        }
    }

Spring MVC框架中有多個(gè)HandlerMapping實(shí)現(xiàn),每個(gè)實(shí)現(xiàn)都可以使用不同的策略來確定請求處理器對象。

最常見的HandlerMapping實(shí)現(xiàn)是RequestMappingHandlerMapping和SimpleUrlHandlerMapping。

2.2 getHandler解析

getHandler方法根據(jù)請求找到對應(yīng)的處理器對象, 在 DispatcherServlet 類中, doDispatch() 方法調(diào)用 getHandler() 方法得到 HandlerExecutionChain 對象。

注: HandlerExecutionChain 是一個(gè)類,用于封裝處理器對象和攔截器列表。它是 HandlerMapping 的 getHandler 方法的返回值。

HandlerExecutionChain 的作用是在請求處理流程中,提供一種責(zé)任鏈模式(看圖),讓攔截器可以在處理器執(zhí)行前后進(jìn)行一些額外的操作,例如驗(yàn)證、日志、事務(wù)等。

拿到這個(gè)對象后,DispatcherServlet會(huì)調(diào)用它的applyPreHandle方法,執(zhí)行所有攔截器的preHandle方法。如果都返回true,則繼續(xù)調(diào)用處理器的方法;否則,返回響應(yīng)或者跳轉(zhuǎn)到其他頁面。

處理器執(zhí)行完畢后,DispatcherServlet會(huì)調(diào)用它的applyPostHandle方法,執(zhí)行所有攔截器的postHandle方法。這些方法可以對模型和視圖進(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對象
@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對象,它包含了處理請求的handler對象和任何配置的攔截器
                //接口方法
                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ù)請求從緩存中查找handler,如果沒有則調(diào)用子類實(shí)現(xiàn)的getHandlerInternal()方法
    	該方法在本類中有定義,是一個(gè)protected型的抽象方法
        Object handler = this.getHandlerInternal(request);
    	// 沒有找到,則會(huì)使用getDefaultHandler()方法獲取默認(rèn)的處理器
        if (handler == null) {
            handler = this.getDefaultHandler();
        }
        if (handler == null) {
            return null;
        } else {
            //檢查handler對象是否是一個(gè)字符串類型。如果是的話,就從應(yīng)用上下文中根據(jù)字符串名稱獲取對應(yīng)的bean作為handler對象
            if (handler instanceof String) {
                String handlerName = (String)handler;
                handler = this.obtainApplicationContext().getBean(handlerName);
            }
			//如果請求對象中沒有緩存的路徑信息,就調(diào)用initLookupPath方法來解析請求的路徑信息,并且保存在請求對象的一個(gè)屬性中
            if (!ServletRequestPathUtils.hasCachedPath(request)) {
                this.initLookupPath(request);
            }
			//根據(jù)handler對象和請求創(chuàng)建一個(gè)HandlerExecutionChain對象,包含處理器對象和攔截器列表
            HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
            return executionChain;
        }
}

到此為止就獲得了executionChain對象實(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è)類級別的對象**,例如一個(gè)Controller類或一個(gè)Bean對象

AbstractHandlerMethodMapping 是基于方法級別的 HandlerMapping ,它支持使用@RequestMapping注解來指定請求路徑和方法。它返回的Handler是一個(gè)方法級別的對象,例如一個(gè)Controller類中的某個(gè)方法或一個(gè)Bean對象中的某個(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注解來定義URL和方法的映射關(guān)系

@Controller
@RequestMapping("/test")
public class TestController {
    @RequestMapping("/do")
    public String doSomething() {
        // ...
    }
}

這兩種方式都可以實(shí)現(xiàn)URL和Handler的綁定,但是后者更靈活和簡潔

3.2 AbstractUrlHandlerMapping

//根據(jù)用戶請求信息中的URL查找handler
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    	//初始化請求的查找路徑,也就是去掉上下文路徑和后綴名的URL路徑
        String lookupPath = this.initLookupPath(request);
        Object handler;
    	//是否使用路徑模式來選擇不同的查找handler的方法
    	//
        if (this.usesPathPatterns()) {
            //獲取請求的RequestPath對象,它封裝了請求的URL路徑、上下文路徑、后綴名等信息。
            RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
            //根據(jù)RequestPath對象、查找路徑和請求對象,調(diào)用lookupHandler()方法來查找匹配的handler
            handler = this.lookupHandler(path, lookupPath, request);
        } else {
            handler = this.lookupHandler(lookupPath, request);
        }
    	//沒有找到匹配的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對象
        return handler;
}

在這里插入圖片描述

3.3 AbstractHandlerMethodMapping

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    	//初始化請求的查找路徑,也就是去掉上下文路徑和后綴名的URL路徑
        String lookupPath = this.initLookupPath(request);
    	//獲取映射注冊表的讀鎖,以保證線程安全
        this.mappingRegistry.acquireReadLock();
        HandlerMethod var4;
    	//獲取讀鎖后,嘗試查找匹配的handler方法,并在最后釋放讀鎖
        try {
            //根據(jù)查找路徑和請求對象,在映射注冊表中查找匹配的handler方法
            HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
            //判斷是否找到了handler方法
            //如果找到了,則調(diào)用createWithResolvedBean()方法來創(chuàng)建一個(gè)新的HandlerMethod實(shí)例,并解析其關(guān)聯(lián)的bean對象;
            //如果沒有找到,則返回null。
            var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
        } finally {
            this.mappingRegistry.releaseReadLock();
        }
		//返回獲取到的handler對象
        return var4;
}

下面簡單介紹一下AbstractUrlHandlerMapping與AbstractHandlerMethodMapping的常用子類實(shí)現(xiàn)

4. RequestMappingHandlerMapping

RequestMappingHandlerMapping 是Spring MVC中最常用的HandlerMapping實(shí)現(xiàn)之一。

它使用 @RequestMapping 注解來確定請求處理器對象。

下面是一個(gè)簡單的控制器示例:

@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è)控制器,并且所有請求路徑以/user開頭。

@GetMapping注解用于處理HTTP GET請求,{id}是一個(gè)路徑變量,它將匹配任何非空字符串,并將其解析為一個(gè)整數(shù)。

getUser方法將通過id獲取用戶對象并將其添加到模型中,最后返回一個(gè)視圖名為"user"的字符串。

RequestMappingHandlerMapping 會(huì)掃描所有@Controller注解標(biāo)記的類,并將所有帶有@RequestMapping注解的方法添加到HandlerMapping中。

在請求到達(dá)服務(wù)器時(shí), RequestMappingHandlerMapping 將匹配請求路徑和請求方法,并將請求映射到適當(dāng)?shù)奶幚砥鞣椒ā?/p>

4.1 加載過程

  1. RequestMappingHandlerMapping 實(shí)現(xiàn)了接口 InitializingBean ,在bean加載完成后會(huì)自動(dòng)調(diào)用 afterPropertiesSet 方法,在此方法中調(diào)用了initHandlerMethods()來實(shí)現(xiàn)初始化
  2. initHandlerMethods() 會(huì)遍歷所有bean,如果bean實(shí)現(xiàn)帶有注解@Controller或者@RequestMapping 則進(jìn)一步調(diào)用 detectHandlerMethods 處理,處理邏輯大致就是根據(jù)@RequestMapping配置的信息,把解析結(jié)果封裝成 RequestMappingInfo 對象,也就是說 RequestMappingInfo 對象是用來裝載方法的匹配相關(guān)信息,每個(gè)匹配的方法都會(huì)對應(yīng)一個(gè) RequestMappingInfo 對象,然后注冊到 MappingRegistry 中

具體流程:

  1. 當(dāng)Spring容器啟動(dòng)時(shí),它會(huì)掃描所有帶有@Controller或@RestController注解的類,并將它們作為處理器對象注冊到RequestMappingHandlerMapping中。
  2. RequestMappingHandlerMapping會(huì)遍歷每個(gè)處理器對象中的所有方法,并使用getMappingForMethod()方法來獲取每個(gè)方法上定義或繼承的@RequestMapping注解,然后將這些注解轉(zhuǎn)換為RequestMappingInfo對象,包含了請求路徑、請求方法、請求參數(shù)、請求頭等信息。
  3. RequestMappingHandlerMapping會(huì)將每個(gè)RequestMappingInfo對象和對應(yīng)的處理器方法封裝成一個(gè)HandlerMethod對象,并將這些對象存儲在一個(gè)Map結(jié)構(gòu)中,以便于后續(xù)查找。
  4. 當(dāng)一個(gè)HTTP請求到達(dá)DispatcherServlet時(shí),它會(huì)調(diào)用RequestMappingHandlerMapping的getHandler()方法來根據(jù)請求URI找到匹配的處理器方法。
  5. RequestMappingHandlerMapping會(huì)遍歷Map中的所有鍵值對(即每個(gè)RequestMappingInfo和HandlerMethod),并使用PathMatcher或PathPatternParser來判斷請求URI是否與RequestMappingInfo中定義的路徑匹配。如果匹配,就返回對應(yīng)的HandlerMethod;如果不匹配,就繼續(xù)查找下一個(gè)鍵值對。
  6. 如果找到了匹配的HandlerMethod,DispatcherServlet就會(huì)根據(jù)其類型選擇合適的HandlerAdapter來執(zhí)行它,并返回響應(yīng)結(jié)果;如果沒有找到匹配的HandlerMethod,DispatcherServlet就會(huì)拋出異?;蛘叻祷?04錯(cuò)誤頁面。

步驟1的注冊過程

public void afterPropertiesSet() {
    	//……
        super.afterPropertiesSet();
}
//父類AbstractHandlerMethodMapping 的方法
public void afterPropertiesSet() {
        this.initHandlerMethods();
}
//掃描容器中的bean,檢測和注冊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];
            //用于過濾掉以"scopedTarget."開頭的bean名稱的。
            //這些bean名稱是由Spring創(chuàng)建的代理對象,用于支持不同作用域的bean
            //例如session或request。這些代理對象不是真正的handler,所以要排除掉。
            if (!beanName.startsWith("scopedTarget.")) {
                //處理候選的bean,判斷候選的bean是否是一個(gè)handler,也就是是否有@Controller注解或者@RequestMapping注解
                //如果是的話,就調(diào)用detectHandlerMethods方法,用于檢測和注冊handler方法
                this.processCandidateBean(beanName);
            }
        }
		//初始化handler方法, 對所有handler方法進(jìn)行排序和日志輸出
        this.handlerMethodsInitialized(this.getHandlerMethods());
}
//檢測和注冊handler方法
protected void detectHandlerMethods(Object handler) {
    	//存儲handler對象的類信息
    	//如果handler是一個(gè)字符串,那么就使用應(yīng)用程序上下文來獲取對應(yīng)的類類型;否則就直接使用handler.getClass()方法來獲取類類型。
        Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
        if (handlerType != null) {
            //存儲handlerType去除代理和增強(qiáng)后的原始類類型。這是為了避免AOP對方法檢測造成干擾。
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            //存儲userType中所有帶有映射注解(如@RequestMapping)的方法及其對應(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è)鍵值對(即每個(gè)方法及其映射信息)
            methods.forEach((method, mapping) -> {
                //存儲經(jīng)過AopUtils.selectInvocableMethod()方法處理后可以被調(diào)用(即沒有被final修飾)
                //且與userType匹配(即沒有被覆蓋) 的原始或橋接(即泛型擦除后生成) 方法
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                //注冊到處理器映射
                this.registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
 }

注: RequestMappingInfo 類,主要用來記錄方法上 @RequestMapping() 注解里面的參數(shù),針對 RequestMappingHandlerMapping 映射器來使用。

步驟2: RequestMappingHandlerMapping 會(huì)遍歷每個(gè)處理器對象中的所有方法,并使用 getMappingForMethod() 方法來獲取每個(gè)方法上定義或繼承的@RequestMapping注解,然后將這些注解轉(zhuǎn)換為 RequestMappingInfo 對象,包含了請求路徑、請求方法、請求參數(shù)、請求頭等信息。

//根據(jù)處理器類和方法上的@RequestMapping注解來創(chuàng)建一個(gè)RequestMappingInfo對象(封裝了請求映射的信息,如請求路徑、請求方法、請求參數(shù)、請求頭等)
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	    //獲取方法上定義或繼承的@RequestMapping注解,并將其轉(zhuǎn)換為一個(gè)RequestMappingInfo對象
        RequestMappingInfo info = this.createRequestMappingInfo(method);
        if (info != null) {
            //獲取處理器類上定義或繼承的@RequestMapping注解,并將其轉(zhuǎn)換為另一個(gè)RequestMappingInfo對象
            RequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                //合并兩個(gè)RequestMappingInfo對象
                info = typeInfo.combine(info);
            }
			//獲取處理器類上定義或繼承的@PathPrefix注解,并將其轉(zhuǎn)換為一個(gè)字符串前綴
            String prefix = this.getPathPrefix(handlerType);
            if (prefix != null) {
                //根據(jù)指定的路徑前綴和配置選項(xiàng)來構(gòu)建一個(gè)新的請求映射信息,并且與原有信息進(jìn)行合并
                info = RequestMappingInfo.paths(new String[]{prefix}).options(this.config).build().combine(info);
            }
        }
        return info;
}

步驟3: RequestMappingHandlerMapping 會(huì)將每個(gè) RequestMappingInfo 對象和對應(yīng)的處理器方法封裝成一個(gè) HandlerMethod 對象,并將這些對象存儲在一個(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對象
                HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method);
                this.validateMethodMapping(handlerMethod, mapping);
                //獲取匹配條件對應(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對象(封裝了handler方法、直接路徑、名稱、跨域配置等信息)添加到registry中
                this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration(mapping, handlerMethod, directPaths, name, corsConfig != null));
            } finally {
            }
}

步驟4:當(dāng)一個(gè)HTTP請求到達(dá) DispatcherServlet 時(shí),它會(huì)調(diào)用 RequestMappingHandlerMapping 的 getHandler() 方法來根據(jù)請求URI找到匹配的處理器方法。(即3.3節(jié)提到的 AbstractHandlerMethodMapping 父類中的 getHandler() 方法)

步驟5: RequestMappingHandlerMapping 會(huì)遍歷Map中的所有鍵值對(即每個(gè) RequestMappingInfo和HandlerMethod ),并使用 PathMatcher 或 PathPatternParser 來判斷請求URI是否與 RequestMappingInfo 中定義的路徑匹配。如果匹配,就返回對應(yīng)的 HandlerMethod ;如果不匹配,就繼續(xù)查找下一個(gè)鍵值對。

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    	// 創(chuàng)建一個(gè)空的Match列表,用來存放匹配的RequestMappingInfo和HandlerMethod對象
        List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();
    	// 從mappingRegistry中獲取直接路徑匹配(即沒有通配符或變量)的RequestMappingInfo列表
        List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
        if (directPathMatches != null) {
            // 調(diào)用addMatchingMappings方法,判斷直接路徑匹配的RequestMappingInfo是否與請求條件匹配
            //如果匹配,就添加到matches列表中
            this.addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
             // 如果matches列表為空,說明沒有直接路徑匹配的RequestMappingInfo與請求條件匹配
            //那么就從mappingRegistry中獲取所有注冊過的RequestMappingInfo,
            //并調(diào)用addMatchingMappings方法判斷是否與請求條件匹配
            //如果匹配,就添加到matches列表中
            this.addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
        }
        if (matches.isEmpty()) {
             // 如果matches列表仍然為空,說明沒有任何一個(gè)RequestMappingInfo與請求條件匹配
            //那么就調(diào)用handleNoMatch方法處理沒有匹配結(jié)果的情況,并返回null或拋出異常
            return this.handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
        } else {
            // 如果matches列表不為空,說明至少有一個(gè)RequestMappingInfo與請求條件匹配
        	// 那么就從matches列表中獲取第一個(gè)Match對象(即最先添加進(jìn)去的),作為最佳匹配結(jié)果
            AbstractHandlerMethodMapping<T>.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
            if (matches.size() > 1) {
                // 如果matches列表中有多個(gè)Match對象(即有多個(gè)RequestMappingInfo與請求條件匹配)
                //那么就需要進(jìn)行排序和篩選
            	// 首先創(chuàng)建一個(gè)比較器comparator,根據(jù)getMappingComparator方法返回的比較器對每個(gè)Match對象進(jìn)行比較(主要比較它們包含的RequestMappingInfo)
                Comparator<AbstractHandlerMethodMapping<T>.Match> comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request));
                 // 然后對matches列表進(jìn)行排序,根據(jù)comparator比較器確定每個(gè)Match對象之間的優(yōu)先級和順序
                matches.sort(comparator);
            	// 再次從matches列表中獲取第一個(gè)Match對象(即排序后最優(yōu)先級最高、順序最靠前、
                //最符合請求條件、最具體化、最少參數(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 來執(zhí)行它,并返回響應(yīng)結(jié)果;如果沒有找到匹配的 HandlerMethod , DispatcherServlet 就會(huì)拋出異?;蛘叻祷?04錯(cuò)誤頁面。

在這里插入圖片描述

5. SimpleUrlHandlerMapping

SimpleUrlHandlerMapping 是另一個(gè)常用的 HandlerMapping 實(shí)現(xiàn)。

它允許指定URL模式和Handler的映射關(guān)系。

在這里插入圖片描述

下面是一個(gè)簡單的SimpleUrlHandlerMapping示例:

public class MySimpleUrlHandlerMapping extends SimpleUrlHandlerMapping {
    public MySimpleUrlHandlerMapping() {
        Properties mappings = new Properties();
        mappings.setProperty("/hello", "helloController");
        setMappings(mappings);
    }
}

在這個(gè)示例中,我們創(chuàng)建了一個(gè)名為 MySimpleUrlHandlerMapping 的自定義 HandlerMapping 類。我們通過創(chuàng)建一個(gè) Properties 對象并將請求路徑"/hello"映射到控制器名為"helloController"來設(shè)置URL映射。

當(dāng)請求到達(dá)服務(wù)器時(shí), SimpleUrlHandlerMapping 將查找請求路徑并將其與已注冊的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映射中,并將其注冊到Spring MVC應(yīng)用程序上下文中。

介紹一下這個(gè)類中的一個(gè)重要方法 registerHandlers() , 這個(gè)方法是在Spring框架中用來注冊URL映射器的,它可以將URL模式和請求處理器(handler)關(guān)聯(lián)起來

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
        if (urlMap.isEmpty()) {
            //不作操作
        } else {
            //遍歷urlMap中的每一對鍵值對,鍵是URL模式,值是handler對象
            urlMap.forEach((url, handler) -> {
                //對于每一個(gè)URL模式,如果它沒有以斜杠(/)開頭,則在前面加上一個(gè)斜杠
                if (!url.startsWith("/")) {
                    url = "/" + url;
                }
				//對于每一個(gè)handler對象,如果它是一個(gè)字符串,則去掉字符串兩端的空白字符
                if (handler instanceof String) {
                    handler = ((String)handler).trim();
                }
				//調(diào)用父類AbstractUrlHandlerMapping的registerHandler方法,將URL模式和handler對象注冊到內(nèi)部的handlerMap中
                this.registerHandler(url, handler);
            });
        }
    }

6. 使用HandlerMapping

在Spring MVC中,使用 HandlerMapping 非常簡單。在控制器中,您可以使用@Autowired注解來注入 HandlerMapping 實(shí)現(xiàn),并使用它來查找適當(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方法來獲取請求的處理程序?qū)ο?。我們可以進(jìn)一步操作處理程序?qū)ο?,例如調(diào)用其方法或檢查其注解。

6.1 如何選擇合適的HandlerMapping

SpringMVC提供了多種HandlerMapping實(shí)現(xiàn)類,你可以根據(jù)不同的方式來配置URL和Handler的映射關(guān)系,例如:

  • 使用XML配置文件來定義SimpleUrlHandlerMapping或BeanNameUrlHandlerMapping,這種方式比較傳統(tǒng),但是可以集中管理所有的映射關(guān)系,也可以自定義攔截器。
  • 使用@RequestMapping注解來定義RequestMappingHandlerMapping,這種方式比較流行,但是需要在每個(gè)Controller類或方法上添加注解,也可以使用@PathVariable或@RequestParam等注解來獲取請求參數(shù)。
  • 使用@ControllerAdvice注解來定義ExceptionHandlerExceptionResolver,這種方式可以用來統(tǒng)一處理異常情況,也可以使用@ExceptionHandler或@ResponseStatus等注解來自定義異常處理邏輯。

你可以根據(jù)你的喜好和需求來選擇一種或多種HandlerMapping實(shí)現(xiàn)類,并且可以通過設(shè)置order屬性來調(diào)整它們的優(yōu)先級

7. 總結(jié)

HandlerMapping是Spring MVC中非常重要的一個(gè)組件,它負(fù)責(zé)將請求映射到適當(dāng)?shù)奶幚沓绦驅(qū)ο蟆?/p>

RequestMappingHandlerMapping和SimpleUrlHandlerMapping是兩個(gè)常用的HandlerMapping實(shí)現(xiàn)。

RequestMappingHandlerMapping使用@RequestMapping注解來確定處理程序?qū)ο螅鳶impleUrlHandlerMapping使用URL模式來確定處理程序?qū)ο蟆?/p>

到此這篇關(guān)于SpringMVC源碼之HandlerMapping處理器映射器解析的文章就介紹到這了,更多相關(guān)SpringMVC的HandlerMapping內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot之解決多個(gè)定時(shí)任務(wù)阻塞的問題

    SpringBoot之解決多個(gè)定時(shí)任務(wù)阻塞的問題

    這篇文章主要介紹了SpringBoot之解決多個(gè)定時(shí)任務(wù)阻塞的問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Java語言的11大特點(diǎn)(Java初學(xué)者必知)

    Java語言的11大特點(diǎn)(Java初學(xué)者必知)

    Java是一種簡單的,面向?qū)ο蟮?,分布式的,解釋型的,健壯安全的,結(jié)構(gòu)中立的,可移植的,性能優(yōu)異、多線程的靜態(tài)語言。這篇文章主要介紹了Java語言的11大特點(diǎn),需要的朋友可以參考下
    2020-07-07
  • SpringBoot Mybatis 配置文件形式詳解

    SpringBoot Mybatis 配置文件形式詳解

    這篇文章主要介紹了SpringBoot Mybatis 配置文件形式詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • java編程實(shí)現(xiàn)郵件定時(shí)發(fā)送的方法

    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)

    本文主要介紹了SpringBoot?docker項(xiàng)目部署實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-08-08
  • FreeSWITCH跨NAT部署配置詳解

    FreeSWITCH跨NAT部署配置詳解

    這篇文章主要為大家介紹了FreeSWITCH跨NAT部署配置詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • 關(guān)于使用Lambda表達(dá)式簡化Comparator的使用問題

    關(guān)于使用Lambda表達(dá)式簡化Comparator的使用問題

    這篇文章主要介紹了關(guān)于使用Lambda表達(dá)式簡化Comparator的使用問題,文中圖文講解了Comparator對象的方法,需要的朋友可以參考下
    2023-04-04
  • Java中的注解與注解處理器

    Java中的注解與注解處理器

    這篇文章主要介紹了Java中的注解與注解處理器,元注解的作用是負(fù)責(zé)注解其他注解, Java5.0定義了4個(gè)標(biāo)準(zhǔn)的meta-annotation(元注解)類型,它們被用來提供對其它注解類型進(jìn)行說明,需要的朋友可以參考下
    2023-11-11
  • java實(shí)現(xiàn)計(jì)算器模板及源碼

    java實(shí)現(xiàn)計(jì)算器模板及源碼

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)計(jì)算器模板及源碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • 深入理解Spring事務(wù)原理

    深入理解Spring事務(wù)原理

    這篇文章主要帶領(lǐng)大家深入理解Spring事務(wù)原理,Spring事務(wù)的傳播屬性
    2016-07-07

最新評論