Spring-MVC
1.SpringMVC簡介
- SpringMVC概述
SpringMVC是一個基于Spring開發的MVC輕量級框架,Spring3.0后發布的組件,SpringMVC和Spring可以無縫整合,使用DispatcherServlet作為前端控制器,且內部提供了處理器映射器、處理器適配器、視圖解析器等組件,可以簡化JavaBean封裝,Json轉化、文件上傳等操作。
- SpringMVC快速入門
(1)導入Spring-webmvc坐標
(2)編寫一個控制器Controller,配置映射信息
(3)在web.xml中配置SpringMVC的前端控制器ServletDispatcher
<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><!--服務器啟動就創建--><load-on-startup>2</load-on-startup>
</servlet><servlet-mapping><servlet-name>DispatcherServlet</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
(4)創建springMVC的核心配置文件 spring-mvc.xml,并配置組件掃描web層
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/xmlSchema-instance"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 組件掃描web層 --><context:component-scan base-package="com.itheima.controller"/></beans>
- Controller中訪問容器中的Bean
配置Spring容器
在web.xml中配置ContextLoaderListener
<!--配置ContextLoaderListener-->
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applictionContext.xml</param-value>
</context-param>
<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
在Controller中使用@Autowire注入service
Controller層是在SpringMVC容器中,Service,Dao層是在Spring容器中
- SpringMVC關鍵組件淺析
組件的關系
SpringMVC的默認組件,SpringMVC 在前端控制器 DispatcherServlet加載時,就會進行初始化操作,在進行初始化時,就會加載SpringMVC默認指定的一些組件,這些默認組件配置在 DispatcherServlet.properties 文件中,該文件存在與spring-webmvc-5.3.7.jar包下的org\springframework\web\servlet\DispatcherServlet.properties
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrl
HandlerMapping,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHand
lerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResource
ViewResolver
配置組件代替默認組件,如果不想使用默認組件,可以將替代方案使用Spring Bean的方式進行配置,例如,在spring-mvc.xml中配置RequestMappingHandlerMapping
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
當我們在Spring容器中配置了HandlerMapping,則就不會在加載默認的HandlerMapping策略了,原理比較簡單,DispatcherServlet 在進行HandlerMapping初始化時,先從SpringMVC容器中找是否存在HandlerMapping,如果存在直接取出容器中的HandlerMapping,在存儲到 DispatcherServlet 中的handlerMappings集合中去。
2.SpringMVC的請求處理
- 請求映射路徑的配置
配置映射路徑,映射器處理器才能找到Controller的方法資源,目前主流映射路徑配置方式就是@RequestMapping
@RequestMapping 在類上使用,@RequestMapping 、@GetMapping、@PostMapping還可以使用在Controller類上,使用在類上后,該類所有方法都公用該@RequestMapping設置的屬性,訪問路徑則為類上的映射地址+方法上的映射地址
- 請求數據的接收
接收普通請求數據,當客戶端提交的數據是普通鍵值對形式時,直接使用同名形參接收即可
接收普通請求數據,當請求參數有特殊格式數據,如日期時
Date可以正常接收,因為Spring內置的類型解析器,可以識別的日期格式是 yyyy/MM/dd。如果想使用自定義的日期解析格式,使用@DateTimeFormat 指定日期格式
接收普通請求數據,當請求參數的名稱與方法參數名不一致時,可以使用@RequestParam注解進行標注
接收實體JavaBean屬性數據,單個JavaBean數據:提交的參數名稱只要與Java的屬性名一致,就可以進行自動封裝
接收數組或集合數據,客戶端傳遞多個同名參數時,可以使用數組接收
接收數組或集合數據,客戶端傳遞多個同名參數時,也可以使用單列集合接收,但是需要使用@RequestParam告知框架傳遞的參數是要同名設置的,不是對象屬性設置的
接收Json數據格式數據,Json數據都是以請求體的方式提交的,且不是原始的鍵值對格式的,所以我們要使用@RequestBody注解整體接收該數據。
將json轉成javebean,使用了SpringMVC的組件RequestMappingHandlerAdapter
接收Restful風格數據,Restful請求數據一般會在URL地址上攜帶,可以使用注解 @PathVariable(占位符參數名稱)
接收文件上傳的數據,文件上傳的表單需要一定的要求,如下:
表單的提交方式必須是POST表單的enctype屬性必須是multipart/form-data文件上傳項需要有name屬性
<form action="" enctype="multipart/form-data"><input type="file" name="myFile">
</form>
服務器端,由于映射器適配器需要文件上傳解析器,而該解析器默認未被注冊,所以手動注冊
<!--配置文件上傳解析器,注意:id的名字是固定寫法-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="defaultEncoding" value="UTF-8"/><!--文件的編碼格式 默認是ISO8859-1--><property name="maxUploadSizePerFile" value="1048576"/><!--上傳的每個文件限制的大小 單位字節--><property name="maxUploadSize" value="3145728"/><!--上傳文件的總大小--><property name="maxInMemorySize" value="1048576"/><!--上傳文件的緩存大小-->
</bean>
使用MultipartFile類型接收上傳文件
@PostMapping("/fileUpload")
public String fileUpload(@RequestBody MultipartFile myFile) throws IOException {System.out.println(myFile);//獲得上傳的文件的流對象InputStream inputStream = myFile.getInputStream();//使用commons-io存儲到C:\haohao\abc.txt位置FileOutputStream outputStream = new FileOutputStream("C:\\Users\\haohao\\"+myFile.getOriginalFilename());IOUtils.copy(inputStream,outputStream);//關閉資源inputStream.close();outputStream.close();return "/index.jsp";
}
接收Http請求頭數據,接收指定名稱的請求頭
接收所有的請求頭信息
獲得客戶端攜帶的Cookie數據
獲得轉發Request域中數據,在進行資源之間轉發時,有時需要將一些參數存儲到request域中攜帶給下一個資源
- Javaweb常用對象獲取
獲得Javaweb常見原生對象,有時在我們的Controller方法中需要用到Javaweb的原生對象,例如:Request、Response等,我們只需要將需要的對象以形參的形式寫在方法上,SpringMVC框架在調用Controller方法時,會自動傳遞實參:
- 請求靜態資源
靜態資源請求失效的原因,當DispatcherServlet的映射路徑配置為 / 的時候,那么就覆蓋的Tomcat容器默認的缺省Servlet
解決方案
在spring-mvc.xml中去配置< mvc:default-servlet-handler >,該方式是注冊了一個DefaultServletHttpRequestHandler 處理器,靜態資源的訪問都由該處理器去處理,這也是開發中使用最多的
<mvc:default-servlet-handler/>
- 注解驅動 mvc:annotation-driven 標簽
該標簽內部會幫我們注冊RequestMappingHandlerMapping、注冊RequestMappingHandlerAdapter并注入Json消息轉換器等
原配置
現配置
3.SpringMVC的響應處理
- 傳統同步業務數據響應
請求資源轉發;
請求資源重定向;
響應模型數據;
直接回寫數據給客戶端;
- 前后端分離異步業務數據響應
此處@ResponseBody也可以將JavaBean自動給我們轉換成Json格式字符串回響應
@RestController替代@Controller和@ResponseBody
4.SpringMVC的攔截器
- 攔截器 Interceptor 簡介
SpringMVC的攔截器Interceptor規范,主要是對Controller資源訪問時進行攔截操作的技術,當然攔截后可以進行權限控制,功能增強等都是可以的。攔截器有點類似 Javaweb 開發中的Filter,攔截器與Filter的區別如下圖:
HandlerInterceptor接口方法的作用及其參數、返回值詳解如下:
- 攔截器快速入門
配置Interceptor
<!--配置攔截器-->
<mvc:interceptors><mvc:interceptor><!--配置對哪些資源進行攔截操作--><mvc:mapping path="/*"/><bean class="com.itheima.interceptor.MyInterceptor01"></bean></mvc:interceptor>
</mvc:interceptors>
- 攔截器執行順序
當每個攔截器都是放行狀態時,三個方法的執行順序如下:
當Interceptor1和Interceptor2處于放行,Interceptor3處于不放行時,三個方法的執行順序如下
攔截器執行順序取決于 interceptor 的配置順序
- 攔截器執行原理
請求到來時先會使用組件HandlerMapping去匹配Controller的方法(Handler)和符合攔截路徑的Interceptor,Handler和多個Interceptor被封裝成一個HandlerExecutionChain的對象
在DispatcherServlet的doDispatch方法中執行攔截器
5.SpringMVC的全注解開發
- spring-mvc.xml 中組件轉化為注解形式
關于SpringMvc的xml
組件掃描,可以通過@ComponentScan注解完成;
文件上傳解析器multipartResolver可以通過非自定義Bean的注解配置方式,即@Bean注解完成
<mvc:default-servlet-handler /> 和 <mvc:interceptor > 怎么辦呢?SpringMVC 提供了一個注解叫做@EnableWebMvc,我們看一下源碼,內部通過@Import 導入了DelegatingWebMvcConfiguration類,WebMvcConfigurer類型的Bean會被注入進來,然后被自動調用,所以可以實現WebMvcConfigurer接口,完成一些解析器、默認Servlet等的指定,WebMvcConfigurer接口定義如下:
創建MyWebMvcConfigurer實現WebMvcConfigurer接口,實現addInterceptors 和configureDefaultServletHandling方法
最后,在SpringMVC核心配置類上添加@EnableWebMvc注解
- DispatcherServlet加載核心配置類
DispatcherServlet在進行SpringMVC配置文件加載時,使用的是以下方式:
現在是使用SpringMVCConfig核心配置類提替代的spring-mvc.xml,怎么加載呢?參照Spring的ContextLoaderListener加載核心配置類的做法,定義了一個AnnotationConfigWebApplicationContext,通過代碼注冊核心配置類
- 消除web.xml
目前,幾乎消除了配置文件,但是web工程的入口還是使用的web.xml進行配置的,如下
Servlet3.0環境中,web容器提供了javax.servlet.ServletContainerInitializer接口,實現了該接口后,在對應的類加載路徑的META-INF/services 目錄創建一個名為javax.servlet.ServletContainerInitializer的文件,文件內容指定具體的ServletContainerInitializer實現類,那么,當web容器啟動時就會運行這個初始化器做一些組件內的初始化工作;
基于這個特性,Spring就定義了一個SpringServletContainerInitializer實現了ServletContainerInitializer接口;
而SpringServletContainerInitializer會查找實現了WebApplicationInitializer的類,Spring又提供了一個WebApplicationInitializer的基礎實現類AbstractAnnotationConfigDispatcherServletInitializer,當我們編寫類繼承AbstractAnnotationConfigDispatcherServletInitializer時,容器就會自動發現我們自己的類,在該類中我們就可以配置Spring和SpringMVC的入口了。
6.SpringMVC的組件原理剖析
- 前端控制器初始化
前端控制器DispatcherServlet是SpringMVC的入口,也是SpringMVC的大腦,主流程的工作都是在此完成的,梳理一下DispatcherServlet 代碼。DispatcherServlet 本質是個Servlet,當配置了 load-on-startup 時,會在服務器啟動時就執行創建和執行初始化init方法,每次請求都會執行service方法
DispatcherServlet 的初始化主要做了兩件事:
(1)獲得了一個 SpringMVC 的 ApplicationContext容器,設置父子容器
父容器:Spring 通過ContextLoaderListener為入口產生的applicationContext容器,內部主要維護的是applicationContext.xml(或相應配置類)配置的Bean信息;
子容器:SpringMVC通過DispatcherServlet的init() 方法產生的applicationContext容器,內部主要維護的是spring-mvc.xml(或相應配置類)配置的Bean信息,且內部還通過parent屬性維護這父容器的引用。
Bean的檢索順序:根據上面子父容器的概念,可以知道Controller存在與子容器中,而Controller中要注入Service時,會先從子容器本身去匹配,匹配不成功時在去父容器中去匹配,于是最終從父容器中匹配到的UserService,這樣子父容器就可以進行聯通了。但是父容器只能從自己容器中進行匹配,不能從子容器中進
行匹配。
(2)注冊了 SpringMVC的 九大組件
注冊 SpringMVC的 九大組件,在初始化容器initWebApplicationContext方法中執行了onRefresh方法,進而執行了初始化策略initStrategies方法,注冊了九個解析器組件
在初始化前端控制器時,會先去父子容器中找到是否有對應的HandlerMapping(自己配置的),如果沒有就加載默認的
- 前端控制器執行主流程
7.SpringMVC的異常處理機制
- SpringMVC 異常的處理流程
SpringMVC 處理異常的思路是,一路向上拋,都拋給前端控制器 DispatcherServlet ,DispatcherServlet 在調用異常處理器ExceptionResolver進行處理,如下圖:
- SpringMVC 的異常處理方式
(1)簡單異常處理器:使用SpringMVC 內置的異常處理器處理 SimpleMappingExceptionResolver;
注解方式配置簡單映射異常處理器
(2)自定義異常處理器:實現HandlerExceptionResolver接口,自定義異常進行處理;
交給Spring管理異常處理器
(3)注解方式:使用@ControllerAdvice + @ExceptionHandler 來處理。
@ControllerAdvice 注解本質是一個@Component,也會被掃描到,與此同時,具備AOP功能,默認情況下對所有的Controller都進行攔截操作,
攔截后干什么呢?就需要在結合@ExceptionHandler、@InitBinder、@ModelAttribute 注解一起使用了,此處我們講解的是異常,所以是@ControllerAdvice + @ExceptionHandler的組合形式。
編寫全局異常處理器類,使用@ControllerAdvice標注,且@ExceptionHandler指定異常類型
如果全局異常處理器響應的數據都是Json格式的字符串的話,可以使用@RestControllerAdvice替代@ControllerAdvice 和 @ResponseBody
- 異常處理機制原理剖析
初始化加載異常處理解析器,配置了自定義的異常處理器后,默認的異常處理器就不會被加載,當配置<mvc:annotation-driven /> 或配置了注解@EnableWebMvc后,默認異常處理器和自定的處理器異常解析器都會被注冊
在發生異常后,獲得當前發生異常的Handler對象,執行對應方法
- SpringMVC 常用的異常解析器.
SpringMVC 相關的處理器異常解析器繼承體系如下:
SpringMVC 常用的異常解析器