首頁>技術>

這次來學習一下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

————————————————

最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 想玩分散式,微服務?必備docker