Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 亚洲二区视频,99精品视频免费观看,欧美综合网站

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          SpringMVC概述和基礎知識詳解

          SpringMVC概述和基礎知識詳解

          . SpringMVC概述及原理

          1. SpringMVC是什么

          Spring MVC屬于SpringFrameWork的后續產品,已經融合在Spring Web Flow里面。Spring 框架提供了構建 Web 應用程序的全功能 MVC 模塊。使用 Spring 可插入的 MVC 架構,從而在使用Spring進行WEB開發時,可以選擇使用Spring的Spring MVC框架或集成其他MVC開發框架,如Struts1(現在一般不用),[Struts 2](https://baike.baidu.com/item/Struts 2/2187934)(一般老項目使用)等。

          SpringMVC 已經成為目前最主流的 MVC 框架之一, 從 Spring3.0 的發布, 就已全面超越 Struts2,成為最優秀的 MVC 框架。它通過一套注解,讓一個簡單的 Java 類成為處理請求的控制器,而無須實現任何接口。同時它還支持RESTful 編程風格的請求。

          2. MVC和三層架構

          MVC模式(Model-View-Controller)是軟件工程中的一種軟件架構模式,把軟件系統分為三個基本部分:模型(Model)、視圖(View)和控制器(Controller)。


          控制器(Controller):Servlet,控制器主要處理用戶的請求

          視圖(View):HTML, JSP, 前端框架

          模型(Model):邏輯業務程序(后臺的功能程序), Service, Dao, JavaBean

          JavaWEB發展史

          Model1

          所有的業務邏輯交給jsp單獨處理完成,一個web項目只存在DB層和JSP層,所有的東西都耦合在一起,對后期的維護和擴展極為不利。

          Model2 第一代

          JSP Model2有所改進,把業務邏輯的內容放到了JavaBean中,而JSP頁面負責顯示以及請求調度的工作。雖然第二代比第一代好了些,但JSP還是把view和controller的業務耦合在一起。依然很不理想。

          Model2 第二代(三層架構)

          Model2第二代就是現在大力推廣的和使用的mvc,將一個項目劃分為三個模塊,各司其事互不干擾,既解決了jsp所形成的耦合性,又增加了邏輯性、業務性以及復用性和維護性

          表示層(web層):包含JSP,Servlet等web相關的內容

          業務邏輯層(Service):處理業務,不允許出現servlet中的request、response。

          數據層(Data Access Object):也叫持久層,封裝了對數據庫的訪問細節。

          其中 web層相當于mvc中的view+controller,Service層和dao層相當于mvc中的model。

          3. SpringMVC 在三層架構的位置

          MVC模式:(Model-View-Controller):為了解決頁面代碼和后臺代碼的分離.


          二. 入門示例

          1. 配置流程-基于XML的配置

          1.1.搭建普通Maven項目



          使用插件將項目轉換為web項目

          轉換成功:

          查看是否生成webapp目錄和maven項目打包方式是否變為war

          添加SpringMVC依賴

          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-webmvc</artifactId>
              <version>5.0.6.RELEASE</version>
          </dependency>

          查看關系依賴圖

          1.2.在web.xml配置核心控制器

          <?xml version="1.0" encoding="UTF-8"?>
          <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          	xmlns="http://java.sun.com/xml/ns/javaee"
          	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
          	version="2.5">
          
          	<!--spring核心(前端控制器)-->
          	<servlet>
          		<servlet-name>dispatcher</servlet-name>
          		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          	</servlet>
          	<servlet-mapping>		<!--只有*.form后綴的請求才會進入springmvc-->
          		<servlet-name>dispatcher</servlet-name>
          		<url-pattern>*.form</url-pattern>
          	</servlet-mapping>
          </web-app>

          1.3.創建一個Spring的配置文件

          <?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:context="http://www.springframework.org/schema/context"
                 xmlns:mvc="http://www.springframework.org/schema/mvc"
                 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.dfbz"/>
          
              <!--開啟springmvc注解支持-->
              <mvc:annotation-driven/>
          </beans>

          mvc:annotation-driven 說明

          在 SpringMVC 的各個組件中,處理器映射器、處理器適配器、視圖解析器稱為 SpringMVC 的三大組件。

          在springmvc早期版本中需要我們自己加載springmvc的三大組件(現在我們使用的版本5.0.6會自動加載這三大組件)

          **處理器映射器:RequestMappingHandlerMapping **

          處理器適配器:RequestMappingHandlerAdapter

          處理器解析器:ExceptionHandlerExceptionResolver

          在早期的版本中使用 <mvc:annotation-driven> 自動加載這三大組件,但是高版本的不需要<mvc:annotation-driven>來加載

          同時它還提供了:數據綁定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,讀寫XML的支持(JAXB,讀寫JSON的支持(Jackson)。我們處理響應ajax請求時,就使用到了對json的支持(配置之后,在加入了jackson的core和mapper包之后,不寫配置文件也能自動轉換成json)

          springmvc配置文件說明

          注意:默認的Spring配置文件放在WEB-INF下,名為{servlet-name}-servlet.xml

          {servlet-name}指的是,核心控制器配置的名字

          如:dispatcherServlet-servlet.xml

          當請求被springmvc處理時,springmvc會去默認路徑下加載xxxx-servlet.xml核心配置文件

          但是我們在開發中一般都是把配置文件寫在classes下的,我們可以在web.xml中設置springmvc配置文件的路徑

          <!--spring核心(前端控制器)-->
          <servlet>
          	<servlet-name>dispatcher</servlet-name>
          	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          	<!--配置springmvc配置文件的位置-->
          	<init-param>
          		<param-name>contextConfigLocation</param-name>
          		<param-value>classpath:dispatcher-servlet.xml</param-value>
          	</init-param>
          </servlet>
          <servlet-mapping>		<!--只有*.form后綴的請求才會進入springmvc-->
          	<servlet-name>dispatcher</servlet-name>
          	<url-pattern>*.form</url-pattern>
          </servlet-mapping>

          1.4.創建一個業務控制器

          package com.dfbz.controller;
          
          import org.springframework.stereotype.Controller;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.servlet.ModelAndView;
          
          @Controller
          public class HelloController{
          
              //代表此方法的訪問路徑為/hello.form
              @RequestMapping("/hello.form")
              public ModelAndView hello(){
                  
                  ModelAndView mav=new ModelAndView();
                  mav.addObject("msg","小標");
                  mav.setViewName("/hello.jsp");
          
                  return mav;
              }
          
          }

          1.5.創建一個返回的視圖頁面

          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <html>
          <head>
             <title>Title</title>
          </head>
          <body>
             ${msg },歡迎您!
          </body>
          </html>

          1.7. SpringMVC的工作流程

          1、客戶端發送請求給前端控制器(DispatcherServlet)

          2、dispatcherServlet接收到請求調用HandlerMapping處理器映射器

          3、處理器映射器根據請求的url找對應的處理器,生成處理器對象(handler)返回

          4、dispatchServlet將handler傳入處理器適配器執行

          5、處理器適配器執行handler

          6、執行完成最終封裝一個ModelAndView

          7、將ModelAndView返回給前端控制器

          8、前端控制器將請求的路徑交給視圖解析器進行解析

          9、最終封裝一個View對象給dispatcherServlet,此View對象封裝了響應參數

          10、JSP頁面渲染數據

          11、響應客戶端

          1.8 SpringMVC源碼分析

          我們知道SpringMVC實質上是對servlet進行封裝,讓我們的開發更加簡便

          1. 準備工作

          我們知道springmvc在工作開始之前會加載默認的處理器映射器、處理器適配器、處理器解析器等

          可以在spring-webmvc-5.0.6.RELEASE.jar源碼包下查看DispatcherServlet.properties文件看有哪些處理器是springmvc默認加載的

          2. 查看DispatcherServlet的繼承體系:

          我們發現DispatcherServlet最終還是繼承與HttpServlet,那么我們就直接找service方法吧!

          經打斷點發現,最終會走向DispacherServlet的doDispacher方法!

          此時請求進入DispatcherServlet,按照我們畫圖分析的結果應該是把請求交給處理器映射器HandlerMapping最終返回一個Handler

          3. 查看HandlerMapping接口:

          4. 尋找HandlerMapping實現類:


          接下來進入處理器適配器HandlerAdapter執行handler最終返回一個ModelAndView

          5. 查看HandlerAdapter接口:

          6. 查看HandlerAdapter實現類:


          然后請求交給視圖解析器進行解析最終返回一個View對象

          7. 查看View接口:

          8. 查看View實現類:


          9. 查看View信息:

          1.9.核心控制器

          SpringMVC自帶了攔截器請求的核心控制器.所以就可以在請求過來的時候,直接啟動Spring框架

          默認情況下,Spring容器是在核心控制器DispatcherServlet獲得請求后才啟動的.

          能不能網站啟動的時候,Spring容器就立刻啟動.

          <servlet>
              <servlet-name>dispatcher</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <load-on-startup>1</load-on-startup><!-- 啟動服務器的時候,Servlet就創建實例 --> 
          </servlet>

          三. Controller方法返回值

          返回值


          返回String

          可以返回視圖字符串,解析器會自動解析

          @RequestMapping("/demo3_1")
          public String demo3_1(){
          
              return "/success.jsp";           //直接返回字符串
          }

          返回ModelAndView

          ModelAndView是SpringMVC幫我們提供的,即"模型和視圖"

          @RequestMapping("/demo3_2")
          public ModelAndView demo3_2(){          //返回ModelAndView
              ModelAndView mav=new ModelAndView();
              mav.setViewName("success.jsp");
              mav.addObject("username","東方標準");
              return mav;
          }

          返回void

          一般用于使用原生的Servlet對象或者ajax請求

          @RequestMapping("/demo3_3")		//返回void(一般用于ajax)
          public void demo3_2(HttpServletResponse response) throws IOException {          
          
              response.setContentType("text/html;charset=utf8");
              response.getWriter().write("東方標準");
          
          }

          轉發和重定向


          SpringMVC提供了一個 String 類型返回值之后會自動解析forward、redirect等特殊字符串

          視圖解析器配置的前綴和后綴解析不支持forward、redirect

          :forward:代表轉發request.getRequestDispatcher(url).forward(request,response)

          :redirect:代表重定向response.sendRedirect(url)

          @RequestMapping("/demo3_4")
          public String demo3_4(Model model)  {          //轉發
          
              System.out.println("執行啦!");
              model.addAttribute("username","東方標準");
              return "forward:/success.jsp";
          }
          @RequestMapping("/demo3_5")
          public String demo3_5(HttpSession session)  {          //重定向
              session.setAttribute("password","admin");
              System.out.println("執行啦!");
          
              return "redirect:/success.jsp";
          }

          四. 映射路徑-@RequestMapping

          1. 探究RequestMapping

          注解式處理器映射器,對類中標記了@ResquestMapping的方法進行映射。根據@ResquestMapping定義的url匹配@ResquestMapping標記的方法,匹配成功返回HandlerMethod對象給前端控制器。HandlerMethod對象中封裝url對應的方法Method。

          @Target({ElementType.METHOD, ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          @Mapping
          public @interface RequestMapping {
          }

          命名空間

          按照我們傳統的url寫法不能很好的規定請求路徑,即不能按照業務來區分請求

          例如現在user需要定義一個findById,goods也需要定義一個findById,此時就會有沖突,針對這種現象我們可以在類上定義一個命名空間,即在類上使用@RequestMapping注解,類似于一級目錄,以后訪問此類下的任意資源都需要加上此目錄

          類上

          請求 URL 的第一級訪問目錄。此處不寫的話,就相當于應用的根目錄。 寫的話需要以/開頭。它出現的目的是為了使我們的 URL 可以按照模塊化管理:

          例如:

          user模塊:

          /user/register

          /user/update

          /user/findById

          goods模塊:

          /goods/add

          /goods/update

          /goods/findById

          映射路徑的有三種:標準的映射路徑,帶通配符的映射路徑,帶路徑變量的映射路徑

          方法上

          請求 URL 的第二級訪問目錄。

          屬性:

          • value:用于指定請求的 URL。 它和 path 屬性的作用是一樣的。
          • method:用于指定請求的方式。
          • params:用于指定限制請求參數的條件。 它支持簡單的表達式。 要求請求參數的 key 和 value 必須和配置的一模一樣。

          params={“userId”},表示請求參數必須有 userId,區分大小寫

          params={“userId!=20”},表示請求參數中 id不能是 20。可以不攜帶userId參數,區分大小寫

          headers:用于指定限制請求消息頭的條件。

          package com.dfbz.controller;
          
          import org.springframework.stereotype.Controller;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.RequestMethod;
          import org.springframework.web.servlet.ModelAndView;
          
          @Controller
          public class HelloController{
          
              public HelloController(){
                  System.out.println("Hello創建了");
              }
              /*
              	代表此方法的訪問路徑為/hello.form,如果攜帶userId參數那么參數必須不能等于1
              	并且提交方式一定要為POST
              */
              @RequestMapping(value="/hello.form",params="userId!=1",method=RequestMethod.POST)
              public ModelAndView hello(){
          
                  ModelAndView mav=new ModelAndView();
                  mav.addObject("msg","小標");
                  mav.setViewName("/hello.jsp");
          
                  return mav;
              }
          
          }

          小結:

          1.參數必須包括:params={“username”,“password”}

          2.參數不能包括:params={"!userid"}

          3參數值必須是指定的值:params={“username=zhangsan”})

          4.參數值必須不是指定的值:params={“userid!=123”})

          2.3. RESTFUL

          所謂的路徑變量,就是將參數放在路徑里面,而不是放在?的后面

          如:原get請求方法 /login.mvc?username=’zhangsan’&pwd=’123456’

          路徑變量寫法:

          /zhangsan/123456/login.form

          2.3.1 什么是RESTFUL

          REST(英文:Representational State Transfer,簡稱REST)RESTful是一種軟件架構風格、設計風格,而不是標準,只是提供了一組設計原則和約束條件。它主要用于客戶端和服務器交互類的軟件。基于這個風格設計的軟件可以更簡潔,更有層次,更易于實現緩存等機制。

          2.3.2 RESTFUL示例:


          請求方式有幾種?7種

          jsp、html,只支持get、post。

          基于restful風格的url:

          添加

          http://localhost:8080/SpringMVC_01/user

          提交方式: post

          修改

          http://localhost:8080/SpringMVC_01/user

          提交方式:put

          刪除

          http://localhost:8080/SpringMVC_01/user/101

          提交方式:delete

          查詢

          http://localhost:8080/SpringMVC_01/user/102

          提交方式:get

          五. 數據綁定

          1. 數據綁定是什么

          SpringMVC里面,所謂的數據綁定就是將請求帶過來的表單數據綁定到執行方法的參數變量中,或將服務器數據綁定到內置對象,傳遞到頁面

          2. 自動綁定的數據類型

          2.1 自動綁定數據類型

          • 基本數據類型:基本數據類型+String+包裝類
          • 包裝數據類型(POJO):包裝實體類
          • 數組和集合類型:List、Map、Set、數組等數據類型

          2.2 內置綁定數據自動綁定:

          ServletAPI:

          HttpServletRequest

          HttpServletResponse

          HttpSession

          SpringMVC內置對象

          Model

          ModelMap

          ModelAndView

          Model和ModelMap默認都是存儲了Request請求作用域的數據的對象

          這個兩個對象的作用是一樣.就將數據返回到頁面.

          測試頁面:

          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
          </head>
          <body>
          <h3>測試ServletAPI綁定</h3>
          <p><a href="/demo1.form"></a></p>
          <h3>測試參數類型綁定</h3>
          <p><a href="/demo2.form?username=zhangsan">屬性參數綁定</a></p>
          
          <h3>測試對象參數綁定</h3>
          <form action="/demo3.form" method="post">
              <input type="text" name="username">
              <input type="text" name="password">
              <input type="text" name="address">
          
              <input type="submit" value="對象參數綁定">
          
          </form>
          
          <h3>測試數組類型綁定</h3>
          <form action="/demo4.form" method="post">
              <input type="checkbox" value="1" name="ids">
              <input type="checkbox" value="2" name="ids">
              <input type="checkbox" value="3" name="ids">
          
              <input type="submit" value="數組類型綁定">
          </form>
          
          <h3>測試Pojo對象數據綁定</h3>
          <hr>
          <form action="/demo6.form" method="post">
          
              <h3>Pojo封裝數組</h3>
              <input type="checkbox" value="1" name="ids">
              <input type="checkbox" value="2" name="ids">
              <input type="checkbox" value="3" name="ids">
          
              <br>
          
              <h3>Pojo封裝user</h3>
              <input type="text" name="user.username">
              <input type="text" name="user.password">
              <input type="text" name="user.address">
              <br>
          
              <h3>Pojo封裝list</h3>
              <input type="text" name="userList[1].name">
              <input type="text" name="userList[1].password">
              <input type="text" name="userList[1].address">
              <input type="text" name="userList[0].name">
              <input type="text" name="userList[0].password">
              <input type="text" name="userList[0].address">
          
              <br>
          
              <h3>Pojo封裝map</h3>
              <input type="text" name="map['username']"><br>
              <input type="text" name="map['password']"><br>
              <input type="text" name="map['age']"><br>
              <input type="text" name="map['address']"><br>
              <input type="submit" value="Pojo對象參數綁定"><br>
          
          </form>
          </body>
          </html>

          測試Controller:

          package com.dfbz.controller;
          
          import com.dfbz.entity.Pojo;
          import com.dfbz.entity.User;
          import org.springframework.stereotype.Controller;
          import org.springframework.web.bind.annotation.RequestMapping;
          
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import javax.servlet.http.HttpSession;
          import java.util.Arrays;
          import java.util.List;
          
          @Controller
          public class DemoController {
          
              @RequestMapping("/demo1")            //自定義屬性綁定
              public String demo1(HttpServletRequest request, HttpServletResponse response, HttpSession session){
          
                  request.setAttribute("username","zhangsan");
          
                  return "/success.jsp";
              }
          
              @RequestMapping("/demo2")            //屬性參數綁定
              public String demo2(String username){
          
                  System.out.println(username);
          
                  return "/success.jsp";
              }
          
              @RequestMapping("/demo3")            //對象參數綁定
              public String demo3(User user){
          
                  System.out.println(user);
          
                  return "/success.jsp";
              }
          
          
              @RequestMapping("/demo4")            //數組參數綁定
              public String demo4(String[] ids){
          
                  System.out.println(Arrays.toString(ids));
                  return "/success.jsp";
              }
          
              @RequestMapping("/demo5")            //Pojo對象參數綁定
              public String demo5(Pojo pojo){
          
                  System.out.println(pojo);
          
                  return "/success.jsp";
              }
          }

          3. Post提交方式亂碼解決

          測試GET/POST提交中文數據

          頁面:

          <h3>測試Post亂碼</h3>
          <form action="/demo" method="post">
              <input type="text" name="username">
              <input type="text" name="password">
              <input type="text" name="address">
          
              <input type="submit">
          </form>

          controller:

          @RequestMapping("/demo7.form")            //Pojo對象參數綁定
          public String demo7(User user){
          
              System.out.println(user);
          
              return "/success.jsp";
          }

          發現POST亂碼

          解決post提交亂碼我們可以配置spring提供的過濾器

          <!--解決Post提交中文亂碼-->
          <filter>
              <filter-name>characterEncoding</filter-name>
              <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
              <init-param>
                  <!--要使用的字符集,一般我們使用UTF-8(保險起見UTF-8最好)-->
                  <param-name>encoding</param-name>
                  <param-value>UTF-8</param-value>
              </init-param>
          </filter>
          <filter-mapping>
              <filter-name>characterEncoding</filter-name>
              <!--代表所有請求都經過編碼處理-->
              <url-pattern>/*</url-pattern>
          </filter-mapping>

          4. SpringMVC常用注解

          SpringMVC有一些數據是不能自動綁定,需要我們使用它提供的注解強制綁定.

          遇到需要強制綁定的幾種情況

          a. 默認參數綁定的是表單數據,如果數據不是來自表單(如restful),那么必須需要強制綁定

          b. 數據是來自表單的,但是參數名不匹配,那么也需要強制綁定

          c. 數據是來自表單的,但是需要將數據綁定在Map對象里面,需要強制綁定

          4.1. @PathVariable:綁定路徑參數

          這個注解是綁定路徑參數的.

          /**
           * http://localhost:8080/20/zhangsan/demo9.form
           * @param id
           * @param username
           * @return
           */
          @RequestMapping("/{id}/{username}/demo9")            //@PathVariable綁定restful請求
          public String demo9(
              @PathVariable("id") Integer id,@PathVariable("username") String username
              ){
          
              System.out.println(id);
              System.out.println(username);
          
              return "/success.jsp";
          }
          <a href="/20/zhangsan/demo9.form">測試restful風個綁定</a>

          4.2. 通過@RequestParam綁定表單數據

          接收的參數的變量名與表單的name屬性不一樣

          /**
           * http://localhost:8080/demo8.form?name=zhangsan
           * @param username
           * @return
           */
          @RequestMapping("/demo8")            //@RequestParam強制數據類型綁定
          public String demo8(@RequestParam("name") String username) {
          
              System.out.println(username);
          
              return "/success.jsp";
          }
          <a href="/demo8.form?name=zhangsan">測試強制類型綁定</a>

          4.3. @CookieValue獲得Cookie值的注解

          /**
          * 獲得JSP 頁面,JSESSIOINID這個Cookie值
          * @param cookevalue
          */
          @RequestMapping("/demo10")
          public void getcookie(@CookieValue(value="JSESSIONID") String cookevalue){
              //輸出Cookie
              System.out.println(cookevalue);
          }
          <a href="/demo10.form">測試@CookieValue注解</a>

          4.4. @RequestHeader獲得指定請求頭的值

          @RequestMapping("/demo11")
          //獲取請求頭host的值封裝到value中
          public void demo10(@RequestHeader("host")String value){
          
              System.out.println(value);
          }
          <a href="/demo11.form">測試@RequestHeader注解</a>

          4.5 @SessionAttributes 注解

          把Model和ModelMap中的指定的key或者指定的屬性的值也存入一份進session域

          package com.dfbz.controller;
          
          import com.dfbz.entity.User;
          import org.springframework.stereotype.Controller;
          import org.springframework.ui.Model;
          import org.springframework.ui.ModelMap;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.SessionAttribute;
          import org.springframework.web.bind.annotation.SessionAttributes;
          
          import javax.servlet.http.HttpServletRequest;
          
          @Controller
          /**
           * names: 代表此類中model/modelMap的username屬性將會添加到一份進入session
           * types: 此類中指定的類型會添加一份到session中
           */
          @SessionAttributes(names={"username"},types={User.class,String.class,Integer.class} )
          public class Demo2Controller {
          
              @RequestMapping("/demo12.form")
              public String demo12(Model modelMap) {
                  modelMap.addAttribute("username","zhangsan");
                  modelMap.addAttribute("password","admin");
                  modelMap.addAttribute("age",20);
                  User user=new User();
                  user.setUsername("xiaodong");
                  modelMap.addAttribute("user",user);
                  return "/success.jsp";
              }
          }
          <a href="/demo12.form">測試@SessionAttribute注解</a>

          @SessionAttribute注解:

          從session中獲取一個值封裝到參數中

          /**
           * @SessionAttribute:從session中獲取一個值
           * @param username
           * @param user
           * @return
           */
          @RequestMapping(value="/demo6")
          public String demo5(@SessionAttribute("password") String username,@SessionAttribute("user") User user){
              System.out.println(username);
          
              return "/success.jsp";
          }

          5. 格式化參數類型

          SpringMVC之所以能夠幫我們實現自動數據類型轉換是因為SpringMVC提供了非常多的轉換器(Converter)

          例如:


          發現他們都實現Converter接口,如果我們需要之定義轉換器必須實現Converter接口

          案例:

          實現日期的轉換

          實體類:

          public class User {
              private Integer id;
              private String username;
              private String password;
              private String address;
              private Date birthday;
          }

          表單:

          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <html>
          <head>
              <title>Title</title>
          </head>
          <body>
          
          <form action="/testConverter.form">
              <input type="text" name="username">
              <input type="text" name="password">
              <input type="text" name="address">
              <input type="text" name="birthday">
          
              <input type="submit">
          </form>
          
          </body>
          </html>

          controller:

          @RequestMapping("/testConverter.form")
          public String testConverter(User user) {
              System.out.println(user);
              return "/success.jsp";
          }

          轉換器類:

          package com.dfbz.converter;
          
          import org.springframework.core.convert.converter.Converter;
          
          import java.text.ParseException;
          import java.text.SimpleDateFormat;
          import java.util.Date;
          
          /**
           * Converter<傳入進來的類型,轉換之后的類型>
           */
          public class MyConverter implements Converter<String,Date> {
              @Override
              public Date convert(String str) {
          
                  SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
                  try {
                      Date date=sdf.parse(str);
                      return date;
          
                  } catch (ParseException e) {
                      e.printStackTrace();
                  }
          
                  return null;
              }
          }

          dispatcher-servlet.xml配置

          <!--在SpringMVC配置的轉換器中添加我們自定義的轉換器-->
          <bean id="myConverters" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
              <!-- 配置多個轉換器 -->
              <property name="converters">
                  <set>
                      <!-- 配置轉換器(自定義轉化器類) -->
                      <bean class="com.dfbz.converter.MyConverter"></bean>
                  </set>
              </property>
          </bean>
          
          <!--開啟springmvc注解支持并重新指定轉換器-->
          <mvc:annotation-driven conversion-service="myConverters"/>

          六. Controller的生命周期

          Spring框架默認創建的對象是單例.所以業務控制器是一個單例對象.

          SpringMVC提供了,request,session ,globalsession三個生命周期

          • request:每次新的請求,創建一個新的實例.
          • session:每次會話創建一個新的實例. 就是同一個瀏覽器,就使用同一個實例
          • globalsession:基于集群的session

          每個Session創建一個實例

          迎關注頭條號:java小馬哥

          周一至周日早九點半!下午三點半!精品技術文章準時送上!!!

          精品學習資料獲取通道,參見文末

          1、回顧servlet 與jsp 執行過程

          2、Spring MVC請求處理流程

          3,mvc 體系結構詳解

          URL映射

          表單參數映射

          調用目標Control

          數據模型映射

          視圖解析

          異常處理

          DispatcherServlet

          DispatcherServlet它的作用有點兒類似于網關,DispatcherServlet負責將請求轉發到不同的Controller上去。

          配置DispatcherServlet

          / 后面不能寫成/*否則容易出問題。

          編寫Controller,繼承controller類。實現handlerRequest接口。指定ModelAndView。這是一種方式。

          第二種方式 繼承 HttpRequestHandler類。那么DispatcherServlet是如何去匹配不同的controller的呢?這就需要了解SpringMVC的體系結構。整個體系結構都搞懂才行。下面就從一個全局的視角去看Spring MVC的體系結構。

          SpringMVC的體系結構圖

          DispatcherServlet是通過HandlerMapping的一組映射關系去找到我們的目標Controller

          通過上面我們知道Controller存在的形式是多種多樣的。可以通過Controller接口的形式,也可以通過@Controller注解的形式存在。等等等。。。有這么多不同的Controller形式DispatcherServlet是如何去執行的呢?這里面就涉及到一個HandlerAdapter控制器適配器。找到數據模型的一個映射。然后試圖解析是通過ViewResolver這個東東。他支持各種各樣的試圖解析。view用于具體的試圖解析。HandlerExceptionResolver異常攔截解析器。

          下面就圍繞這張圖去逐一進行源碼分析。

          一,HandlerMapping源碼分析

          HandlerMapping是一個接口,他有兩個實現類,MatchableHandlerMapping和AbstractHandlerMapping。HandlerMapping在getHandler里面并沒有返回handler(我們的目標執行器,也就是我們的controller)而是返回一個執行鏈條。HandlerExecutionChain。

          根據下圖可知HandlerExecutionChain里面有一個getHandler方法,這里面返回了Handler。但是具體的是哪個一個Handler是不確定的,因為他是Object類型的。所以通過動態代理的方式是不能實現的。只能通過這個執行鏈條去返回目標Handler。后續的文章我們會對執行鏈條做深入的講解。

          然后看上面的類繼承關系圖。左面繼承了AbstractUrlHandlerMapping其中AbstractDetectingUrlHandlerMapping起到自動發現的作用,他是根據Bean的名稱,而且是必須以/開頭。比如這樣

          <bean name="/hello.do" class="com.tuling.control.SimpleControl"/>

          然后SimpleUrlHandlerMapping。如果不需要自動發現功能,則使用這個。SimpleUrlHandlerMapping是自己配置URL和controller之間的一個關系。所以他們之間的區別就是一個自動發現,一個手動配置。AbstractHandlerMethodMapping是對方法進行映射RequestMappingInfoHandlerMapping這個大家比較熟悉了,就是通過@RequestMapping進行映射的。如果配置了SimpleUrlHandlerMapping或者BeanNameUrlHandlerMapping那么默認的就會失效。SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping可以同時配置,都可以映射到我們的Controller控制器。

          SimpleUrlHandlerMapping的初始化執行流程

          對initApplicationContext()方法進行調用。其中super.initApplicationContext();這里是初始化一些攔截器。比如自動發現攔截器啊等等等。this.registerHandlers(this.urlMap);開始進行Url的匹配點進去看一下可知如果urlMap不為空才進行匹配。第一步是!url.startsWith("/")如果不是/開頭的。就會把你的URL加上/然后獲取Handler,就是我們的控制器。然后判斷handler是不是String類型的,。如果是進行去除空格的處理。然后執行this.registerHandler(url, handler);點進去發現,將handler轉化為Object類型。然后判斷是否是懶加載并且是String類型。然后將Handler轉化成String的字符串。然后獲取一個ApplicationContext對象。判斷handler是都是單例。如果是通過ApplicationContext.getBean(handlerName)獲取到一個Object對象。然后獲取handlerMap,判斷是否是空,如果不為空說明你配置了兩個name屬性此時會拋出異常。接著判斷你的url是否等于/如果是設置一個根目錄的Handler通過localhost:8080/即可訪問我們的handler。然后判斷是否等于/*如果是就設置一個默認的和Handler。也就是說你永遠都不會擔心找不到這個url的異常。

          最后如果以上情況都不是,則通過this.handlerMap.put(urlPath, resolvedHandler);將url和handler綁定在一起。如果說handler是懶加載那么此時map中存儲的value就是beanName。這就是SimpleUrlHandlerMapping的初始化流程。

          SimpleUrlHandlerMapping的訪問執行流程

          我們知道最終執行調用的一定是DispatcherServlet。所以我們從這里開始入手。找到getHandler

          首先通過Iterator var2=this.handlerMappings.iterator();或取到所有的Mapping然后遍歷。遍歷一次取出一個handlerMapping然后調用handlerMapping。點進去看看。第一行getHandlerInternal這個方法很重要。點進去看看,在這個方法里第一行通過getUrlPathHelper().getLookupPathForRequest(request);去解析我們地址欄上的URL。比如或取到的是/hello.do。第二行通過Object handler=lookupHandler(lookupPath, request);去查找對應的handler。點進去發現就是從上面put進去的handlerMap找到對應的handler。在最后return一個buildPathExposingHandler。點進去發現HandlerExecutionChain這里面配置了一個攔截器鏈,那么返回的并不是我們最終的handler。

          那么getHandlerInternal方法的第二行也執行完了。最終拿到得是一個攔截器鏈,如果說攔截器鏈為空則判斷第一行的url解析器拿到得/hello.do是否等于/如果是Object rawHandler=getRootHandler()設置為根節點的handler,然后判斷rawHandler是否等于空,如果是則調用默認的handler。如果不等于空呢,則直接通過beanName從Spring容器找到Handler。 再調用buildPathExposingHandler。還是獲取一個攔截器鏈。最終,將攔截器鏈返回。getHandlerInternal方法執行結束。返回到getHandler方法里。那么如果說,沒有獲取到攔截器鏈,則獲取默認的handler。如果不為空,則直接通過beanName從容器中獲取到handler。然后,去創建一個攔截器鏈條,又加入了一個攔截器。最后進一步獲取handler。然后返回HandlerExecutionChain。最后通過HandlerAdapter ha=this.getHandlerAdapter(mappedHandler.getHandler());獲取到的才是我們的handler。SimpleUrlHandlerMapping執行流程到此介紹完畢,說實話SpringMVC的代碼寫的跟IOC\aop差著一大塊兒的水平感覺。

          Controller 接口:

          HttpRequestHandler 接口:

          HttpServlet 接口:

          @RequestMapping方法注解

          可以看出 Handler 沒有統一的接口,當dispatchServlet獲取當對應的Handler之后如何調用呢?調用其哪個方法?這里有兩種解決辦法,一是用instanceof 判斷Handler 類型然后調用相關方法 。二是通過引入適配器實現,每個適配器實現對指定Handler的調用。spring 采用后者。

          HandlerAdapter詳解

          HandlerAdapter中有三個接口

          1supports接口,主要的作用是傳入一個handler看看是否可以被執行。如果可以返回true。

          2handle處理接口,處理完返回一個ModelAndView。

          3getLastModified,用作緩存處理,獲取最后一次修改時間。

          我們知道HandlerAdapter對應適配了4種handler關系圖如下。

          舉個例子,如果說我們寫了一個Servlet,然后繼承HttpServlet此時如果不配置SimpleServletHandlerAdapter適配器,那么就會報錯,報500的異常。但是如果配置了這個適配器,那么SpringMVC給我們配置的默認適配器SimpleControllerHandlerAdapter就會失效。所以需要手動的配置一下。下面看一下源碼即可驗證這個說法。上面講到返回一個攔截器鏈條。然后下面的代碼就是獲取適配器如下圖。

          點進getHandlerAdapter方法。這個方法里做了一個do-while循環。通過support方法判斷是否是可用的適配器。

          在下圖的方法里判斷是否是我們配置的適配器,如果是則返回true。 拿到適配器以后就開始了真正的調用。

          此時適配器找到了以后就開始真正的調用handler也就是我們servlet或者是controller。然后通過一個叫ViewResolver的接口類去解析。其中有一個接口叫resolveViewName。他返回一個View。然后View里面有一個render接口。通過里面的model進行處理,封裝HTML。通過response進行寫入。所以它是先有ViewResolver才有的View。

          然后看看View有哪些實現類如下圖。

          第一個AbstractTemplateViewResolver里面有以下兩種視圖支持,比如我們常用的FreeMarkerViewReslover。

          第二個BeanNameViewResolver可以通過這種方式實現自定的視圖解析器,生明一個自定義View類。然后實現View接口。在render里面去做返回。然后controller里面通過ModelAndView視圖解析器的構造器去加載我們的自定義View類型。

          第三個就是InternalResourceViewResolver。這個就是我們最常用的,通過配置前綴,后綴等信息去實現視圖解析。

          下圖是通過BeanName的形式做的視圖解析

          最后附上一張整體的流程圖

          封面圖源網絡,侵權刪除)

          私信頭條號,發送:“資料”,獲取更多“秘制” 精品學習資料

          如有收獲,請幫忙轉發,您的鼓勵是作者最大的動力,謝謝!

          一大波微服務、分布式、高并發、高可用的原創系列文章正在路上,

          歡迎關注頭條號:java小馬哥

          周一至周日早九點半!下午三點半!精品技術文章準時送上!!!

          十余年BAT架構經驗傾囊相授


          對于 Web 應用程序而言,我們從瀏覽器發起一個請求,請求經過一系列的分發和處理,最終會進入到我們指定的方法之中,這一系列的的具體流程到底是怎么樣的呢?

          Spring MVC 請求流程

          記得在初入職場的時候,面試前經常會背一背 Spring MVC 流程,印象最深的就是一個請求最先會經過 DispatcherServlet 進行分發處理,DispatcherServlet 就是我們 Spring MVC 的入口類,下面就是一個請求的大致流轉流程(圖片參考自 Spring In Action):

          1. 一個請求過來之后會到達 DispatcherServlet,但是 DispatcherServlet 也并不知道這個請求要去哪里。
          2. DispatcherServlet 收到請求之后會去查詢處理器映射(HandlerMapping),從而根據瀏覽器發送過來的 URL 解析出請求最終應該調用哪個控制器。
          3. 到達對應控制器(Controller)之后,會完成一些邏輯處理,而且在處理完成之后會生成一些返回信息,也就是 Model,然后還需要選擇對應的視圖名。
          4. 將模型(Model)和視圖(View)傳遞給對應的視圖解析器(View Resolver),視圖解析器會將模型和視圖進行結合。
          5. 模型和視圖結合之后就會得到一個完整的視圖,最終將視圖返回前端。

          上面就是一個傳統的完整的 Spring MVC 流程,為什么要說這是傳統的流程呢?因為這個流程是用于前后端沒有分離的時候,后臺直接返回頁面給瀏覽器進行渲染,而現在大部分應用都是前后端分離,后臺直接生成一個 Json 字符串就直接返回前端,不需要經過視圖解析器進行處理,也就是說前后端分離之后,流程就簡化成了 1-2-3-4-7(其中第四步返回的一般是 Json 格式數據)。

          Spring MVC 兩大階段

          Spring MVC主要可以分為兩大過程,一是初始化,二就是處理請求。初始化的過程主要就是將我們定義好的 RequestMapping 映射路徑和 Controller 中的方法進行一一映射存儲,這樣當收到請求之后就可以處理請求調用對應的方法,從而響應請求。

          初始化

          初始化過程的入口方法是 DispatchServlet 的 init() 方法,而實際上 DispatchServlet 中并沒有這個方法,所以我們就繼續尋找父類,會發現 init 方法在其父類(FrameworkServlet)的父類 HttpServletBean 中。

          HttpServletBean#init()

          在這個方法中,首先會去家在一些 Servlet 相關配置(web.xml),然后會調用 initServletBean() 方法,這個方法是一個空的模板方法,業務邏輯由子類 FrameworkServlet 來實現。

          FrameworkServlet#initServletBean

          這個方法本身沒有什么業務邏輯,主要是初始化 WebApplicationContext 對象,WebApplicationContext 繼承自 ApplicationContext,主要是用來處理 web 應用的上下文。

          FrameworkServlet#initWebApplicationContext

          initWebApplicationContext() 方法主要就是為了找到一個上下文,找不到就會創建一個上下文,創建之后,最終會調用方法 configureAndRefreshWebApplicationContext(cwac) 方法,而這個方法最終在設置一些基本容器標識信息之后會去調用 refresh()方法,也就是初始化 ioc 容器。

          當調用 refresh() 方法初始化 ioc 容器之后,最終會調用方法 onRefresh(),這個方法也是一個模板鉤子方法,由子類實現,也就是回到了我們 Spring MVC 的入口類 DispatcherServlet。

          DispatchServlet#onRefresh

          onRefresh() 方法就是 Spring MVC 初始化的最后一個步驟,在這個步驟當中會初始化 Spring MVC 流程中可能需要使用到的九大組件。

          Spring MVC 九大組件

          MultipartResolver

          這個組件比較熟悉,主要就是用來處理文件上傳請求,通過將普通的 Request 對象包裝成 MultipartHttpServletRequest 對象來進行處理。

          LocaleResolver

          LocaleResolver 用于初始化本地語言環境,其從 Request 對象中解析出當前所處的語言環境,如中國大陸則會解析出 zh-CN 等等,模板解析以及國際化的時候都會用到本地語言環境。

          ThemeResolver

          這個主要是用戶主題解析,在 Spring MVC 中,一套主題對應一個 .properties 文件,可以存放和當前主題相關的所有資源,如圖片,css樣式等。

          HandlerMapping

          用于查找處理器(Handler),比如我們 Controller 中的方法,這個其實最主要就是用來存儲 url 和 調用方法的映射關系,存儲好映射關系之后,后續有請求進來,就可以知道調用哪個 Controller 中的哪個方法,以及方法的參數是哪些。

          HandlerAdapter

          這是一個適配器,因為 Spring MVC 中支持很多種 Handler,但是最終將請求交給 Servlet 時,只能是 doService(req,resp) 形式,所以 HandlerAdapter 就是用來適配轉換格式的。

          HandlerExceptionResolver

          這個組件主要是用來處理異常,不過看名字也很明顯,這個只會對處理 Handler 時產生的異常進行處理,然后會根據異常設置對應的 ModelAndView,然后交給 Render 渲染成頁面。

          RequestToViewNameTranslator

          這個主鍵主要是從 Request 中獲取到視圖名稱。

          ViewResolver

          這個組件會依賴于 RequestToViewNameTranslator 組件獲取到的視圖名稱,因為視圖名稱是字符串格式,所以這里會將字符串格式的視圖名稱轉換成為 View 類型視圖,最終經過一系列解析和變量替換等操作返回一個頁面到前端。

          FlashMapManager

          這個主鍵主要是用來管理 FlashMap,那么 FlashMap 又有什么用呢?要明白這個那就不得不提到重定向了,有時候我們提交一個請求的時候會需要重定向,那么假如參數過多或者說我們不想把參數拼接到 url 上(比如敏感數據之類的),這時候怎么辦呢?因為參數不拼接在 url 上重定向是無法攜帶參數的。

          FlashMap 就是為了解決這個問題,我們可以在請求發生重定向之前,將參數寫入 request 的屬性 OUTPUT_FLASH_MAP_ATTRIBUTE 中,這樣在重定向之后的 handler 中,Spring 會自動將其設置到 Model 中,這樣就可以從 Model 中取到我們傳遞的參數了。

          處理請求

          在九大組件初始化完成之后,Spring MVC 的初始化就完成了,接下來就是接收并處理請求了,那么處理請求的入口在哪里呢?處理請求的入口方法就是 DispatcherServlet 中的 doService 方法,而 doService 方法又會調用 doDispatch 方法。

          DispatcherServlet#doDispatch

          這個方法最關鍵的就是調用了 getHandler 方法,這個方法就是會獲取到前面九大組件中的 HandlerMapping,然后進行反射調用對應的方法完成請求,完成請求之后后續還會經過視圖轉換之類的一些操作,最終返回 ModelAndView,不過現在都是前后端分離,基本也不需要用到視圖模型,在這里我們就不分析后續過程,主要就是分析 HandlerMapping 的初始化和查詢過程。

          DispatcherServlet#getHandler

          這個方法里面會遍歷 handllerMappings,這個 handllerMappings 是一個 List 集合,因為 HandlerMapping 有多重實現,也就是 HandlerMapping 不止一個實現,其最常用的兩個實現為 RequestMappingHandlerMapping 和 BeanNameUrlHandlerMapping。

          AbstractHandlerMapping#getHandler

          AbstractHandlerMapping 是一個抽象類,其 getHandlerInternal 這個方法也是一個模板方法:

          getHandlerInternal 方法最終其會調用子類實現,而這里的子類實現會有多個,其中最主要的就是 AbstractHandlerMethodMapping 和 AbstractUrlHandlerMapping 兩個抽象類,那么最終到底會調用哪個實現類呢?

          這時候如果拿捏不準我們就可以看一下類圖,上面我們提到,HandlerMapper 有兩個非常主要的實現類:RequestMappingHandlerMapping 和 BeanNameUrlHandlerMapping。那么我們就分別來看一下這兩個類的類圖關系:

          可以看到,這兩個實現類的抽象父類正好對應了 AbstractHandlerMapping 的兩個子類,所以這時候具體看哪個方法,那就看我們想看哪種類型了。

          • RequestMappingHandlerMapping:主要用來存儲 RequestMapping 注解相關的控制器和 url 的映射關系。
          • BeanNameUrlHandlerMapping:主要用來處理 Bean name 直接以 / 開頭的控制器和 url 的映射關系。

          其實除了這兩種 HandlerMapping 之外,Spring 中還有其他一些 HandllerMapping,如 SimpleUrlHandlerMapping 等。

          提到的這幾種 HandlerMapping,對我們來說最常用,最熟悉的那肯定就是 RequestMappingHandlerMapping ,在這里我們就以這個為例來進行分析,所以我們應該

          AbstractHandlerMethodMapping#getHandlerInternal

          這個方法本身也沒有什么邏輯,其主要的核心查找 Handler 邏輯在 lookupHandlerMethod 方法中,這個方法主要是為了獲取一個 HandlerMethod 對象,前面的方法都是 Object,而到這里變成了 HandlerMethod 類型,這是因為 Handler 有各種類型,目前我們已經基本跟到了具體類型之下,所以類型就變成了具體類型,而如果我們看的的另一條分支線,那么返回的就會是其他對象,正是因為支持多種不同類型的 HandlerMapping 對象,所以最終為了統一執行,才會需要在獲得 Hanlder 之后,DispatcherServlet 中會再次通過調用 getHandlerAdapter 方法來進一步封裝成 HandlerAdapter 對象,才能進行方法的調用

          AbstractHandlerMethodMapping#lookupHandlerMethod

          這個方法主要會從 mappingRegistry 中獲取命中的方法,獲取之后還會經過一系列的判斷比較判斷比較,因為有些 url 會對應多個方法,而方法的請求類型不同,比如一個 GET 方法,一個 POST 方法,或者其他一些屬性不相同等等,都會導致最終命中到不同的方法,這些邏輯主要都是在 addMatchingMappings 方法去進一步實現,并最終將命中的結果加入到 matches 集合內。

          在這個方法中,有一個對象非常關鍵,那就是 mappingRegistry,因為最終我們根據 url 到這里獲取到對應的 HandlerMtthod,所以這個對象很關鍵:

          看這個對象其實很明顯可以看出來,這個對象其實只是維護了一些 Map 對象,所以我們可以很容易猜測到,一定在某一個地方,將 url 和 HandlerMapping 或者 HandlerMethod 的映射關系存進來了,這時候其實我們可以根據 getMappingsByUrl 方法來進行反推,看看 urlLookup 這個 Map 是什么時候被存入的,結合上面的類圖關系,一路反推,很容易就可以找到這個 Map 中的映射關系是 AbstractHandlerMethodMapping 對象的 afterPropertiesSet 方法實現的(AbstractHandlerMethodMapping 實現了 InitializingBean 接口),也就是當這個對象初始化完成之后,我們的 url 和 Handler 映射關系已經存入了 MappingRegistry 對象中的集合 Map 中。

          AbstractHandlerMethodMapping 的初始化

          afterPropertiesSet 方法中并沒有任何邏輯,而是直接調用了 initHandlerMethods。

          AbstractHandlerMethodMapping#initHandlerMethods

          initHandlerMethods 方法中,首先還是會從 Spring 的上下文中獲取所有的 Bean,然后會進一步從帶有 RequestMapping 注解和 Controller 注解中的 Bean 去解析并獲得 HandlerMethod。

          AbstractHandlerMethodMapping#detectHandlerMethods

          這個方法中,其實就是通過反射獲取到 Controller 中的所有方法,然后調用 registerHandlerMethod 方法將相關信息注冊到 MappingRegistry 對象中的各種 Map 集合之內:

          AbstractHandlerMethodMapping#register

          registerHandlerMethod 方法中會直接調用 AbstractHandlerMethodMapping 對象持有的 mappingRegistry 對象中的 regidter方法,這里會對 Controller 中方法上的一些元信息進行各種解析,比如參數,路徑,請求方式等等,然后會將各種信息注冊到對應的 Map 集合中,最終完成了整個初始化。

          總結

          本文重點以 RequestMappingHandlerMapping 為例子分析了在 Spring 當中如何初始化 HandlerMethod,并最終在調用的時候又是如何根據 url 獲取到對應的方法并進行執行最終完成整個流程。


          主站蜘蛛池模板: 精品黑人一区二区三区| 国产一区二区三区小向美奈子| 国产日韩一区二区三区在线观看| 无码国产精品一区二区免费模式 | 中文字幕一区二区人妻性色| 97人妻无码一区二区精品免费| 在线观看午夜亚洲一区| 亚洲AV无码一区二区三区网址| 国产免费一区二区视频| 国产在线观看一区二区三区精品 | 国产品无码一区二区三区在线蜜桃| 天堂Av无码Av一区二区三区| 少妇无码一区二区三区免费| 国产在线精品一区二区夜色| 香蕉久久AⅤ一区二区三区| 韩国精品一区视频在线播放| AA区一区二区三无码精片| 精品欧洲av无码一区二区14| 亚洲一区二区三区在线观看蜜桃| 色欲精品国产一区二区三区AV| 亚洲韩国精品无码一区二区三区| 国产精品一区二区av不卡| 亚洲av午夜精品一区二区三区| 日韩社区一区二区三区| 一区五十路在线中出| 亚洲一区二区三区国产精品| 国产一区二区视频在线播放| 国产人妖视频一区二区 | 欲色影视天天一区二区三区色香欲| 国产伦精品一区二区三区女| 亚欧在线精品免费观看一区 | 国产在线观看一区二区三区四区 | 亚洲午夜福利AV一区二区无码| 成人免费av一区二区三区| 国产一区二区精品久久凹凸| 色久综合网精品一区二区| 国产乱人伦精品一区二区| 天天综合色一区二区三区| 日韩动漫av在线播放一区| 国产精品熟女一区二区| 亚洲高清一区二区三区 |