springBoot-springMVC请求处理原理
目錄
一、rest風(fēng)格使用及原理
1.rest使用
2.rest原理(表單提交)
3.rest使用(客戶端工具如postman)
4.rest優(yōu)化使用,與上面等同。
二、請求映射原理
1.本質(zhì)還是繼承自原生的HttpServlet
2.關(guān)鍵方法-DispatcherServlet的doDispatch方法
3.容器中注冊的HandlerMapping
4.自定義HandlerMapping
一、rest風(fēng)格使用及原理
1.rest使用
(1)使用@GetMapping、@PostMapping、@DeleteMapping等等。。
(2)Rest風(fēng)格支持(使用HTTP請求方式動詞來表示對資源的操作)
* 以前:/getUser???獲取用戶??/deleteUser 刪除用戶???/editUser??修改用戶???/saveUser 保存用戶
* 現(xiàn)在: /user GET-獲取用戶 DELETE-刪除用戶 PUT-修改用戶 POST-保存用戶
(3)核心Filter;HiddenHttpMethodFilter
?? ?用法: 表單method=post,隱藏域 _method=put
注意:表單提交直接使用method=delete、method=put,是不識別的!表單只能寫get、post!
?? ?SpringBoot中手動開啟
spring:mvc:hiddenmethod:filter:enabled: true #開啟頁面表單的Rest功能原因是:spring自動配置已經(jīng)配置了,需要手動開啟
@Bean@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();}(4)擴展:如何把_method 這個名字換成我們自己喜歡的。
//自定義filter import org.springframework.web.filter.HiddenHttpMethodFilter;@Beanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();methodFilter.setMethodParam("_m");return methodFilter;}(5)測試rest風(fēng)格的表單提交
@RequestMapping(value = "/user",method = RequestMethod.GET)public String getUser(){return "GET-張三";}@RequestMapping(value = "/user",method = RequestMethod.POST)public String saveUser(){return "POST-張三";}@RequestMapping(value = "/user",method = RequestMethod.PUT)public String putUser(){return "PUT-張三";}@RequestMapping(value = "/user",method = RequestMethod.DELETE)public String deleteUser(){return "DELETE-張三";} 測試REST風(fēng)格; <form action="/user" method="get"><input value="REST-GET 提交" type="submit"/> </form> <form action="/user" method="post"><input value="REST-POST 提交" type="submit"/> </form> <form action="/user" method="post"><input name="_method" type="hidden" value="delete"/><input name="_m" type="hidden" value="delete"/><input value="REST-DELETE 提交" type="submit"/> </form> <form action="/user" method="post"><input name="_method" type="hidden" value="PUT"/><input value="REST-PUT 提交" type="submit"/> </form>2.rest原理(表單提交)
(1)表單提交會帶上_method=PUT
(2)請求過來被HiddenHttpMethodFilter攔截
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {HttpServletRequest requestToUse = request;// 請求是否正常,并且是POSTif ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {// 獲取到_method的值String paramValue = request.getParameter(this.methodParam);if (StringUtils.hasLength(paramValue)) {// 表單提交的_method的值大小寫無所謂。String method = paramValue.toUpperCase(Locale.ENGLISH);// 兼容以下請求:PUT.DELETE.PATCHif (ALLOWED_METHODS.contains(method)) {// 原生request(post),包裝模式requesWrapper重寫了getMethod方法,返回的是傳入的值。// 過濾器鏈放行的時候用wrapper。以后的方法調(diào)用getMethod是調(diào)用requesWrapper的。requestToUse = new HttpMethodRequestWrapper(request, method);}}}filterChain.doFilter(requestToUse, response); }3.rest使用(客戶端工具如postman)
* 如PostMan直接發(fā)送Put、delete等方式請求,無需Filter。
request.getMethod()?就直接獲取到的PUT、DELETE。
4.rest優(yōu)化使用,與上面等同。
// @RequestMapping(value = "/user",method = RequestMethod.GET)@GetMapping("/user")public String getUser(){return "GET-張三";}// @RequestMapping(value = "/user",method = RequestMethod.POST)@PostMapping("/user")public String saveUser(){return "POST-張三";}// @RequestMapping(value = "/user",method = RequestMethod.PUT)@PutMapping("/user")public String putUser(){return "PUT-張三";}@DeleteMapping("/user") // @RequestMapping(value = "/user",method = RequestMethod.DELETE)public String deleteUser(){return "DELETE-張三";}二、請求映射原理
1.本質(zhì)還是繼承自原生的HttpServlet
(1)繼承關(guān)系
(2).doGet、doPost等請求是在FrameworkServlet實現(xiàn)的
@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {processRequest(request, response); }@Override protected final void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {processRequest(request, response); }@Override protected final void doPut(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {processRequest(request, response); }@Override protected final void doDelete(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {processRequest(request, response); }?
(3).doGet最終會調(diào)用FrameworkServlet的processRequest方法
processRequest調(diào)用FrameworkServlet的doService,FrameworkServlet的doService并沒有實現(xiàn),是一個抽象方法。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {long startTime = System.currentTimeMillis();Throwable failureCause = null;LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());initContextHolders(request, localeContext, requestAttributes);try {doService(request, response);}catch (ServletException | IOException ex) {failureCause = ex;throw ex;}catch (Throwable ex) {failureCause = ex;throw new NestedServletException("Request processing failed", ex);}finally {resetContextHolders(request, previousLocaleContext, previousAttributes);if (requestAttributes != null) {requestAttributes.requestCompleted();}logResult(request, response, failureCause, asyncManager);publishRequestHandledEvent(request, response, startTime, failureCause);} }(4)DispatcherServlet有最終的doService實現(xiàn)
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {logRequest(request);// Keep a snapshot of the request attributes in case of an include,// to be able to restore the original attributes after the include.Map<String, Object> attributesSnapshot = null;if (WebUtils.isIncludeRequest(request)) {attributesSnapshot = new HashMap<>();Enumeration<?> attrNames = request.getAttributeNames();while (attrNames.hasMoreElements()) {String attrName = (String) attrNames.nextElement();if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {attributesSnapshot.put(attrName, request.getAttribute(attrName));}}}// Make framework objects available to handlers and view objects.request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());if (this.flashMapManager != null) {FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);if (inputFlashMap != null) {request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));}request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);}RequestPath requestPath = null;if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {requestPath = ServletRequestPathUtils.parseAndCache(request);}try {doDispatch(request, response);}finally {if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Restore the original attribute snapshot, in case of an include.if (attributesSnapshot != null) {restoreAttributesAfterInclude(request, attributesSnapshot);}}if (requestPath != null) {ServletRequestPathUtils.clearParsedRequestPath(request);}} }(5)DispatcherServlet有doDispatch方法(關(guān)鍵)
2.關(guān)鍵方法-DispatcherServlet的doDispatch方法
(1)doDispatch方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;// 文件上傳請求默認(rèn)是falseboolean multipartRequestParsed = false;// 請求是否是異步,異步管理器WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {// 檢查是否是文件上傳請求processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request. 找到當(dāng)前請求使用哪個Handler(Controller的方法)處理mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}} }(2)getHandler方法
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// HandlerMapping:處理器映射。/xxx->>xxxxif (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null; }(3)HandlerMapping
WebcomePageHandlerMapping:SpringBoot自動配置歡迎頁,訪問 /能訪問到index.html。
RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射規(guī)則。
所有的請求映射都在HandlerMapping中。
?
?
請求進(jìn)來,挨個嘗試所有的HandlerMapping看是否有請求信息。
* 如果有就找到這個請求對應(yīng)的handler
* 如果沒有就循環(huán)查找下一個 HandlerMapping
3.容器中注冊的HandlerMapping
HandlerMapping都是在org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration中注冊的
// RequestMappingHandlerMapping用于解析所有標(biāo)注@RequestMapping的請求 @Bean @Primary @Override public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {// Must be @Primary for MvcUriComponentsBuilder to workreturn super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,resourceUrlProvider); } // 用于解析歡迎頁的請求 @Bean public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),this.mvcProperties.getStaticPathPattern());welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());return welcomePageHandlerMapping; }4.自定義HandlerMapping
我們自定義的映射處理,也可以自己給容器中放HandlerMapping
(有興趣可以自行百度學(xué)習(xí),此處略)
總結(jié)
以上是生活随笔為你收集整理的springBoot-springMVC请求处理原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot-自动配置原理
- 下一篇: springboot-springmvc