【计算机是如何通信 四】Web服务器/Severlet/DispatcherServlet/Controller
web服務器
web服務器從字面上理解:是用于存儲web網站程序的服務器
WebServer做了哪些事兒?
當客戶端(可以是瀏覽器、也可以是postman等工具)發送請求給服務器后,服務器會調用并執行對應的邏輯代碼進行請求處理。邏輯代碼是由程序員自己編寫然后放進服務器進行運行,然后講運行后的結果返回給客戶端。那么服務器需要處理兩件事情:
- 1、處理網絡連接
- 2、找到我要尋找的資源(無論是程序還是其他網頁資源)
- 3、將找到的東西返回給用戶
處理網絡連接這里就不重點闡述,默認他能夠接受并處理http協議棧的請求。
這里的資源在網上一般叫web資源:
web資源按照實現的技術和呈現的效果的不同,又分為靜態資源和動態資源兩種
靜態資源:
html,css,js,txt,mp4視頻,jpg圖片…
動態資源:
JSP頁面,Servlet程序
對于靜態資源比較好辦,直接返回就好了。但是對于動態資源比如代碼如java代碼,我必須找到這個代碼的class文件,還需要根據請求找到代碼從哪里開始運行。在本地我們直接運行main方法即可啟動程序執行java代碼,但是在web應用中我們不可能每次來個請求,我都去點一下main方法去執行相應的代碼。
????????所以聰明的程序員們寫了一個根據用戶請求來調用相應邏輯代碼的容器(對象),執行完相應邏輯后,java主線程并沒有結束,而是在那里等待用戶請求。這個容器就叫做服務器,每個請求的過程就是把程序員自己些的代碼放入這個服務器執行并相應的過程。可是這個容器要能跑java代碼的話,那也就必須要按照jvm規范去實現。那么服務器又如何知道去應該調用那個類和哪個方法來處呢?也就是服務器怎么才能認識程序員執行的代碼。由于java的多態的性質,程序員寫的代碼只要和服務器共同實現一個接口。只要將程序員寫的這個對象傳入服務器,就可以直接執行這個代碼。而這個技術就叫Servlet,只要程序員的請求都去實現servlet接口,請求的時候傳給服務器容器,就去找這個實例對象中的一個service方法,再由service方法根據請求信息去找到對應的業務邏輯執行。而如tomcat服務器就是一種這樣的服務器實現。
用戶訪問頁面過程:
下面看個具體實現?
Spring MVC (DispatcherServlet)請求處理流程
?從servlet的doService開始:
DispatcherServlet中的 doService();
/*** Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}* for the actual dispatching.*//*翻譯:暴露(個人理解是講參數取出,或者傳入新的參數)請求參數并指派(傳遞)給 doDispatch() 方法目的: 為了實際調度(處理請求)個人理解: 即對request進行預加工,設置默認屬性,然后將 request,response 傳入 doDispatch() 方法,對請求進行處理*/@Overrideprotected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {if (logger.isDebugEnabled()) {String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +" processing " + request.getMethod() + " request for [" + getRequestUri(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<String, Object>();Enumeration<?> attrNames = request.getAttributeNames();while (attrNames.hasMoreElements()) {String attrName = (String) attrNames.nextElement();if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {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());//mark ... 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);try {//springMVC處理請求的核心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);}}}}doDispatch(request, response) 方法:
/*** Process the actual dispatching to the handler.* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters* to find the first that supports the handler class.* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers* themselves to decide which methods are acceptable.* @param request current HTTP request* @param response current HTTP response* @throws Exception in case of any kind of processing failure*//*大致翻譯: 所有http請求都是由這個方法處理,取決于 HandlerAdapters 或 handlers 決定哪個方法處理HandlerAdapter 將會通過查找 servlect 初始化后(安裝后)的 HandlerAdapters 列表,找到第一個滿足的 HandlerAdapter所有請求處理 handler 將會通過 HandlerMappings 查找到*/protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;//mark WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {// 視圖類ModelAndView mv = null;// 異常類Exception dispatchException = null;try {//檢查request類型 判斷 http請求是否 multipart/form-data 類型processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request. // 獲取 匹配當前請求的 HandlerExecutionChain類 的 handlermappedHandler = getHandler(processedRequest);// mappedHandler.getHandler() 返回值是個Object 類型 通過打斷點證實是個 HandlerMethod 類 /*public class HandlerMethod {/** Logger that is available to subclasses */protected final Log logger = LogFactory.getLog(HandlerMethod.class);// controller 的類private final Object bean;// spring 的beanFactoryprivate final BeanFactory beanFactory;//匹配url(@RequestMapping) 處理的method 方法private final Method method;//mark.... private final Method bridgedMethod;//參數private final MethodParameter[] parameters;}*/if (mappedHandler == null || mappedHandler.getHandler() == null) {//未獲取到請求即返回noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request./*(適配器模式)通過遍歷 handlerAdapters 來查找 supports 這個handler 的 handlerAdapter 類, 返回第一個匹配,查找不到即拋異常HandlerAdapter 的 supports 方法源碼在 AbstractHandlerMethodAdapter(模板方法模式)控制子類的 supportsInternal 方法在 RequestMappingHandlerAdapter 中 supportsInternal 方法中 寫死了return true 此處 supportsInternal 方法可以用來 自己創建 HandlerAdapter 與 自己定義 HandlerMethod 處理對象的格式 判斷 handler 是否是 HandlerMethod 類型 AbstractHandlerMethodAdapter 類中的 supports() 方法:@Overridepublic final boolean supports(Object handler) {return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));}此處也可以知道 handler 的類型 是 HandlerMethod*/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 (logger.isDebugEnabled()) {logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}/*applyPreHandle 方法中調用了 HandlerExecutionChain 類中的 HandlerInterceptor 攔截器對請求進行攔截處理(preHandle方法)HandlerInterceptor 可以通過配置 <mvc:interceptors> 攔截器 </mvc:interceptors> 對請求進行攔截HandlerInterceptor 的 preHandle() 方法*/if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.// 翻譯: 真正調用請求方法主體 即controller 中處理請求的那個 method 也是 HandlerMethod 中的method 屬性// handler 方法中還有很多邏輯// 返回視圖 @ResponseBody 情況下 mv 為null, 但是返回值已經存到response中mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(request, mv);// 調用 HandlerInterceptor 攔截器的 postHandle 方法mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}//返回視圖,瀏覽器上展示效果processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {/*調用攔截器的 afterCompletion() 方法*/triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Error err) {triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);}}}}getHandler(processedRequest) 方法 返回 HandlerExecutionChain 對象,? ? ? ? ? ? getHandlerInternal 通過獲取request 的url 請求 ,從urlMap 找到RequestMapping的Method 封裝成 handlder( HandlerMethod 類 ),這就找到了我們要執行的類的方法了
/*** Look up a handler for the given request, falling back to the default* handler if no specific one is found.* @param request current HTTP request* @return the corresponding handler instance, or the default handler* @see #getHandlerInternal*/@Overridepublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// 通過 request 獲取 handler (HandlerMethod 類型 )/*getHandlerInternal 通過獲取request 的url 請求 ,從urlMap 找到RequestMapping的Method 封裝成 handlder( HandlerMethod 類 )*/Object handler = getHandlerInternal(request);if (handler == null) {handler = getDefaultHandler();}if (handler == null) {return null;}// Bean name or resolved handler?if (handler instanceof String) {String handlerName = (String) handler;handler = getApplicationContext().getBean(handlerName);}/*將 handlder( HandlerMethod 類 ) 與 request 方法 并添加攔截器數組封裝成 HandlerExecutionChain 的對象返回代碼塊:protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));chain.addInterceptors(getAdaptedInterceptors()); // url請求String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);//找到匹配該請求的 MappedInterceptor 攔截器for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {chain.addInterceptor(mappedInterceptor.getInterceptor());}}return chain;}*/return getHandlerExecutionChain(handler, request);}getHandlerAdapter(mappedHandler.getHandler()) 方法,返回 HandlerAdapter 對象
?
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException) 方法處理視圖并跳轉到頁面 (處理異常HandlerExceptionResolvers 出現的地方)
/*** Handle the result of handler selection and handler invocation, which is* either a ModelAndView or an Exception to be resolved to a ModelAndView.*/private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {boolean errorView = false;if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);/*處理異常,異常HandlerExceptionResolvers處理器*/mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// Did the handler return a view to render?if (mv != null && !mv.wasCleared()) {/*跳轉頁面 ViewResolver 用來解析頁面*/render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isDebugEnabled()) {logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +"': assuming HandlerAdapter completed request handling");}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}if (mappedHandler != null) {// 調用了 攔截器 HandlerInterceptor 的afterCompletion() 方法mappedHandler.triggerAfterCompletion(request, response, null);}}Controller
?@Controller //告訴編譯器當前類為一個控制器@RequestMapping("/hello"),實現url和控制器之間的映射。severlet通過url找到控制器,從而實現執行我們類里面的某個方法
代碼解析參考:
Spring MVC (DispatcherServlet)請求處理流程_zjs40的博客-CSDN博客
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的【计算机是如何通信 四】Web服务器/Severlet/DispatcherServlet/Controller的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中间件系列「三」netty之NIO基础
- 下一篇: 【身份认证及权限控制一】单点登录