SpringBoot之@Controller和@RequestMapping的實(shí)現(xiàn)原理解讀
SpringBoot之@Controller和@RequestMapping的實(shí)現(xiàn)原理
干貨分享,SpringBoot中Web接口資源是如何被管理起來呢?
一個(gè)請(qǐng)求,通過在瀏覽器上輸入了一個(gè)url,是如何被SpringWeb匹配到對(duì)應(yīng)的方法的呢?
帶著這個(gè)疑問我們來學(xué)習(xí)本篇。
瀏覽器的請(qǐng)求,是如何被映射到后端服務(wù)的方法上呢?
后端服務(wù)使用SpringBoot只使用了一個(gè)注解就提供了web服務(wù)的實(shí)現(xiàn)原理是什么呢?
@RestController
public class TestController {
@GetMapping("/name")
public String name(HttpServletRequest request){
return request.toString();
}
}
帶著上面的疑問,小編通過源碼的方式帶你一看究竟吧。 為了能讓各位童鞋更好的更容易的理解。第一趴我們先來補(bǔ)充點(diǎn)知識(shí)點(diǎn)。
一、注解派生概念
在java體系中,類是可以被繼承,接口可以被實(shí)現(xiàn)。但是注解沒有這些概念,而是有一個(gè)派生的概念。舉例,注解A。被標(biāo)記了在注解B頭上,那么我們可以說注解B就是注解A的派生。
下面我們舉一個(gè)例子:
@RestController
public class PostController {
@ApiOperation(value = "查詢Bbs所有文章")
@PostMapping(value = "/query/bbs/posts", produces = MediaType.APPLICATION_JSON_VALUE)
public Result<PostAllResponse> queryBbsPostAll(@RequestBody PostAllSelectRequest postAllSelectRequest) {
return postBiz.queryBbsPostAll(postAllSelectRequest);
}
public static void main(String[] args) {
Method queryBbsPostAll = ClassUtils.getMethod(PostController.class, "queryBbsPostAll",PostAllSelectRequest.class);
PostMapping annotation = AnnotationUtils.findAnnotation(queryBbsPostAll, PostMapping.class);
///query/bbs/posts
System.out.println(StringUtils.arrayToCommaDelimitedString(annotation.value()));
//application/json
System.out.println(StringUtils.arrayToCommaDelimitedString(annotation.produces()));
//是否包含RequestMapping: true
System.out.println("是否包含RequestMapping: "+AnnotatedElementUtils.hasAnnotation(queryBbsPostAll,RequestMapping.class));
RequestMapping mergedAnnotation = AnnotatedElementUtils.getMergedAnnotation(queryBbsPostAll, RequestMapping.class);
///query/bbs/posts
System.out.println(StringUtils.arrayToCommaDelimitedString(mergedAnnotation.value()));
}
}- queryBbsPostAll是用了PostMapping類進(jìn)行標(biāo)記是一個(gè)POST資源
- 我們通過main方法里面可以知道。通過反射我們能拿到Method上的PostMapping注解信息。
- 但是看這一行AnnotatedElementUtils.hasAnnotation(queryBbsPostAll,RequestMapping.class)為什么也是true呢?
沒錯(cuò)因?yàn)镻ostMapping是RequestMapping的派生注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.POST)
public @interface PostMapping {
...
}請(qǐng)記住這個(gè)小的知識(shí)點(diǎn),后面的邏輯會(huì)用到。因?yàn)橄旅婧笥写罅康脑创a,為了方便標(biāo)注,小編使用截圖的形式,在截圖上會(huì)加上注釋信息。 ?
二、進(jìn)入正題,跟進(jìn)源碼解析請(qǐng)求Method
通過跟進(jìn)源碼,我們會(huì)發(fā)現(xiàn)這樣一個(gè)類。AbstractHandlerMethodMapping。其實(shí)現(xiàn)了實(shí)現(xiàn) InitializingBean接口。在當(dāng)前 Bean初始化時(shí)候會(huì)執(zhí)行afterPropertiesSet -> initHandlerMethods。
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
/**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #getCandidateBeanNames()
* @see #processCandidateBean
* @see #handlerMethodsInitialized
*/
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
}RequestMappingHandlerMapping解析Method上的RequestMapping信息

isHandler 方法判斷是否是web資源類。當(dāng)一個(gè)類被標(biāo)記了 @Controller 或者@RequestMapping。 注意 @RestController 是@Controller的派生類。所以這里只用判斷 @Controller 或者@RequestMapping就行了。
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
detectHandlerMethods方法就是真正開始解析Method的邏輯。通過解析Method上的 @RequestMapping或者其他派生的注解。生成請(qǐng)求信息。 注意這個(gè)請(qǐng)求信息里面也是有很多邏輯的不過不是本篇討論的重點(diǎn),就不說了。稍微提一下。根據(jù)規(guī)則來匹配url邏輯就在這里面。

這里我們能看到源碼里拿到了Method并拿到了執(zhí)行這個(gè)Method的實(shí)例Bean。在這里封裝成了HandlerMethod并注冊(cè)到了MappingRegistry中。

在注冊(cè)的過程中把RequestMapping中的路徑信息同事也放到一個(gè)urlLookup中。key是url,value是Mapping信息。

到這里其實(shí)我們就把本篇的議題就說明清楚了。下面我們?cè)诳聪耂pringWeb是如何將http請(qǐng)求信息路由到具體的HandlerMethod的吧。
三、最后串一下請(qǐng)求流程
看了前面的截圖,我們知道Spring是如何把這些Web資源信息給保存起來的了。然后就看是DispatcherServlet的邏輯了。 首先DispatcherServlet 是一個(gè)Servlet。Servlet相信大家都都知道就不重點(diǎn)說原理。 我們直接看doService -> doDispatch 方法

根據(jù)請(qǐng)求路徑,找到從Mapping信息,然后根據(jù)Mapping信息匹配到具體的HandlerMethod。 ok本篇內(nèi)容就到這里。謝謝大家。


總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java中關(guān)鍵字synchronized的使用方法詳解
synchronized關(guān)鍵字可以作為函數(shù)的修飾符,也可作為函數(shù)內(nèi)的語(yǔ)句,也就是平時(shí)說的同步方法和同步語(yǔ)句塊,下面這篇文章主要給大家介紹了關(guān)于Java中synchronized使用的相關(guān)資料,需要的朋友可以參考下2021-08-08
Mybatis中SqlMapper配置的擴(kuò)展與應(yīng)用詳細(xì)介紹(1)
這篇文章主要介紹了Mybatis中SqlMapper配置的擴(kuò)展與應(yīng)用(1)的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
SpringBoot如何實(shí)現(xiàn)并發(fā)任務(wù)并返回結(jié)果
這篇文章主要介紹了SpringBoot如何實(shí)現(xiàn)并發(fā)任務(wù)并返回結(jié)果問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
SpringBoot 優(yōu)雅停機(jī)實(shí)現(xiàn)原理與最佳實(shí)踐指南
文章介紹了Spring Boot在2.3版本中引入的優(yōu)雅停機(jī)機(jī)制,詳細(xì)解釋了優(yōu)雅停機(jī)的概念、核心組件與層級(jí)關(guān)系、Spring Boot 2.3前后的區(qū)別,以及在生產(chǎn)環(huán)境中的最佳實(shí)踐,強(qiáng)調(diào)了啟用優(yōu)雅停機(jī)的重要性,以提高系統(tǒng)的高可用性和穩(wěn)定性2026-04-04

