1、概述 Spring MVC是Spring Framework的Web開發部分,是基於Java實現MVC的輕量級Web框架。 官方文檔:https://docs.spring.io/spring-framework/docs/4.3.24.RELEASE/spring-framework-refe ...
1、概述
Spring MVC是Spring Framework的Web開發部分,是基於Java實現MVC的輕量級Web框架。
- 官方文檔:https://docs.spring.io/spring-framework/docs/4.3.24.RELEASE/spring-framework-reference/html/
- 中文官方文檔:https://www.w3cschool.cn/spring_mvc_documentation_linesh_translation/spring_mvc_documentation_linesh_translation-y2ud27r5.html
- 中文文檔下載地址:https://www.jb51.net/books/593599.html
為什麼要學習SpringMVC ?
Spring MVC的特點:
- 輕量級,簡單易學
- 高效 , 基於請求響應的MVC框架
- 與Spring相容性好,無縫結合
- 約定優於配置
- 功能強大:RESTful、數據驗證、格式化、本地化、主題等
- 簡潔靈活
Spring的web框架圍繞DispatcherServlet [ 調度Servlet ] 設計。
DispatcherServlet的作用是將請求分發到不同的Handler。從Spring 2.5開始,使用Java 5或者以上版本的用戶可以採用基於註解形式進行開發,十分簡潔;
正因為SpringMVC好 , 簡單 , 便捷 , 易學 , 天生和Spring無縫集成(可以使用IOC和AOP) , 使用約定優於配置 ,能夠進行簡單的junit測試 ,支持Restful風格 ,異常處理 ,本地化 ,國際化 ,數據驗證 ,類型轉換 ,攔截器 等等......所以我們要學習。
最重要的一點還是用的人多 , 使用的公司多 。
2、SpringMVC 核心組件
- 前端控制器:DispactherServlet
- 處理器映射器:HandlerMapping
- 處理器適配器:HandlerAdapter
- 處理器:Handler
- 視圖解析器:ViewResolver
組件介紹:
- DispactherServlet:用於接收所有的客戶端請求並將請求分發給合適的Handler(Controller)進行處理。在原生JavaWeb中,分發請求是由 Tomcat 根據 web.xml 來做的,SpringMVC使用 DispactherServlet 替代了這個功能。(不需要程式員開發)
- HandlerMapping:解析每個請求的URL,找到對應的 Handler。(不需要程式員開發)
- HandlerAdapter:適配器模式,適配調用具體的 Handler。(不需要程式員開發)
- Handler:Controller層組件,對客戶端請求進行邏輯處理,替代了原生JavaWeb中的 Servlet 所做的功能。(需要程式員手動開發)
- ViewResolver:將 SpringMVC 中的 邏輯視圖名 拼接成具體的視圖地址,簡化了視圖地址的編寫。(不需要程式員開發)
Spring MVC框架像許多其他MVC框架一樣, 以請求為驅動 , 圍繞一個中心Servlet分派請求及提供其他功能,DispatcherServlet是一個實際的Servlet (它繼承自HttpServlet 基類)。
3、SpringMVC 執行原理
簡要分析執行流程
- 當用戶通過瀏覽器發起一個HTTP請求,請求直接到前端控制器 DispatcherServlet;
- 前端控制器接收到請求以後調用處理器映射器HandlerMapping,處理器映射器根據請求的URL解析出URI,找到具體的Handler,並將它返回給前端控制器;
- 前端控制器調用處理器適配器HandlerAdapter去適配調用Handler;
- 處理器適配器會根據Handler去調用真正的處理器去處理請求,並且處理對應的業務邏輯;
- 當處理器處理完業務之後,會返回一個ModelAndView對象給處理器適配器,HandlerAdapter再將該對象返回給前端控制器;ModelAndView 中的 Model 是指 被Model層執行業務處理後返回的數據對象,View 是指 將要轉向的視圖信息。
- 前端控制器DispatcherServlet將返回的ModelAndView對象傳給視圖解析器ViewResolver進行解析,解析完成之後就會返回一個具體的視圖地址給前端控制器;(ViewResolver根據邏輯的View查找具體的View)
- 前端控制器DispatcherServlet轉向具體的視圖,併進行渲染;
- 渲染完成之後響應給用戶(瀏覽器顯示);
4、HelloSpringMVC(舉個實例)
4.1、配置版
-
新建一個Moudle , SpringMVC-02-HelloMVC , 添加web的支持!
-
確定導入了SpringMVC 的依賴!
-
配置web.xml , 註冊DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--註冊DispatcherServlet--> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--關聯一個springmvc的配置文件--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <!--啟動級別-1--> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
配置說明:
init-param
:當請求進入此servlet時,會攜帶的初始參數classpath:
與classpath*:
:前者指的是 當前項目的編譯路徑,後者代表的是 當前項目及其依賴jar包的所有編譯路徑load-on-startup
:啟動級別,當值為0或者大於0時,表示容器在應用啟動時就載入這個servlet,值越小,優先順序越高,當值是一個負數時或者沒有指定時,則指示容器在該servlet被選擇時才載入。url-pattern 為 ‘/’
:預設匹配,詳見 關於tomcat中servlet的url-pattern匹配規則
-
編寫SpringMVC 的 配置文件!名稱:spring-mvc.xml,添加處理器映射器、 處理器適配器、 視圖解析器
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- Handler映射器 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!-- Handler適配器 --> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!-- 視圖解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
-
編寫處理器 Handler ,實現Controller介面 或者 增加註解;需要返回一個ModelAndView,裝數據,封視圖
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 { ModelAndView mv = new ModelAndView(); mv.addObject("msg","HelloMVC"); mv.setViewName("hello"); return mv; } }
-
將編寫的 Handler 交給SpringIOC容器,註冊bean,添加到spring-mvc.xml中。
<!-- Handler --> <bean id="/hello" class="controller.HelloController"/>
-
編寫要跳轉的jsp頁面,顯示ModelandView存放的數據,以及正常頁面;
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>hello</title> </head> <body> ${msg} </body> </html>
-
配置Tomcat,啟動測試!
可能遇到的問題:訪問出現404,排查步驟:
-
查看控制台輸出,看一下是不是缺少了什麼jar包。
-
如果缺少jar包,就在項目結構中添加相應的lib依賴!
-
重啟Tomcat 即可解決!
4.2、註解版
-
新建一個Moudle,SpringMVC-03-Annotation ,添加web支持!
-
在pom.xml文件引入相關的依賴:主要有Spring框架核心庫、Spring MVC、Servlet、 JSTL等。在父項目中已經引入了!
-
配置web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
配置說明:在上面的配置版講過了,這裡一樣,就不說了。
-
添加Spring MVC配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd "> <!-- 組件掃描 --> <context:component-scan base-package="com.moondream.controller"/> <!-- 使用 default-servlet 處理靜態資源 --> <mvc:default-servlet-handler/> <!-- 使用mvc註解驅動 --> <mvc:annotation-driven/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
配置說明:
-
加入了兩個Xml命名空間
context
、mvc
-
<context:component-scan/>
:組件掃描命名空間 context 下的標簽,掃描指定包下所有被模板註解標註了的類,將其bean化並註冊到IOC中。
模板註解 即 @component 及其 組合註解(@Controller、@Configuration……),它們大都位於 org.springframework.stereotype 包下,抽象定義為 項目的某個模塊,比如 @Controller 代表 項目的 控制層,@Service 代表 項目的 服務層,@Configuration 代表 項目的 配置,@Component 代表 項目的 組件。
不想使用組件掃描,也可以將其手動一個個註冊成bean
<bean class="com.moondream.controller.HelloController"/>
-
<mvc:default-servlet-handler/>
:使用 default-servlet 來處理請求命名空間 mvc 下的標簽,將 匹配到的請求 轉發到 Tomcat 內建的 default servlet 中處理。
一般用來處理 靜態資源請求(.html、.css、.mp3、.jpg……),使用此標簽要註意 配置中多個HandlerMapping的優先順序問題,免得 非靜態資源請求 被匹配上 default servlet ,那樣會報 404 錯誤。
其本質是
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <!-- SimpleUrlHandlerMapping 通過urlMap來找相應的Handler --> <property name="urlMap"> <map> <entry key="/**" value="org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0"/> </map> </property> </bean> <bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/> <bean class="org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler"/>
-
<mvc:annotation-driven/>
:使用 mvc 註解驅動命名空間 mvc 下的標簽,使用註解的方式來處理請求。
其本質是
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> <!-- 使用高優先順序 --> <property name="order" value="0"/> </bean> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
(spring3.1之前使用的是 DefaultAnnotationHandlerMapping 與 AnnotationMethodHandlerAdapter )
(spring3.1之後替換成如上述所示)
註解方式處理請求的簡要流程:
1)Spring容器啟動,
RequestMappingHandlerMapping
在bean載入完成後,掃描出所有帶有@Controller註解的bean,進而遍歷出其中所有帶有@RequestMapping註解 的方法,將方法上@RequestMapping註解信息封裝成RequestMappingInfo
對象,其包含了請求路徑、請求方法、請求參數、請求頭等信息,將方法本身封裝成HandlerMethod
對象,並以RequestMappingInfo
為鍵,HandlerMethod
為值,存儲在一個Map結構中,以便於後續查找。2)當一個HTTP請求到達
DispatcherServlet
時,DispatcherServlet
會調用RequestMappingHandlerMapping
的getHandler()
方法來查找到相應的 Handler,這裡就是HandlerMethod
。3)如果找到了匹配的
HandlerMethod
,DispatcherServlet
就會根據其類型選擇合適的HandlerAdapter
(即RequestMappingHandlerAdapter
)來執行它,並返回結果;如果沒有找到匹配的HandlerMethod
,DispatcherServlet
就會拋出異常並使用下一個 HandlerMapping 來查找 Handler 或者返回404錯誤頁面。4)如果被封裝成
HandlerMethod
的方法 或者 其上的類 被@ResponseBody註解 標註,那麼HandlerMethod
的返回結果就是一個單純的數據字元串,會被當成響應體直接返回給客戶端瀏覽器,否則就是邏輯視圖名,會被視圖解析器解析後轉向具體視圖。詳見這篇文章的 RequestMappingHandlerMapping 部分:Spring MVC源碼解析——HandlerMapping(處理器映射器)
-
-
編寫Controller層,創建一個Java類:com.moondream.controller.HelloController , 註意編碼規範
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/HelloController") public class HelloController { //真實訪問地址 : 項目名/HelloController/hello @RequestMapping("/hello") public String sayHello(Model model){ //向模型中添加屬性msg與值,可以在JSP頁面中取出並渲染 model.addAttribute("msg","hello,SpringMVC"); // /WEB-INF/jsp/hello.jsp return "hello"; } }
註意:
- 標註在類上的 @RequestMapping,相當於前置要求,每個
RequestMappingInfo
在被封裝時,都會將類和對應方法上的 @RequestMapping 註解信息 整合註入自己。 - 被封裝成
HandlerMethod
的方法,返回值必須是String類型,方法參數會被框架進行數據綁定。如果想在訪問的請求上加數據,可以設置一個Model類型的參數,這個參數對象將由框架傳入,它會把自身被加入的數據封裝到訪問的請求上。不要設置ModelAndView類型的參數,它在HandlerMethod
中無效。 - 一個
RequestMappingInfo
匹配的請求範圍不能被另一個RequestMappingInfo
完全覆蓋,不然程式會報錯。簡單來說,你要保證一個RequestMappingInfo
至少有一類請求能匹配上。
- 標註在類上的 @RequestMapping,相當於前置要求,每個
-
創建視圖層
在WEB-INF/jsp目錄中創建hello.jsp , 視圖可以直接取出並展示從Controller帶回的信息;
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Hello</title> </head> <body> ${msg} <br/> <%out.print(request.getRequestURI());%> </body> </html>
-
配置Tomcat運行,開啟伺服器,訪問對應的請求路徑!
http://localhost:8080/spring03/HelloController/hello
運行成功!!!
4.3、小結
實現步驟:
- 新建一個web項目
- 導入相關jar包
- 編寫web.xml , 註冊DispatcherServlet
- 編寫springmvc配置文件
- 接下來就是去創建對應的控制類 , controller
- 最後完善前端視圖和controller之間的對應
- 測試運行調試.
使用springMVC必須配置的三大件:
- 處理器映射器、處理器適配器、視圖解析器
通常,我們只需要手動配置視圖解析器,而處理器映射器和處理器適配器只需要開啟註解驅動即可,省去了大段的xml配置。
不知道有沒有小伙伴註意到一個問題:
在上述註解版的案例中,我們通過命名空間mvc下的兩個標簽default-servlet-handler
和annotation-driven
,在SpringIOC中配置了多個HandlerMapping和HandlerAdapter,
那麼,當請求到達時,DispatcherServlet該使用哪個HandlerMapping和HandlerAdapter呢?
當IOC中有多個HandlerMapping時:
- DispatcherServlet會優先使用優先順序高的HandlerMapping(所有HandlerMapping中,都有一個order屬性,代表優先順序)
- 如果HandlerMapping優先順序一樣,DispatcherServlet會使用先註冊的
- 如果使用的HandlerMapping根據請求找不到Handler,DispatcherServlet會繼續使用下一優先順序的HandlerMapping繼續找,直到找到Handler
當IOC中有多個HandlerAdapter時:
- DispatcherServlet會遍歷所有註冊的HandlerAdapter,根據請求的Handler類型來選擇對應的HandlerAdapter
- 如果有多個HandlerAdapter都能夠支持當前請求的Handler類型,選擇第一個適配成功的HandlerAdapter來執行Handler,即選擇先註冊的HandlerAdapter