溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何進行SpringMVC的DispatcherServlet源碼分析

發(fā)布時間:2021-11-09 17:15:54 來源:億速云 閱讀:165 作者:柒染 欄目:大數(shù)據(jù)

本篇文章為大家展示了如何進行SpringMVC的DispatcherServlet源碼分析,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

DispatcherServlet源碼分析

SpringMVC核心就是DispatcherServlet,所有得請求都會轉(zhuǎn)發(fā)到DispatcherServlet,然后再通過DispatcherServlet執(zhí)行具體得控制層(Handler)返回ModelAndView給客戶端視圖展示。

// 3. 將我們的DispatcherServlet 注入到 serlvet容器中ServletRegistration.Dynamic dynamic = servletContext.addServlet("dispatcher", new DispatcherServlet(app));// 4.填寫url路徑映射dynamic.addMapping("/");

DispatcherServlet其實就是一個Servlet類,無非就是包裝一層,通過url能夠映射找到我們得SpringMvc中定義得請求方法。

源代碼分析:

  1. 類的集成關(guān)系

DispatcherServlet繼承FrameworkServlet繼承HttpServlet

面向基本上思想 重寫 先走父類 ,在走子類。

得出答案:先看HttpServlet在找到我們最后的子類

如何進行SpringMVC的DispatcherServlet源碼分析

如何進行SpringMVC的DispatcherServlet源碼分析

protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.processRequest(request, response);
}

如何進行SpringMVC的DispatcherServlet源碼分析

如何進行SpringMVC的DispatcherServlet源碼分析

DispatcherServlet與Servlet關(guān)系

關(guān)系:DispatcherServlet繼承FrameworkServlet繼承HttpServlet

流程執(zhí)行關(guān)系:

HttpServlet service方法 判斷請求方法的類型

FrameworkServlet doService

DispatcherServlet doService

DispatcherServlet的初始化

在servlet初始化階段會調(diào)用其init方法,所以我們首先要查看在DispatcherServlet中是否重寫了init方法。我們在其父類HttpServletBean中找到該方法

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
 ....
    public final void init() throws ServletException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Initializing servlet '" + this.getServletName() + "'");
        }
        //解析init-param并封裝至pvs中PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                //將當(dāng)前的servlet類轉(zhuǎn)換為一個BeanWrapper,從而能夠以Spring的方式來對init-param的值進行注入BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
                //注冊自定義屬性編輯器,一旦遇到Resource類型的屬性會使用ResourceEditor進行解析bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
                //空實現(xiàn),留給子類覆蓋this.initBeanWrapper(bw);
                //屬性注入bw.setPropertyValues(pvs, true);
            } catch (BeansException var4) {
                if (this.logger.isErrorEnabled()) {
                    this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
                }

                throw var4;
            }
        }
        //留給子類擴展this.initServletBean();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");
        }

    }
....
}

DispatcherServlet的初始化過程主要是通過將當(dāng)前的Servlet類型實例轉(zhuǎn)換為BeanWrapper類型實例,以便使用Spring中提供的注入功能進行對應(yīng)屬性的注入。

我們看下servletBean的初始化,HttpServletBean其父類FrameworkServlet覆蓋了它的initServletBean函數(shù),如下:

如何進行SpringMVC的DispatcherServlet源碼分析

protected final void initServletBean() throws ServletException {
    this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
        this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");
    }
    //計時器,統(tǒng)計初始化的執(zhí)行時間long startTime = System.currentTimeMillis();

    try {
        //關(guān)鍵的初始化邏輯委托給了這個方法this.webApplicationContext = this.initWebApplicationContext();
        //設(shè)計為子類覆蓋this.initFrameworkServlet();
    } catch (RuntimeException | ServletException var5) {
        this.logger.error("Context initialization failed", var5);
        throw var5;
    }

    if (this.logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms");
    }

}

WebApplicationContext 的初始化

initWebApplicationContext函數(shù)主要工作就是創(chuàng)建或者刷新WebApplicationContext 實例并對servlet功能所使用的變量進行初始化

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
    WebApplicationContext wac = null;
    if (this.webApplicationContext != null) {
        //context實例在構(gòu)造函數(shù)中被注入wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
            if (!cwac.isActive()) {
                if (cwac.getParent() == null) {
                    cwac.setParent(rootContext);
                }
                //刷新上下文環(huán)境this.configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }

    if (wac == null) {
        //根據(jù)contextAttribute屬性加載webApplicationContextwac = this.findWebApplicationContext();
    }

    if (wac == null) {
        wac = this.createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
        this.onRefresh(wac);
    }

    if (this.publishContext) {
        String attrName = this.getServletContextAttributeName();
        this.getServletContext().setAttribute(attrName, wac);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");
        }
    }

    return wac;
}

刷新方法onRefresh

protected void onRefresh(ApplicationContext context) {
    this.initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {

                  initMultipartResolver(context); //初始化上傳文件解析器(或者是多部分請求解析器)

                  initLocaleResolver(context);//初始化本地化解析器

                  initThemeResolver(context);//初始化主題解析器

                  initHandlerMappings(context);//初始化處理器映射器

                  initHandlerAdapters(context);//初始化處理器適配器

                  initHandlerExceptionResolvers(context);//初始化處理器異常解析器

                  initRequestToViewNameTranslator(context);//初始化請求到視圖名翻譯器

                  initViewResolvers(context);//初始化視圖解析器

                  initFlashMapManager(context);//初始化重定向數(shù)據(jù)管理器

DispatcherServlet的邏輯處理

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  ...
    try {
        try {
     ...
                //通過url路徑地址去查找控制層類方法,如果沒有找到的化,直接返回404mappedHandler = this.getHandler(processedRequest);    
    ....
                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
               ....
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
          ....
                mappedHandler.applyPostHandle(processedRequest, response, mv);
....
}

SpringMVC源碼之定位Handler原理

如何進行SpringMVC的DispatcherServlet源碼分析

private List<HandlerMapping> handlerMappings;

如何進行SpringMVC的DispatcherServlet源碼分析

mappedHandler = this.getHandler(processedRequest);

如何進行SpringMVC的DispatcherServlet源碼分析

如何進行SpringMVC的DispatcherServlet源碼分析

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

如何進行SpringMVC的DispatcherServlet源碼分析

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}

如何進行SpringMVC的DispatcherServlet源碼分析

/**
 * 請求方法前置攔截,如果返回true 表示會執(zhí)行到目標方法(請求方法) 如果返回false的情況下 則不會執(zhí)行目標方法。
 */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String token = request.getParameter("token");
    System.out.println(">>>>token<<<<:" + token);
    if (StringUtils.isEmpty(token)) {
        response.setStatus(500);
        response.getWriter().print(" token is null");
        return false;
    }    // 執(zhí)行我們的請求方法return true;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

執(zhí)行目標方法:

@RequestMapping("/pageIndex")
public String pageIndex() {
    System.out.println(">>>pageIndex<<<<");
    return "pageIndex";
}
mappedHandler.applyPostHandle(processedRequest, response, mv);

如何進行SpringMVC的DispatcherServlet源碼分析

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("<<<postHandle>>>>");    // 請求之后執(zhí)行。}

DispatcherServlet源碼流程分析

1.執(zhí)行doDispatch

2.調(diào)用getHandler方法獲取請求目標的方法  也就是  請求url映射路徑對應(yīng)的控制層具體的方法

handlerMappings的作用查找控制器位置,比如xml和注解方式。

3.調(diào)用getHandlerAdapter獲取控制層適配器 RequestMappingHandlerAdapter

4.執(zhí)行攔截器前置方法 preHandle() 如果返回為true的話

5.執(zhí)行實際請求目標方法 返回modeAndView對象

6.執(zhí)行攔截器PostHandle()方法

7.設(shè)置渲染視圖層內(nèi)容

8.執(zhí)行攔截器afterCompletion方

SpringMVC控制層容器初始化

  1. HttpServletBean  init ()方法

  2. FrameworkServlet initServletBean方法→  initWebApplicationContext();

  3. DispatcherServlet onRefresh方法→  initStrategies()方法

如何進行SpringMVC的DispatcherServlet源碼分析

protected void onRefresh(ApplicationContext context) {
    this.initStrategies(context);
}

當(dāng)我們servlet容器初始化的時候初始化

this.initHandlerMappings(context);

如何進行SpringMVC的DispatcherServlet源碼分析

上述內(nèi)容就是如何進行SpringMVC的DispatcherServlet源碼分析,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI