1.@RequestMapping
- @RequestMapping註解來映射URl,返回值會通過視圖解析器解析爲實際的物理試圖,對於org.springframework.web.servlet.view.InternalResourceViewResolver試圖通過prefix+returnVal+後綴這樣的方式得到實際的物理試圖,然後做轉發操作.
<bean class=*"org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name=*prefix* *value=**/WEB-INF/view/"></property>
<property name=*"suffix" value=".jsp"></property>
</bean>
2.@ModelAttribute
被@ModelAttribute標記的方法,會在每一個目標方法執行之前被springmvc調用
@ModelAttribute
public void getUserInfo(@RequestParam(value = "id",required = false) Integer id, Map<String,Object> map){
System.out.println("xxxxxx");
if(id!=null){
Person person=new Person("x","1");
System.out.println(person);
map.put("person",person);
}
}
@RequestMapping("/demo/xx")
public ModelAndView demo(HttpServletRequest request, HttpServletResponse response,Person person ){
System.out.println("x");
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject(person);
return modelAndView;
}
運行流程:
- 執行@ModelAttribute註解修飾的方法,從數據庫中取出對象,把對象放到map中,
- springmvc從map取出User對象,並把表單的請求參數賦值到給該對象的對象屬性
- springmvc吧上述對象傳入目標方法參數
源碼分析
- 首先會調用@ModelAttribute 註解修飾的方法, 實際上把@ModelAttribute方法中的map 的數據保存到implicitModel中
- 解析請求參數中的目標參數,實際上該目標參數 來自於WebDateBinder對象的target屬性
WebDateBinder=bingObject(target對象)+attrName- 創建WebDateBinder對象
- 確定objectName屬性:若傳入的attrNa me屬性值爲"",則objectName爲類型首字母小寫,如果目標方法的pojo屬性使用了@ModelAttribute來修飾,則attrName值爲@ModelAttribute的value屬性值
- 確定target屬性
- 在implicitModel中查找attrName對應的屬性值,若存在,OK
- 若不存在則驗證當前handler是否使用了@SessionAttributes進行修飾,若使用了,則嘗試從session中獲取attrName所對應的屬性值,若session中沒有對應的屬性值,則拋出異常
- 若Handler沒有使用@SessionAttributes修飾,或@SessionAttributes中沒有使用value值指定的key和attrName相匹配,則通過反射創建POJO對象
- springmvc把表單的請求參數賦給了WebDateBinder的target對象
- springmvc會把WebDateBinder的attrName和target給到implicitModel,傳到request域中
- 把WebDateBinder的target作爲參數傳遞給目標方法入參
- 創建WebDateBinder對象
3.springmvc是怎麼確定目標對象方法POJO類型入參的過程的?
- 確定一個key
- 若目標方法的pojo類型的參數沒有@ModelAttribute作爲修飾,則key爲POJO類型的首字母小寫
- 若使用了@ModelAttribute來修飾,則key爲@ModelAttribute註解的value屬性值
- 在implicitModel中查找key對象的對象,若存在,則作爲入參傳入
- 若在@ModelAttribute標記的方法中在map中保存在,且key和之前確定的key一致,則會獲取到
- 若implicitModel獲取不到key對應的對象,則會檢查當前的handler是否使用@SessionAttributes註解修飾,若使用了該註解,且@SessionAttributes註解的value包含了key,則會從HttpSession獲取到key所對應的value的值,若存在則直接傳入到目標對象的入參中,若不存在則將拋出異常
- 若handler沒有標識@SessionAttributes註解或@SessionAttributes註解的@Value不包含key ,則通過反射創建POJO類型的參數,傳入目標方法的參數
- springmvc會把key和POJO類型的對象保存到implicitModel中,進而保存到request中
4.springmvc數據綁定流程
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-iLt64TnN-1575211185165)(/Users/laiyanxin/Library/Application Support/typora-user-images/image-20191201140712842.png)]
流程
-
Springmvc通過反射機制對目標處理方法進行解析,並將請求消息綁定到處理方法入參中.
-
Springmvc會將ServletRequest對象及目標處理方法的參數對象實例傳遞到DataBinder,DataBinder調用裝配在Spring web上下文的ConversionService組件進行數據類型轉換,數據格式化工作,並將ServletRequest中的消息填充到參數對象中,然後再調用Vaildator組件已經綁定了請求消息數據的參數對象進行數據合法性校驗,並最終生成數據綁定結果BindingResult對象.
-
BindingResult對象包含了已完成數據綁定參數對象,還包含了相對應的校驗錯誤對象.
5.@RequestBody @ResponseBody原理
工作原理圖[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-TPrzMpcm-1575211185166)(/Users/laiyanxin/Library/Application Support/typora-user-images/image-20191201153135250.png)]
- 從請求報文先轉爲HttpInputMessage再由HttpMessgetConverter轉爲我們需要的java對象
- spingmvc返回一個對象先轉爲 HttpMessgetConverter再轉HttpOutPutMessage再響應信息
public interface HttpInputMessage extends HttpMessage {
//把請求信息轉爲輸入流
InputStream getBody() throws IOException;
}
public interface HttpOutputMessage extends HttpMessage {
//把請求信息轉爲輸出流
OutputStream getBody() throws IOException;
}
核心在於HttpMessgetConverter,SpringMVC處理請求和響應時,支持多種類型的請求參數和返回類型,而此種功能的實現就需要對HTTP消息體和參數及返回值進行轉換,爲此SpringMVC提供了大量的轉換類,所有轉換類都實現了HttpMessageConverter接口。
6攔截器執行流程
攔截器有三個方法
- preHandle 在目標方法之前調用
- postHandle 調用目標方法後,渲染試圖之前
- afterCompletion 渲染試圖後
- 攔截器可以通過配置作用的路徑
<mvc:mapping ><mvc:exclude-mapping>
配置包含關係
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
----省略代碼-----
//在目標方法之前調用
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 調用目標方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//調用目標方法後,渲染試圖之前調用
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
//渲染試圖
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
}
//渲染視圖
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
------....---
}
if (mappedHandler != null) {
// 渲染視圖完之後調用
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
7攔截器執行順序
- 正常流程
第一個攔截器preHandle—》第二個攔截器preHandle–》目標方法–》第二個攔截器postHandle–》第一個攔截器postHandle --》第二個攔截器afterCompletion–》第一個攔截器afterCompletion
- 第二個攔截器preHandle返回false
第一個攔截器preHandle—》第二個攔截器preHandle–》第一個攔截器afterCompletion
- preHandle正序,afterCompletion和postHandle倒序
8. springmvc執行流程圖
9.springmvc源碼解析
核心方法在於DispatcherServlet中的doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 獲取當前請求的HandlerExecutionChain,根據handleMapping獲取
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//獲取.HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 調用目標方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
-
先獲取一個mappingHandler,類型就是HandleExecutionChain,處理器調用鏈(包含了攔截器,目標方法和handler ),通過handlerMapping獲取
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
-
獲取到hadlerAdapter,( 包含messageConver,數據轉換,數據格式化,校驗等等)
-
調用目標方法返回ModelAndView
-
調用攔截的applyPostHandle
-
處理視圖processDispatchResult
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//如果有異常就通過handlerExceptionResolvers來處理
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// 如果沒異常就進行渲染試圖
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
}
if (mappedHandler != null) {
// 渲染完調用 攔截器afterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
-
會先判斷是否有異常,如果有異常就通過handlerExceptionResolvers來處理
-
如果沒異常就進行渲染試圖
- 會先通過ViewResolvers得到View
- 下一步渲染視圖
-
渲染完調用 攔截器afterCompletion
-
會先判斷是否有異常,如果有異常就通過handlerExceptionResolvers來處理
-
如果沒異常就進行渲染試圖
- 會先通過ViewResolvers得到View
- 下一步渲染視圖
-
渲染完調用 攔截器afterCompletion