SpringMVC工作流程

打雜匠發表於2018-12-11

一、springMVC是什麼?

​ springMVC是一個MVC的開源框架,springMVC就相當於是Struts2加上sring的整合,那麼springMVC和spring是什麼樣的關係呢?在百度百科上有一個很好的解釋:springMVC是spring的一個後續產品,其實就是spring在原有基礎上,又提供了web應用的MVC模組,可以簡單的把springMVC理解為是spring的一個模組(類似AOP,IOC這樣的模組),網路上經常會說springMVC和spring無縫整合,其實springMVC就是spring的一個子模組,所以根本不需要同spring進行整合。

​ 工作原理圖:

SpringMVC工作流程

SpringMVC工作流程

可以看到這裡分為大致11個步驟,這11個步驟的任務是這樣的:

​ 1、使用者傳送請求至前端控制器DispatcherServlet。

​ 2、 DispatcherServlet收到請求呼叫HandlerMapping處理器對映器。

​ 3、 處理器對映器找到具體的處理器(可以根據xml配置、註解進行查詢),生成處理器物件及處理器攔截器(如果有則生成)一併返回給DispatcherServlet。

​ 4、DispatcherServlet呼叫HandlerAdapter處理器介面卡。

​ 5、 HandlerAdapter經過適配呼叫具體的處理器(Controller,也叫後端控制器)。

​ 6、Controller執行完成返回ModelAndView。

​ 7、 HandlerAdapter將controller執行結果ModelAndView返回給DispatcherServlet。

​ 8、 DispatcherServlet將ModelAndView傳給ViewReslover檢視解析器。

​ 9、 ViewReslover解析後返回具體View,這個view不是完整的,僅僅是一個頁面(檢視)名字,且沒有字尾名。

​ 10、DispatcherServlet根據View進行渲染檢視(即將模型資料填充至檢視中)。

​ 11、 DispatcherServlet響應使用者。

圖中可以看到,DispatcherServlet(前端控制器)佔據了很大的一部分,事實也是這樣,springMVC中,DispatcherServlet是他的核心。

接下來再看一下springMVC元件及其核心步驟的說明:

​ 黃色字型和綠色字型是對元件作用的說明,其中黃色字型說明的元件是框架提供,綠色字型說明的部分是要由工程師實現,紅色字型是核心架構的具體流程步驟

SpringMVC工作流程

二、配置檔案的方式結合原始碼,瞭解工作流程

看完了原理,再來通過註釋的方式,寫一個Hello World,熟悉一下過程。

準備maven的web工程

SpringMVC工作流程

maven需要的相關依賴,springMVC需要的spring-webmvc,日誌相關的slf4j-log4j12,JSP相關的jstl、servlet-api、jsp-api。

配置web.xml

因為DispatcherServlet本身就是一個Servlet,所以要在web.xml配置:

<servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
複製程式碼

建立springmvc配置檔案

前端控制器要協調各個元件幹活,所以就要依賴於配置檔案,SpringMVC要讀取的配置檔案是要是自己手動建立,怎麼建立?配置檔案應該怎麼命名?先來到DispatcherServlet的原始碼中:

SpringMVC工作流程

可以看到DispatcherServlet繼承了FrameworkServlet,FrameworkServlet是Spring web框架的基本servlet。為整合提供了基於javabean的整體解決方案中的Spring應用程式上下文。FrameworkServlet註釋中有這樣一段描述:

SpringMVC工作流程

大致意思就是,預設的SpringMVC配置檔名字是“servlet-name”-servlet.xml, 而這個“servlet-name”就是在web.xml中配置的的名字,這裡就是springmvc,當然你也可以起其他名字。然後讀取的預設位置是在/WEB-INF/“servlet-name”-servlet.xml,所以這裡要在WEB-INF下面建立的是名叫springmvc-servlet.xml的配置檔案,因為他和Spring都是一個體系內的,所以可以直接建立spring的配置檔案模板:

SpringMVC工作流程

​ 現在有了配置檔案,那麼改在配置檔案中配置什麼東西?可以回到SpringMVC工作流程圖看到,在springMVC配置檔案中起碼要配置,對映器HandlerMapping、介面卡HandelAdapter、處理器Handel(Controller)、檢視解析器ViewResolver。View的渲染不是SpringMVC來完成,所以這裡不需要配置。

配置對映器

​ 先來配置HandlerMapping,找到HandlerMapping,他是一個介面,所以要配置的是HandlerMapping的實現類,找到HandlerMapping原始碼,檢視他的繼承樹:

SpringMVC工作流程

不看已經過時的(這裡Spring基於4.3的一個版本),我們要通過請求中的url和HandlerMapping找到處理器,所以使用BeanNameUrlHandlerMapping,類名中的BeanName也就是要找的處理器,在註釋中也有說明,如果url是/foo ,對應的處理器名字也是/foo,假設我們等等要訪問的url是localhost:8080/hello.do,那麼後面我們再寫 處理器中的程式碼的名字也是hello.do.

SpringMVC工作流程

然後再springmvc-servler.xml中配置對映器:

<!--HandelMapping-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
複製程式碼

配置介面卡

​ 對映器處理完後,返回執行鏈,DispatcherServlet去呼叫處理器,處理器實現本身就就是多元化的,要根據處理器的具體實現,通過介面卡(處理器包裝後)呼叫處理器,我們先找到HandlerAdapter,看看這個介面的繼承樹:

SpringMVC工作流程

有一個介面卡SimpleControllerHandlerAdapter,能夠幫我們呼叫處理器(也可以叫Controller)中的方法,

那麼他是怎麼呼叫處理器方法?

SpringMVC工作流程

他是通過判斷你的處理器是否屬於Controller的型別,是則返回true。那麼Controller又是什麼呢?檢視原始碼發現Controller是一個介面,也就意味著,我們寫處理器部分的程式碼要實現這一個介面。

SpringMVC工作流程

這個介面中就一個方法,也就是處理器方法,這個方法等同與Servlet中的doGet/doPost方法。

我們呼叫處理器方法的前提是實現Controller介面,在交給介面卡,介面卡判斷完之後,處理器(handler)在呼叫Controller介面中的處理器方法,所以最終呼叫處理器方法返回ModelAndView的是介面卡的handler方法:

SpringMVC工作流程

接下來我們要做的事就是在springmvc-servlet.xml配置這個SimpleControllerHandlerAdapter:

<!--SimpleControllerHandlerAdapter-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
複製程式碼

建立處理器

配置好介面卡,按照流程圖接下來搞配置處理器,前面說提到處理器部分是我們自己來實現的。

我們在專案中建立一個一個包com.ljm.springmvc.controller,然後在包中建立處理器HelloController。這裡不叫包名類名不叫Handler而叫controller,至於為什麼,可能是因為他要實現一個介面,這介面叫Controller。然後重寫Controller中的方法:

package com.ljm.springmvc.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return null;
    }
}
複製程式碼

配置處理器

因為我們自己建立了處理器和Spring沒啥關係,要想有有點什麼關係,就要把它配置到springmvc-servlet.xml中,處理器有很多個,那麼就要給他配上id或者name,這裡給他配置name,前面在配置對映器時提到,這個名字是我們要訪問的url所以,不能隨便配:

<!--處理器,注意/不能丟-->
<bean name="/hello.do" class="com.ljm.springmvc.controller.HelloController" />
複製程式碼

實現處理器

建立配置好處理器後,就要開始實現處理器中的業務,該呼叫哪個service層就呼叫哪個service,然後把結果響應到客戶端,這裡響應回去的是一個ModelAndView,這裡不實現service和dao層,寫一個死資料,不管三七二十一,先建立一個ModelAndView物件用於返回,

SpringMVC工作流程

這裡可以看到modelAndView有一些add方法:addObject(),根據引數可以聯想到request的get/set Attribute方法,先設定一對key/value;但這時候只設定了資料,沒有告訴他往哪個頁面返回,所以這裡還有一個操作,叫設定檢視名稱,使用的是modelAndView的setViewName方法,但這裡引數僅僅是設定要返回到的檢視名稱(邏輯檢視),且不帶字尾名,這裡我們要返回到index.jsp,所以引數是index;所以處理器的程式碼實現已經完成了:

public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","spring MVC : Hello World!");
        modelAndView.setViewName("index");
        return modelAndView;
    }
}
複製程式碼

配置檢視解析器

檢視解析器:ViewResolver是框架提供的,所以直接配置到springmvc-servlet.xml就行。

ViewResolver也是個介面,檢視繼承樹,

SpringMVC工作流程

其中有一個InternalResourceViewResolver,根據註釋我們可以看到我們要用的就是他InternalResourceViewResolver繼承了UrlBasedViewResolver,根據UrlBasedViewResolver註釋的提示,我們可以看到檢視解析器正確的配置姿勢:

SpringMVC工作流程

我們這裡的配置就應該是:

<!--檢視解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
    <!--配置路徑:返回頁面在webapp下,所以配置/-->
    <property name="prefix" value="/" />
    <!--配置字尾名-->
    <property name="suffix" value=".jsp" />
    <!--viewname已經在處理器配置過了-->
</bean>
複製程式碼

頁面接收返回引數

<html>
<body>
<h2>${msg}</h2>
</body>
</html>
複製程式碼

新增日誌

新增log4j.properties 到resources

log4j.rootLogger=DEBUG,A1
log4j.logger.org.apache=DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
複製程式碼

測試

SpringMVC工作流程

相關文章