SpringBoot之@Controller和@RequestMapping的實現(xiàn)原理解讀
SpringBoot之@Controller和@RequestMapping的實現(xiàn)原理
干貨分享,SpringBoot中Web接口資源是如何被管理起來呢?
一個請求,通過在瀏覽器上輸入了一個url,是如何被SpringWeb匹配到對應的方法的呢?
帶著這個疑問我們來學習本篇。
瀏覽器的請求,是如何被映射到后端服務的方法上呢?
后端服務使用SpringBoot只使用了一個注解就提供了web服務的實現(xiàn)原理是什么呢?
@RestController public class TestController { @GetMapping("/name") public String name(HttpServletRequest request){ return request.toString(); } }
帶著上面的疑問,小編通過源碼的方式帶你一看究竟吧。 為了能讓各位童鞋更好的更容易的理解。第一趴我們先來補充點知識點。
一、注解派生概念
在java體系中,類是可以被繼承,接口可以被實現(xiàn)。但是注解沒有這些概念,而是有一個派生的概念。舉例,注解A。被標記了在注解B頭上,那么我們可以說注解B就是注解A的派生。
下面我們舉一個例子:
@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類進行標記是一個POST資源
- 我們通過main方法里面可以知道。通過反射我們能拿到Method上的PostMapping注解信息。
- 但是看這一行AnnotatedElementUtils.hasAnnotation(queryBbsPostAll,RequestMapping.class)為什么也是true呢?
沒錯因為PostMapping是RequestMapping的派生注解。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.POST) public @interface PostMapping { ... }
請記住這個小的知識點,后面的邏輯會用到。因為下面后有大量的源碼,為了方便標注,小編使用截圖的形式,在截圖上會加上注釋信息。 ?
二、進入正題,跟進源碼解析請求Method
通過跟進源碼,我們會發(fā)現(xiàn)這樣一個類。AbstractHandlerMethodMapping。其實現(xiàn)了實現(xiàn) InitializingBean接口。在當前 Bean初始化時候會執(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資源類。當一個類被標記了 @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或者其他派生的注解。生成請求信息。 注意這個請求信息里面也是有很多邏輯的不過不是本篇討論的重點,就不說了。稍微提一下。根據(jù)規(guī)則來匹配url邏輯就在這里面。
這里我們能看到源碼里拿到了Method并拿到了執(zhí)行這個Method的實例Bean。在這里封裝成了HandlerMethod并注冊到了MappingRegistry中。
在注冊的過程中把RequestMapping中的路徑信息同事也放到一個urlLookup中。key是url,value是Mapping信息。
到這里其實我們就把本篇的議題就說明清楚了。下面我們在看下SpringWeb是如何將http請求信息路由到具體的HandlerMethod的吧。
三、最后串一下請求流程
看了前面的截圖,我們知道Spring是如何把這些Web資源信息給保存起來的了。然后就看是DispatcherServlet的邏輯了。 首先DispatcherServlet 是一個Servlet。Servlet相信大家都都知道就不重點說原理。 我們直接看doService -> doDispatch 方法
根據(jù)請求路徑,找到從Mapping信息,然后根據(jù)Mapping信息匹配到具體的HandlerMethod。 ok本篇內(nèi)容就到這里。謝謝大家。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Maven繼承父工程時的relativePath標簽解析用法小結(jié)
relativePath 的作用是為了找到父級工程的pom.xml,本文主要介紹了Maven繼承父工程時的relativePath標簽解析用法小結(jié),具有一定的參考價值,感興趣的可以了解一下2024-03-03java利用udp實現(xiàn)發(fā)送數(shù)據(jù)
這篇文章主要為大家詳細介紹了java利用udp實現(xiàn)發(fā)送數(shù)據(jù),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-07-07Java數(shù)據(jù)導入功能之讀取Excel文件實例
這篇文章主要介紹了Java數(shù)據(jù)導入功能之讀取Excel文件實例,本文給出了jar包的下載地址以及讀取Excel文件的代碼實例,需要的朋友可以參考下2015-06-06mybatis3使用@Select等注解實現(xiàn)增刪改查操作
這篇文章主要介紹了mybatis3使用@Select等注解實現(xiàn)增刪改查操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11Java解析調(diào)用webservice服務的返回XML串詳解
這篇文章主要介紹了Java解析調(diào)用webservice服務的返回XML串詳解的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-07-07