這次來學習一下SpringMVC的原始碼.
對於常見的專案架構模式,比如大名鼎鼎的SSM(SpringMVC,Spring,Mybatis)框架.
SpringMVC ->web層(Controller層)
Spring ->service層
mybatis ->dao層
從SpringMVC層面上講,他的構成如下:
Model ->資料
View ->檢視
Controller ->業務
經過上面的分層,使得資料,檢視(展示效果),業務邏輯進行分離,每一層的變化可以不影響其他層,增加程式的可維護性和可擴充套件性。
瀏覽器發出使用者請求,處於web.xml中的dispacherServlet前端控制器進行接收,此時這個前端控制器並不處理請求.而是將使用者傳送的url請求轉發到HandleMapping處理器對映器第二步:HandlerMapping根據請求路徑找到相對應的HandlerAdapter處理器介面卡(處理器介面卡就是那些攔截器或者是controller)第三步:HandlerAdapter處理器介面卡,可以處理一些功能請求,返回一個ModelAndView物件(包括模型資料/邏輯檢視名)第四步:ViewResolver檢視解析器,先根據 ModelAndView中設定的view解析具體檢視第五步:然後再將Model模型中的資料渲染到View中
下面我們實際在專案中進行操作
一丶建立一個帶有web.xml的maven專案
二丶首先自己寫一個類繼承HttpServlet類並重寫它的doGet,doPost方法
package com.spring.mvc.config;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/** * @Author: XiaoZhe * @Description: * @Date: Created in 17:39 2019/12/16 */public class MyDispatchServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("這是呼叫了doGet方法"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("這是呼叫了doPost方法"); }}
三丶修改web.xml檔案
四丶 啟動專案,在位址列輸入專案地址並回車
可以看到控制檯列印輸出了我們定義的話
這是呼叫了doGet方法這是呼叫了doGet方法
五丶建立幾個註解@Controller,@RequestMapping
用過SpringMVC框架的人都知道在類上打了@Controller註解的才能被認作是一個Controller,而打了@RequestMapping才能被請求對映。
@MyController
package com.spring.mvc.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * @Author: ZhengZhe * @Description: 作用於class上的功能類似於Spring的@Controller註解 * @Date: Created in 10:34 2019/12/17 */@Target(ElementType.TYPE)//標識此註解只能作用在類上面@Retention(RetentionPolicy.RUNTIME)//標識此註解一直存活,可被反射獲取public @interface MyController {}
@MyRequestMapping
package com.spring.mvc.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * @Author: ZhengZhe * @Description: 作用於class或者method上的功能類似於Spring的@RequestMapping註解 * @Date: Created in 10:38 2019/12/17 */@Target({ElementType.TYPE,ElementType.METHOD})//標識此註解只能作用在類或者方法上面@Retention(RetentionPolicy.RUNTIME)//標識此註解一直存活,可被反射獲取public @interface MyRequestMapping { String value();//用來儲存對應的url , 網路請求路徑}
六丶DispatchServlet
DispatchServlet在MVC引導著非常強大的作用,網路中的請求傳到DispatchServlet中,由DispatchServlet進行擷取分析並傳到對應的由@Controller和@RequestMapping註解的類或方法中,使得網路請求能正確的請求到對應的資源上.
下面我們自定義一個DispatchServlet實現他所實現的功能
首先看一下原始碼中MVC做了什麼
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); }
從上面我們可以看到初始化方法的引數是ApplicationContext,這個是IOC的初始化容器,我之前的部落格中解析過IOC的原始碼,不懂的可以去裡面解讀.
initStrategies方法的目的就是從容器中獲取已經解析出來的bean資源,並獲取其帶有@Controller和@RequestMapping註解的bean資源.
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (this.logger.isDebugEnabled()) { String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]"); } Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap(); Enumeration attrNames = request.getAttributeNames(); label108: while(true) { String attrName; do { if (!attrNames.hasMoreElements()) { break label108; } attrName = (String)attrNames.nextElement(); } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet")); attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource()); 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 { this.doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) { this.restoreAttributesAfterInclude(request, attributesSnapshot); } } }
doService方法其實目的就是解析使用者的請求路徑,根據請求路徑找到對應類和方法,使用反射呼叫.
七丶MyDispatchServlet(自定義前端控制器)
我們自己寫程式碼來實現對應的init方法和Service方法的功能.
package com.spring.mvc.controller;import com.spring.mvc.annotation.MyController;import com.spring.mvc.annotation.MyRequestMapping;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.util.HashMap;import java.util.Map;/** * @Author: ZhengZhe * @Description: * @Date: Created in 15:07 2019/12/17 */@MyController@MyRequestMapping("/hello")public class ControllerTest { @MyRequestMapping("/world") public void helloworld(){ System.out.println("自定義MVC測試成功~ ,現在時間是"+System.currentTimeMillis()); }}
檢視控制檯輸出:
資訊: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.[2019-12-17 03:33:26,276] Artifact mvc:war exploded: Artifact is deployed successfully[2019-12-17 03:33:26,276] Artifact mvc:war exploded: Deploy took 565 milliseconds自定義MVC測試成功~ ,現在時間是1576568012163
————————————————