SSM框架之Spring MVC小結

Spring MVC總結

1.Spring MVC的基本配置

1)web.xml配置

    <!--前端控制器-->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!-- contextConfigLocation:指定SpringMVC配置文件位置 -->
            <!-- 如果不指定配置文件位置,則會默認找尋一個文件:
            /WEB-INF/xxx-servlet.xml-----xxx爲在web中配置的前端控制器servlet名字 -->
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- servlet啓動加載,servlet原本是第一次訪問創建對象;
        load-on-startup:服務器啓動的時候創建對象;值越小優先級越高,越先創建對象;
         -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- Map all requests to the DispatcherServlet for handling -->
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <!--
            /*和/都是攔截所有請求; /:會攔截所有請求,但是不會攔截*.jsp;能保證jsp訪問正常;
            /*的範圍更大;還會攔截到*.jsp這些請求;一但攔截jsp頁面就不能顯示了;
          -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
<!--	encoding設置請求編碼,forceEncoding設置響應體編碼-->
<!-- 一定注意:字符編碼filter的位置一般都在其他Filter之前 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!-- encoding:指定解決POST請求亂碼,GET請求亂碼在大 -->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
         <!-- forceEncoding:解決響應亂碼(默認不解決響應亂碼):底層調用response.setCharacterEncoding(this.encoding); -->
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
<!--	支持rest風格編碼方式-->
    <filter>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Tips:攔截路徑/和/*區別
/:攔截所有請求(靜態資源,servlet),不攔截jsp頁面
/*:攔截所有請求,包括jsp頁面
       所有項目的小web.xml都是繼承於tomcat的大web.xml,在tomcat中DefaultServlet是處理靜態資源的(除jsp和servlet外都是靜態資源),而前端控制器的“/”禁用了tomcat服務器中的DefaultServlet
       但是沒有覆蓋服務器中的JspServlet的配置,大web中有一個jspservlet,設置的攔截路徑是.jsp,所以寫/能訪問到jsp頁面

2)springmvc配置文件

    <context:component-scan base-package="com"/>
<!--    視圖解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
<!--    解析靜態資源,如html,js等-->
    <mvc:default-servlet-handler/>
<!--    解析動態資源-->
    <mvc:annotation-driven></mvc:annotation-driven>

2.@RequestMapping詳解

1)用法

@RequestMapping註解標註在方法上,是告訴SpringMVC這個方法處理的請求路徑, /代表從當前項目下開始,可以省略
@RequestMapping註解標註在類上,是給類中所有的@RequestMapping標註的方法加一個基礎路徑

   @RequestMapping("/hello")
   public String myfirstRequest(){
      System.out.println("請求收到了....正在處理中");
      //視圖解析器自動拼串;
//    <property name="prefix" value="/WEB-INF/pages/"></property>
//    <property name="suffix" value=".jsp"></property>
      //   (前綴)/WEB-INF/pages/+返回值(success)+後綴(.jsp)
      return "success";
   }

2)@RequestMapping屬性

  • method:限定請求方式,默認是任何請求方式都接受
           HTTP協議中的所有請求方式:
                  【GET】,【POST】,HEAD, PUT, PATCH, DELETE, OPTIONS, TRACE
    GET、POST
           eg:method=RequestMethod.POST:只接受post類型的請求
  • params:規定請求參數
           params 和 headers支持簡單的表達式:
                  param1: 表示請求必須包含名爲 param1 的請求參數
           eg:params={“username”}:發送請求的時候必須帶上一個名爲username的參數;沒帶都會404
                  !param1: 表示請求不能包含名爲 param1 的請求參數
           eg:params={"!username"}:發送請求的時候必須不攜帶上一個名爲username的參數;帶了都會404
                  param1 != value1: 表示請求包含名爲 param1 的請求參數,但其值不能爲 value1
            eg:params={“username!=123”}:發送請求的時候;攜帶的username值必須不是123(不帶username或者username不是123)
  • headers:規定請求頭;也和params一樣能寫簡單的表達式
           User-Agent:瀏覽器信息;
                  eg:讓火狐能訪問,讓谷歌不能訪問
    谷歌User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
    火狐User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0
@RequestMapping(value="/hello",headers={"User-Agent=Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0"})
   public String handle04(){
        System.out.println("hello....");
        return "success";
   }
  • consumes:只接受內容類型是哪種的請求,即規定請求頭中的Content-Type
  • produces:告訴瀏覽器返回的內容類型是什麼,相當於給響應頭中加上Content-Type:text/html;charset=utf-8

3)@RequestMapping模糊匹配

優先級:?>*>**

  • ?:能替代任意一個字符,不能匹配空字符,路徑符“/”也不能匹配
    @RequestMapping("/antTest0?")
    public String antTest02(){
        System.out.println("antTest02...");
        return "success";
    }
  • 能替代任意多個字符,或一層路徑
    @RequestMapping("/a/*/antTest01")
    public String antTest04(){
        System.out.println("antTest04...");
        return "success";
    }
  • **:能替代多層路徑
    @RequestMapping("/a/**/antTest01")
    public String antTest05(){
        System.out.println("antTest05...");
        return "success";
    }
  • 路徑上可以有佔位符,佔位符可以在方法中獲取到
    語法:在任意路徑的地方寫一個{變量名},可以寫多個獲取多級路徑,egeg:/{haha}/{id}
    @RequestMapping("/user/{id}")
    public String pathVariableTest(@PathVariable("id")String id){
        System.out.println("路徑上的佔位符的值"+id);
        return "success";

    }

4)@RequestMapping獲取請求參數

(1)獲取請求參數:直接給方法入參上寫一個和請求參數名相同的變量

請求參數中有值,沒帶則爲null

<a href="handle01?user=lily">handle01</a><br/>
@RequestMapping("/hello")
public String handle01(String user) {
   System.out.println(user);//打印lily
}
(2)@RequestParam:獲取請求參數

@RequestParam(“user”) 相當於String username =request.getParameter(“user”)
如果在方法參數中標記了該註解,默認在請求中參數是必須帶該參數
value/name:請求參數名
required:參數是否必須要帶(可寫成http://…/hello?)
defaultValue:沒帶參數時的默認值

(3)@RequestHeader:獲取請求頭中某個key的值

@RequestHeader(“User-Agent”)相當於String userAgent userAgent =request.getHeader(“User-Agent”)
默認也是必須帶
和上面一樣有三個參數:value/required(是否要求請求頭中可以沒有value所對應的請求頭)/defaultValue

(4)@CookieValue:獲取某個cookie的值

@CookieValue(value=“JSESSIONID”,required=false)
相當於:Cookie[] cookies = request.getCookies(); (獲取全部cookies)
(然後遍歷)for(Cookie c:cookies){
if(c.getName().equals(“JSESSIONID”))
{String cv = c.getValue(); } }
也有三個參數:value/required(瀏覽器第一次訪問網站就沒有jsessionid)/defaultValue



    @RequestMapping("/handle01")
    public String handle02(
            @RequestParam(value = "user", required = false, defaultValue = "lily") String username,
            @RequestHeader(value = "AHAHA", required = false, defaultValue = "hhh") String userAgent,
            @CookieValue(value="JSESSIONID",required=false)String jid) {
        System.out.println("這個變量的值:" + username);
        System.out.println("請求頭中瀏覽器的信息:" + userAgent);
        System.out.println("cookie中的jid的值"+jid);
        return "success";
    }
(5)POJO(plain old java object):請求參數是一個POJO,SpringMVC會自動的爲這個POJO進行賦值

1)、將POJO中的每一個屬性,從請求參數中嘗試獲取出來,並封裝即可。要求請求參數的參數名和對象中的屬性名要一一對應
2)、還可以級聯封裝,即屬性的屬性
POJO對象

//該類一定要有無參構造器
public class Book {
   
   private String bookName;
   private String author;
   private Double price;
   private Integer stock;
   private Integer sales;
   private Address address;
   
   /**
    * @return the bookName
    */
   public String getBookName() {
      return bookName;
   }
   /**
    * @return the address
    */
   public Address getAddress() {
      return address;
   }
   /**
    * @param address the address to set
    */
   public void setAddress(Address address) {
      this.address = address;
   }
   /**
    * @param bookName the bookName to set
    */
   public void setBookName(String bookName) {
      this.bookName = bookName;
   }
   /**
    * @return the author
    */
   public String getAuthor() {
      return author;
   }
   /**
    * @param author the author to set
    */
   public void setAuthor(String author) {
      this.author = author;
   }
   /**
    * @return the price
    */
   public Double getPrice() {
      return price;
   }
   /**
    * @param price the price to set
    */
   public void setPrice(Double price) {
      this.price = price;
   }
   /**
    * @return the stock
    */
   public Integer getStock() {
      return stock;
   }
   /**
    * @param stock the stock to set
    */
   public void setStock(Integer stock) {
      this.stock = stock;
   }
   /**
    * @return the sales
    */
   public Integer getSales() {
      return sales;
   }
   /**
    * @param sales the sales to set
    */
   public void setSales(Integer sales) {
      this.sales = sales;
   }
   /* (non-Javadoc)
    * @see java.lang.Object#toString()
    */
   /* (non-Javadoc)
    * @see java.lang.Object#toString()
    */
   @Override
   public String toString() {
      return "Book [bookName=" + bookName + ", author=" + author + ", price="
            + price + ", stock=" + stock + ", sales=" + sales
            + ", address=" + address + "]";
   }
}

JSP頁面部分代碼

   書名:<input type="text" name="bookName"/><br/>
   作者:<input type="text" name="author"/><br/>
   價格:<input type="text" name="price"/><br/>
   庫存:<input type="text" name="stock"/><br/>
   銷量:<input type="text" name="sales"/><br/>
   省:<input type="text" name="address.province"/>
   市:<input type="text" name="address.city"/>
   街道:<input type="text" name="address.street"/><br/>
@RequestMapping("/book")
public String addBook(Book book){
   System.out.println("我要保存的圖書:"+book);
   return "success";
}
(6)直接在參數上寫原生API

原生API能使用的對象:
HttpServletRequest
HttpServletResponse
HttpSession
java.security.Principal(不用管)
Locale:國際化有關的區域信息對象
InputStream:ServletInputStream inputStream = request.getInputStream();
OutputStream:ServletOutputStream outputStream = response.getOutputStream();
Reader:BufferedReader reader = request.getReader();
Writer:PrintWriter writer = response.getWriter();

@RequestMapping("/handle03")
    public String handle03(HttpSession session,HttpServletRequest request,HttpServletResponse response) throws IOException {
        request.setAttribute("reqParam", "我是請求域中的");
        session.setAttribute("sessionParam", "額我是Session域中的");
        return "success";
    }
}

5)@RequestMapping數據輸出

(1)傳入Map\Model\ModelMap

此三者是同一對象(BindingAwareModelMap類型),即地址一樣,給這些參數裏面保存的所有數據都會放在請求域(requestscope)中,可以在頁面獲取,eg:${requestscope.username}

(2)方法的返回值可以爲ModelAndView

包含視圖信息(頁面地址,字符串)+模型數據(給頁面帶的數據)
而且數據是放在請求域中(request)(因爲請求域方便)

    @RequestMapping("/handle04")
    public ModelAndView handle04(){
        //之前的返回值我們就叫視圖名;視圖名視圖解析器是會幫我們最終拼串得到頁面的真實地址;
        //ModelAndView mv = new ModelAndView("success");
        ModelAndView mv = new ModelAndView();
        mv.setViewName("success");
        mv.addObject("msg", "你好哦!");
        return mv;
    }
(3)@SessionAttributes(只能標在類上):給Session域中保存數據的方式,在請求域(model等)中也同時保存

此方法將會給BindingAwareModelMap中保存的數據,或者ModelAndView中的數據,同時給session中放一份,即session和request中都有

@SessionAttributes(value=“msg”): value指定保存數據時要給session中放的數據的key,如果有多個鍵,用大括號括起來寫多個 value={“msg”,“haha”}

value=“msg”:只要保存的是這種key的數據,給Session中放一份,
types={String.class}:只要保存的是這種類型的數據,給Session中也放一份
兩個同時用取並集
jsp頁面取值:${Sessionscope.haha}

後來推薦@SessionAttributes就別用了,因爲如果沒有向其中保存值而又聲明瞭該註解,在取值的時候會報錯,如果要給session中放數據建議使用原生API

@SessionAttributes(value={"msg"},types={String.class})
@Controller
public class OutputController{...}
(4)@ModelAttribute:調用目標方法前會優先依次調用標註了該註解的方法

適用於並非全字段(即對象的所有字段)修改場景

例如要修改數據庫中的圖書信息,不應該是直接new一個Book對象,而是應該先從數據庫中查詢出該圖書對象,再對其進行修改:

@Controller
public class ModelAttributeTestController { 
     /**
     * @ModelAttribute:可以放在方法參數和方法聲明前兩個地方
     *      方法:這個方法就會提前於目標方法先運行;
     *          1)我們可以在這裏提前查出數據庫中圖書的信息
     *          2)將這個圖書信息保存起來(方便下一個方法還能使用)
     *      參數:取出剛纔保存的數據
     */
    @ModelAttribute
    public void hahaMyModelAttribute(Map<String, Object> map){
        Book book = BookService.getBookById(1);//先在數據庫中查到的book數據
        map.put("book", book);
    }


    /**
     * 可以告訴SpringMVC不要new這個book了我剛纔保存了一個book;
     * 哪個就是從數據庫中查詢出來的;用我這個book?@ModelAttribute("book")-
     *
     * model同都是BindingAwareModelMap並且和上面的map對象是同一個,所以在此處取出上面方法map放置的值是可以的
     */
    @RequestMapping("/updateBook")
    public String updateBook(@ModelAttribute("book")Book book,Map<String, Object> model){
        Book haha = (Book)model.get("book");
        //對圖書進行修改
        ...
        return "success";
    }
}

3.視圖解析器

視圖解析器解析方法返回值得到視圖對象
視圖對象完成真正的轉發(將模型數據全部放在請求域中)或者重定向到頁面,即視圖對象才能真正的渲染視圖

1) 在有視圖解析器的情況下轉發路徑寫法

//方法一:用相對路徑
   @RequestMapping("/hello")
   public String hello(){
      //  要想轉發到web下的hello:默認視圖解析器會拼串,即如果直接寫一個hello會解析成web-inf/pages/hello.jsp
      //所以,要寫相對路徑,就要退出兩層,即相對路徑寫法
      return "../../hello";
   }
//方法二:forward前綴
   /**
    *  forward:轉發到一個頁面
    *  /hello.jsp:轉發當前項目下的hello,“/代表是當前項目下”
    *  
    *  一定加上/,如果不加/就是相對路徑,容易出問題
    *  forward:/hello.jsp
    *  forward:前綴的轉發,不會由我們配置的視圖解析器拼串
    */
   @RequestMapping("/handle01")
   public String handle01(){
      System.out.println("handle01");
      return "forward:/hello.jsp";
   }


   /**
    * 轉發到handle01,handle01在轉發到hello
    */
   @RequestMapping("/handle02")
   public String handle02(){
      System.out.println("handle02");
      return "forward:/handle01";
   }

2) 有資源解析器的情況下,重定向路徑寫法

 /**
    * 重定向 redirect:重定向的路徑
    * /hello.jsp:代表就是從當前項目下開始;SpringMVC會爲路徑自動的拼接上項目名,原生的Servlet重定向“/路徑”需要加上項目名才能成功
    * 相當於response.sendRedirect("/hello.jsp")
    */
   @RequestMapping("/handle03")
   public String handle03(){
      System.out.println("handle03....");
      return "redirect:/hello.jsp";
   }
   
   @RequestMapping("/handle04")
   public String handle04(){
      System.out.println("handle04...");
      return "redirect:/handle03";
   }

有前綴的轉發和重定向操作,配置的視圖解析器就不會進行拼串

3)JstlView支持國際化

導包導入了jstl的時候會自動創建爲一個jstlView,jstlView可以快速方便的支持國際化功能;
即如果導入jstl包後,寫入下面的配置則解析器解析爲InternalResourceView的子類jstlView

maven座標:

<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

springmvc配置:

<!--InternalResourceViewResolver:優先級是最低的-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="prefix" value="/WEB-INF/pages/"></property>
   <property name="suffix" value=".jsp"></property>
   <!-- <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>  指定視圖類型,可以不寫-->
</bean>

步驟:

  1. 讓Spring管理國際化資源
    <!--配置一個資源文件管理器,id必須叫messageSource  -->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <!--  basename指定文件基礎名-->
        <property name="basename" value="i18n"></property>
    </bean>
  1. 直接去頁面使用<fmt:message>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>
<fmt:message key="welcomeinfo"/>
</h1>
<form action="">
   <fmt:message key="username"/>:<input /><br/>
   <fmt:message key="password"/>:<input /><br/>
   <input type="submit" value='<fmt:message key="loginBtn"/>'/>
</form>
</body>
</html>

注意:因爲是spring mvc的視圖解析器創建的jstlView,所以轉發的視圖一定要經過視圖解析器的解析,如果直接添加forward或redirect前綴,則國際化無效

4)mvc:view-controller:請求映射標籤,直接去到某個頁面

<!-- 發送一個請求("toLoginPage")將會直接來到web-inf下的login頁面-->
<!-- path="":指定哪個請求
    view-name:指定映射給哪個視圖;
    如果只配置mvc:view-controller,其他的註解的requestmapping就不好使-->
<mvc:view-controller path="/toLoginPage" view-name="login"/>

<!-- 所以要開啓mvc註解驅動模式-->
<mvc:annotation-driven></mvc:annotation-driven>

5)自定義視圖解析器/視圖類:實現ViewResolver、View接口

  • 自定義視圖解析器類:
//因爲還配置有Internel的解析器,要再繼承一個order接口改變其順序,讓其先於internel解析器完成解析,要不然進入internel中,internel就會拼串解析一個url然後報錯
public class MyCustomViewResolver implements ViewResolver,Ordered{
   private Integer order = 0;
   @Override
   public View resolveViewName(String viewName, Locale locale)
         throws Exception {
      // TODO Auto-generated method stub
      //根據視圖名返回視圖對象
      if(viewName.startsWith("custom:")){
         return new MyView();
      }else{
         //如果不能處理返回null即可
         return null;
      }
   }
   /**
    *
    */
   @Override
   public int getOrder() {
      // TODO Auto-generated method stub
      return order;
   }
   //改變視圖解析器的優先級
   public void setOrder(Integer order){
      this.order = order;
   }
}
  • 自定義視圖類
public class MyView implements View{
   /**
    * 返回的數據的內容類型
    */
   @Override
   public String getContentType() {
      return "text/html";
   }

//渲染界面
  @Override
   public void render(Map<String, ?> model, HttpServletRequest request,
         HttpServletResponse response) throws Exception {
      response.setContentType("text/html");
      List<String> vn = (List<String>) model.get("video");
      response.getWriter().write("哈哈<h1>即將展現精彩內容</h1>");
      for (String string : vn) {
         response.getWriter().write("<a>下載"+string+".avi</a><br/>");
      }
   }
}
  • springmvc配置文件
<context:component-scan base-package="com.atguigu"></context:component-scan>


<!--InternalResourceViewResolver:優先級是最低的
  -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="prefix" value="/WEB-INF/pages/"></property>
   <property name="suffix" value=".jsp"></property>
</bean>
<!--自定義的視圖解析器    value="1"數字越小優先級越高-->
<bean class="com.atguigu.view.MyMeiNVViewResolver">
   <property name="order" value="1"></property>
</bean>

4.REST風格請求

  • REST(Representational State Transfer):(資源)表現層狀態轉化。HTTP協議是一個無狀態協議,即所有的狀態都保存在服務器端。因此,如果客戶端資源想要操作服務器,必須通過某種手段,讓服務器端發生“狀態轉化”(State Transfer)。

具體說,就是 HTTP 協議裏面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET用來獲取資源,POST用來新建資源,PUT 用來更新資源,DELETE用來刪除資源。

  • 語法:
    GET、POST操作:直接發送請求即可
    DELETE、PUT操作:1、創建一個post類型的表單 2、表單項中攜帶一個_method的參數,3、這個_method的值就是DELETE、PUT<
<!-- 發送GET請求 -->
<a href="/hello">去hello</a>
<form method="get" action="${ctp}/query/1">
    <input type="submit" value="提交">
</form>
<!-- 發送POST請求 -->
<form method="post" action="${ctp }/add/4">
    <input type="submit" value="提交">
</form>
<!-- 發送DELETE請求 -->
<form method="post" action="${ctp }/delete/3">
    <input name="_method" value="delete"> <%--value(delete)不區分大小寫 --%>
    <input type="submit" value="提交">
</form>
<!-- 發送PUT請求 -->
<form method="post" action="${ctp }/update/2">
    <input name="_method" value="put">
    <input type="submit" value="提交">
</form>

JAVA代碼:

    @RequestMapping(value = "/hello")
    public String firstReq(){
        System.out.println("來到了hello");
        return "success";
    }

    @RequestMapping(value = "/query/{id}",method = RequestMethod.GET)
    public String queryTest(@PathVariable("id") int id){
        System.out.println("query"+id);
        return "success";
    }
    @RequestMapping(value = "/update/{id}",method = RequestMethod.PUT)
    public String updateTest(@PathVariable("id") int id){
        System.out.println("update"+id);
        return "success";
    }
    @RequestMapping(value = "/delete/{id}",method = RequestMethod.DELETE)
    public String deleteTest(@PathVariable("id") int id){
        System.out.println("delete"+id);
        return "success";
    }
    @RequestMapping(value = "/add/{id}",method = RequestMethod.POST)
    public String addTest(@PathVariable("id") int id){
        System.out.println("add"+id);
        return "success";
    }

注意:高版本Tomcat(8以上)Rest支持有點問題,轉發頁面success是還攜帶的是delete或put請求是不允許的,需要將轉發頁面設置成錯誤頁面
isErrorPage

5.數據轉換/格式化/校驗

在這裏插入圖片描述

1)自定義數據轉換器

(1)實現Converter接口,寫一個自定義的類型轉換器
public class MyStringToEmployeeConverter implements Converter<String, Employee> {
   @Autowired
   DepartmentDao departmentDao;
   /**
    * 自定義的轉換規則
    */
   @Override
   public Employee convert(String source) {
      // TODO Auto-generated method stub
      // [email protected]
      System.out.println("頁面提交的將要轉換的字符串" + source);
      Employee employee = new Employee();
      if (source.contains("-")) {
         String[] split = source.split("-");
         employee.setLastName(split[0]);
         employee.setEmail(split[1]);
         employee.setGender(Integer.parseInt(split[2]));
         employee.setDepartment(departmentDao.getDepartment(Integer.parseInt(split[3])));
      }
      return employee;
   }
}
(2)將自定義的Converter放進ConversionService中

因爲在spring中,ConversionService是ioc根據工廠方法ConversionServiceFactoryBean創建的,所以配置的時候也在工廠方法中配置

<!-- 告訴SpringMVC別用默認的ConversionService,
            而用我自定義的ConversionService、可能有我們自定義的Converter; -->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <!--converters轉換器中添加我們自定義的類型轉換器  -->
        <property name="converters">
            <set>
                <bean class="com.atguigu.component.MyStringToEmployeeConverter"></bean>
            </set>
        </property>
    </bean>
(3)讓SpringMVC用自定義ConversionService
<!-- conversion-service="conversionService":使用配置的類型轉換組件 -->
    <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

使用:JSP部分代碼

<%
   pageContext.setAttribute("ctp", request.getContextPath());
%>
<form action="${ctp}/quickadd">
   <!--將員工的所有信息都寫成固定的  -->
   <input name="empinfo" value="[email protected]"/>
   <input type="submit" value="快速添加"/>
</form>

查看源碼可以發現,源碼上WebDataBinder上的ConversionService組件就替換了,新增了一個java.lang.String -> com.atguigu.bean.Employee

ConversionService converters =
    java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@47d21368
    java.lang.Character -> java.lang.Number : org.springframework.core.convert.support.CharacterToNumberFactory@3cdad94b
    java.lang.Character -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@2a65d058
    java.lang.Enum -> java.lang.String : org.springframework.core.convert.support.EnumToStringConverter@5b63b99f
    java.lang.Number -> java.lang.Character : org.springframework.core.convert.support.NumberToCharacterConverter@1fee86c
    java.lang.Number -> java.lang.Number : org.springframework.core.convert.support.NumberToNumberConverterFactory@281e80cc
    java.lang.Number -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@7cd83820
    //新增
    java.lang.String -> com.atguigu.bean.Employee :  com.atguigu.component.MyStringToEmployeeConverter@3dc22c7a
    java.lang.String -> java.lang.Boolean : org.springframework.core.convert.support.StringToBooleanConverter@4331ec46
    java.lang.String -> java.lang.Character : org.springframework.core.convert.support.StringToCharacterConverter@76a136a0
    ....

2)數據格式化

指定數據提交的格式,比如指定日期格式必須爲2017-12-19,如果提交格式不是這個格式,就會報錯(錯誤碼:400)

如果設置自定義轉換器ConversionServiceFactoryBean創建的ConversionService組件,是沒有格式化器存在的;默認的轉換器纔有格式化器

解決方法:在springmvc的配置文件中,將ConversionServiceFactoryBean更改爲FormattingConversionServiceFactoryBean,裏面除了可以自定義轉換器,還保留格式化器

<!-- conversion-service="conversionService":使用我們自己配置的類型轉換組件 -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>


<!-- 以後寫自定義類型轉換器的時候,就使用FormattingConversionServiceFactoryBean來註冊;既具有類型轉換還有格式化功能 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
   <!--converters轉換器中添加我們自定義的類型轉換器  -->
   <property name="converters">
      <set>
         <bean class="com.atguigu.component.MyStringToEmployeeConverter"></bean>
      </set>
   </property>
</bean>

日期格式化:


@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birth = new Date();

數字格式化:

//假設頁面,爲了顯示方便提交的工資是  ¥10,000.98
@NumberFormat(pattern="#,###.##")//每個#表示一位,即每隔三位就以逗號分隔,小數點後面保留兩位小數
private Double salary;

3)數據校驗

對於在重要數據一定要加上後端驗證,因爲只做前端校驗是不安全的:比如瀏覽器可以禁用js,也可以另外自己寫一個頁面提交到後端處理器,直接跳過自己寫的前端頁面

JSR303(Java Specification Requests第303號標準提案)是Java爲Bean數據合法性校驗提供的框架,通過在Bean的屬性上添加註解指定校驗規則,並通過指定的接口實現校驗
實現之一:Hibernate Validator(第三方校驗框架)

(1)註解種類:

在這裏插入圖片描述
此外,Hibernate Validator除支持所有標準的校驗註解外,它還支持以下的擴展註解
在這裏插入圖片描述

(2)校驗步驟

1.添加依賴jar包

hibernate-validator-5.0.0.CR2.jar
hibernate-validator-annotation-processor-5.0.0.CR2.jar
classmate-0.8.0.jar
jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar

或maven依賴項

<dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.1.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.1-b11</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator-cdi</artifactId>
        <version>6.1.0.Final</version>
    </dependency>

2.給JavaBean對象屬性添加註解

//直接用message屬性指定發生錯誤時的提示消息,但是不能國際化
@NotEmpty(message="非空")
@Length(min=5,max=17,message="長度錯誤")
private String lastName;

@Email
private String email;

//規定頁面提交的日期格式  
//@Past:必須是一個過去的時間
//@Future :必須是一個未來的時間
@DateTimeFormat(pattern="yyyy-MM-dd")
@Past
private Date birth;

3.給參數中的JavaBean對象添加註解@Valid,通知springMVC這個對象需要校驗
獲取校驗結果:給需要校驗的javaBean後面緊跟一個BindingResult(兩者中間不能有任何其他的東西,必須是緊貼着的)。這個BindingResult就是封裝前一個bean的校驗結果

@RequestMapping(value = "/emp", method = RequestMethod.POST)
public String addEmp(@Valid Employee employee, BindingResult result)
{
   System.out.println("要添加的員工:" + employee);
   // 獲取是否有校驗錯誤
   boolean hasErrors = result.hasErrors();
   if (hasErrors) {
      System.out.println("有校驗錯誤");
      return "add";
   } else {
      employeeDao.save(employee);
      // 返回列表頁面;重定向到查詢所有員工的請求
      return "redirect:/emps";
   }
}

4.在頁面獲取校驗錯誤
使用原生標籤時:則直接在隱含模型中存值,在頁面中用EL表達式取出即可

  List<FieldError> errors = result.getFieldErrors();
      for (FieldError fieldError : errors) {
         errorsMap.put(fieldError.getField(),
               fieldError.getDefaultMessage());
      }
      model.addAttribute("errorInfo", errorsMap);

使用表單標籤時:<form:errors path=“每個表單提交項的name值”/>

<form:input path="lastname"/><form:errors path="lastname"/>
<form:input path="email"/><form:errors path="email"/>

補充表單標籤:
       對於請求域數據進行頁面回顯時,原來是在servelt中通過在域中添加屬性,再在頁面進行EL取值
       現在通過 SpringMVC的表單標籤實現將模型數據中的屬性和HTML表單元素相綁定,以實現表單數據更便捷編輯和表單值的回顯

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!-- 表單標籤寫法 -->
<form:form action="${ctp }/emp" modelAttribute="employee" method="POST">
   <!-- path就是原來html-input的name項,且能自動回顯隱含模型中某個對象對應的這個屬性的值 -->
   lastName:<form:input path="lastName"/><br/>
   email:<form:input path="email"/><br/>
   gender:<br/>
      男:<form:radiobutton path="gender" value="1"/><br/>
      女:<form:radiobutton path="gender" value="0"/><br/>
   dept:
      <!--
      items="":指定要遍歷的集合 ;遍歷出的每一個元素是一個department對象
      itemLabel="屬性名":指定遍歷出的這個對象的哪個屬性是作爲option標籤體的值
      itemValue="屬性名":指定剛纔遍歷出來的這個對象的哪個屬性是作爲要提交 的value值
       -->
      <form:select path="department.id"
         items="${depts}"
         itemLabel="departmentName"
         itemValue="id"></form:select><br/>
   <input type="submit" value="保存"/>
</form:form>

Spring MVC規定:path指定屬性是從隱含模型(請求域中)取出的某個對象中的屬性並且
path指定的每一個屬性,請求域中必須有一個對象,擁有這個屬性,這個對象在請求域就的key爲command
如果想要自定義key的名稱,則在表單標籤中使用modelAttribute來表明需要回顯的對象名稱

@RequestMapping("/toaddpage")
public String toAddPage(Model model) {
   // 1、先查出所有部門
   Collection<Department> departments = departmentDao.getDepartments();
   // 2、放在請求域中
   model.addAttribute("depts", departments);
   // 未使用modelAttribute屬性:model.addAttribute("command", new Employee(){.....});
   model.addAttribute("employee", new Employee(){.....});
   // 3、去添加頁面
   return "add";
}
(3)自定義國際化錯誤消息

       每個屬性在數據綁定和數據校驗發生錯誤時,都會生成一個對應的 FieldError 對象。當一個屬性校驗失敗後,校驗框架會爲該屬性生成 4 個消息代碼,這些代碼以校驗註解類名爲前綴,結合 modleAttribute、屬性名及屬性類型名生成多個對應的消息代碼。
       例如:User 類中的 password 屬性標註了一個 @Pattern 註解,當該屬性值不滿足 @Pattern 所定義的規則時, 就會產生以下 4 個錯誤代碼:
Pattern.user.password
Pattern.password
Pattern.java.lang.String
Pattern
       當使用 Spring MVC 標籤顯示錯誤消息時, Spring MVC 會查看 WEB 上下文是否裝配了對應的國際化消息,如果沒有,則顯示默認的錯誤消息,否則使用國際化消息。
       若數據類型轉換或數據格式轉換時發生錯誤,或該有的參數不存在,或調用處理方法時發生錯誤,都會在隱含模型中創建錯誤消息。其錯誤代碼前綴說明如下:

  1. required:必要的參數不存在。如 @RequiredParam(“param1”) 標註了一個入參,但是該參數不存在
  2. typeMismatch:在數據綁定時,發生數據類型不匹配的問題
  3. methodInvocation:Spring MVC 在調用處理方法時發生了錯誤

如果是hibernate的自己的註解,是做好了國際化的,但JAVAEE的校驗規則沒有做國際化,如果需要實現國際化,則:

a.國際化文件中錯誤消息的key必須對應一個錯誤代碼(codes):
      Email.employee.email,      校驗規則.隱含模型中這個對象的key.對象的屬性
      Email.email,               校驗規則.屬性名
      Email.java.lang.String,    校驗規則.屬性類型
      Email

解釋:1、如果是隱含模型中employee對象的email屬性字段發生了@Email校驗錯誤,就會生成 Email.employee.email;
2、 Email.email:所有的email屬性只要發生了@Email錯誤;
3、 Email.java.lang.String:只要是String類型屬性發生了@Email錯誤
4、 Email:只要發生了@Email校驗錯誤;
tips:如果同時用兩個,會優先用精確的那一個錯誤消息。從上到下越來越模糊。

因此國際化文件編寫:

Email=email error!
NotEmpty=must not empty!
Length.java.lang.String= length incorrect ,must between {2} and {1}!
Past=must a past time!
typeMismatch.birth=birth format error!
b.讓SpringMVC管理國際化資源文件
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="errors"></property>
</bean>
c.高級國際化:動態傳入消息參數

{0}:永遠都是當前註解下的屬性名
{1}、{2}:纔是註解後面的參數,而且參數按照字符串順序排序1,2…
即對於:

@Length(min=5,max=17,message="長度錯誤")
private String lastName;

提取參數時爲,纔會顯示“5and17”

Length.java.lang.String= length incorrect ,must between {2} and {1}

6.SpringMVC ajax響應和封裝json數據

       使用 HttpMessageConverter 將請求信息轉化並綁定到處理方法的入參中或將響應結果轉爲對應類型的響應信息,Spring 提供了兩種途徑:
1.使用 @RequestBody / @ResponseBody 對處理方法進行標註
2.使用 HttpEntity / ResponseEntity 作爲處理方法的入參或返回值
       當控制器處理方法使用到@RequestBody、 @ResponseBody 或HttpEntity、ResponseEntity 時, Spring 首先根據請求頭或響應頭的 Accept 屬性選擇匹配的HttpMessageConverter, 進而根據參數類型或泛型類型的過濾得到匹配的 HttpMessageConverter, 若找不到可用的HttpMessageConverter 將報錯。

1)SpringMVC-ajax jar包:

jackson-annotations-2.1.5.jar
jackson-core-2.1.5.jar
jackson-databind-2.1.5.jar

maven依賴:

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency> 

2)ajax返回json數據:在@RequestMapping註解上加@ResponseBody,則直接返回json數據

部分jsp:

<%
   pageContext.setAttribute("ctp", request.getContextPath());
%>
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
</head>
<body>
<%=new Date() %>
<a href="${ctp }/getallajax">ajax獲取所有員工</a><br/>
<div>
</div>
<script type="text/javascript">
   $("a:first").click(function(){
      //1、發送ajax獲取所有員工上
      $.ajax({
         url:"${ctp}/getallajax",
         type:"GET",
         success:function(data){
            $.each(data,function(){
               var empInfo = this.lastName+"-->"+this.birth+"--->"+this.gender;
               $("div").append(empInfo+"<br/>");
            });
         }
      });
      return false;//要禁用掉超鏈接的默認行爲,要不然會以超鏈接跳轉而並非發送ajax
   });
</script>
</body>
/**
* @ResponseBody:將返回的數據()放在響應體中
* 如果是對象,jackson包自動將對象轉爲json格式
*/
@ResponseBody  
@RequestMapping("/getallajax")
public Collection<Employee> ajaxGetAll(){
   Collection<Employee> all = employeeDao.getAll();
   return all;
}

3)@RequestBody用法

(1)獲取請求體

只有POST請求方式纔會有請求體

<form action="${ctp}/testRequestBody" method="post">
   <input name="username" value="tomcat" /> 
<input name="password" value="123456">
<input type="submit" />
</form>

   @RequestMapping("/testRequestBody")
   public String testRequestBody(@RequestBody String str){
      System.out.println("請求體:"+str);
      return "success";
   }
(2)接受json數據並封裝爲對象
<%
   pageContext.setAttribute("ctp", request.getContextPath());
%>
</head>
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
   $("a:first").click(function() {
      //點擊發送ajax請求,請求帶的數據是json
      var emp = {
         lastName : "張三",
         email : "[email protected]",
         gender : 0
      };
      //js對象
      var empStr = JSON.stringify(emp);
      $.ajax({
         url : '${ctp}/testRequestBody',
         type : "POST",
         data : empStr,
         contentType : "application/json",
         success : function(data) {
            alert(data);
         }
      });
      return false;
   });
</script>
@Controller
public class AjaxTestController {
   
   @Autowired
   EmployeeDao employeeDao;
   @RequestMapping("/testRequestBody")
   public String testRequestBody(@RequestBody Employee employee){ //@RequestBody標註下,json包直接將數據轉爲json格式
      System.out.println("請求體:"+employee);
      return "success";
   }
}

3)HttpEntity 獲取請求體+請求頭

/**
* 比@RequestBody更強,不僅可以拿到請求體,還可以拿到請求頭
* 而@RequestHeader("")只能拿到某個請求頭
*/
@RequestMapping("/test02")
public String test02(HttpEntity<String> str){
   System.out.println(str);
   return "success";
}

4)ResponseEntity自定義響應體內容

/**
* 返回string時,加@ResponseBody將返回數據放在響應體中,所以return時,寫的內容會直接顯示在頁面上
* 返回ResponseEntity<String>:設置響應體和響應頭內容,泛型指的是響應體中內容的類型
*/
//@ResponseBody
/**
* SpringMVC文件下載;
*/
@RequestMapping("/download")
public ResponseEntity<byte[]> download(HttpServletRequest request) throws Exception{
   //1、得到要下載的文件的流,找到要下載的文件的真實路徑
   ServletContext context = request.getServletContext();
   String realPath = context.getRealPath("/scripts/jquery-1.9.1.min.js");
   FileInputStream is = new FileInputStream(realPath);
   byte[] tmp = new byte[is.available()];
   is.read(tmp);
   is.close();
   //2、將要下載的文件流返回
   HttpHeaders httpHeaders = new HttpHeaders();
   httpHeaders.set("Content-Disposition", "attachment;filename="+"jquery-1.9.1.min.js");
   return new ResponseEntity<byte[]>(tmp, httpHeaders, HttpStatus.OK);
}

7.攔截器

配置攔截器

SpringMVC的攔截器HandlerInterceptor允許運行目標方法之前進行一些攔截工作,或者目標方法運行之後進行一些其他處理,類似於javaWeb的Filte。

(1)實現HandlerInterceptor 接口
public class MyFirstInterceptor implements HandlerInterceptor {
   /**
    * 目標方法運行之前運行,返回true則放行執行目標方法,返回false則後面的都會被攔截
    */
   @Override
   public boolean preHandle(HttpServletRequest request,
         HttpServletResponse response, Object handler) throws Exception {
      System.out.println("MyFirstInterceptor...preHandle...");
      return true;
   }
   @Override
   public void postHandle(HttpServletRequest request,
         HttpServletResponse response, Object handler,
         ModelAndView modelAndView) throws Exception {
      System.out.println("MyFirstInterceptor...postHandle...");
   }
   @Override
   public void afterCompletion(HttpServletRequest request,
         HttpServletResponse response, Object handler, Exception ex)
         throws Exception {
      System.out.println("MyFirstInterceptor...afterCompletion");
   }
}
(2)springmvc配置攔截器
<!-- 測試攔截器 -->
<mvc:interceptors>
   <!--配置某個攔截器,默認是攔截所有請求的 -->
   <bean class="com.controller.MyFirstInterceptor"></bean>
   <!-- 配置某個攔截器更詳細的信息,只來攔截test01請求 -->
   <mvc:interceptor>
      <mvc:mapping path="/test01"/>
      <bean class="com.controller.MySecondInterceptor"></bean>
   </mvc:interceptor>
</mvc:interceptors>

測試

@Controller
public class InterceptorTestController {
   @RequestMapping("/test01")
   public String test01(){
      System.out.println("test01....");
      //int i =10/0;
      return "success";
   }
}
(3)攔截器運行流程

正常情況下:
攔截器的preHandle------目標方法-----攔截器postHandle-----頁面(資源)-------攔截器的afterCompletion

MyFirstInterceptor...preHandle...
test01....
MyFirstInterceptor...postHandle...
success.jsp....
MyFirstInterceptor...afterCompletion

只要preHandle不放行就沒有以後的流程,但只要放行了,afterCompletion都會執行。即使目標方法報錯了,postHandle不會執行,但afterCompletion會執行。
對於多個攔截器,對於前面已經放行了的攔截器的afterCompletion總會執行

(4)多個攔截器執行順序:與過濾器相同

先進來的後出去:

MyFirstInterceptor...preHandle...
MySecondInterceptor...preHandle...
test01....
MySecondInterceptor...postHandle...
MyFirstInterceptor...postHandle...
success.jsp....
MySecondInterceptor...afterCompletion...
MyFirstInterceptor...afterCompletion

8.異常處理

SpringMVC默認異常處理器(HandlerExceptionResolver):
ExceptionHandlerExceptionResolver(配置了mvc:annotation-driven)、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver

1)ExceptionHandlerExceptionResolver

在方法上標註@ExceptionHandler,告訴SpringMVC這個方法專門處理的異常類類型
注意:1.要攜帶異常信息不能給參數位置寫Model,只能寫Exception,但是可以將異常信息寫在ModelAndView返回
2.一個類中可以有多個方法標註@ExceptionHandler,如果有多個@ExceptionHandler都能處理這個異常,精確異常優先
3.可以寫一個類,集中處理所有異常。全局異常處理與本類同時存在,本類優先,即使本類的異常類比全局的大,還是會執行本類而不執行異常類

(1)本類異常方法
//頁面上獲取異常信息:${exception}
@Controller
public class ExceptionTestController {
@ExceptionHandler(value = { Exception.class })
public String handleException01(Exception exception) {
   System.out.println("本類的:handleException01..." + exception);
   // 視圖解析器拼串
   return "error";
}

//頁面上獲取異常信息:${ex}
@ExceptionHandler(value = { ArithmeticException.class})
public ModelAndView handleException01(Exception exception) {
   System.out.println("本類的:handleException01..." + exception);
   ModelAndView view = new ModelAndView("myerror");
   view.addObject("ex", exception);
   // 視圖解析器拼串
   return view;
}
}

(2)異常處理類
/**
* 集中處理所有異常
* 1、集中處理所有異常的類(MyJiZhongException)需要加入到ioc容器中,用@ControllerAdvice標註專門處理異常的類
*/
@ControllerAdvice
public class MyJiZhongException {
   @ExceptionHandler(value={ArithmeticException.class})
   public ModelAndView handleException01(Exception exception){
      System.out.println("全局的:handleException01..."+exception);
      //
      ModelAndView view = new ModelAndView("myerror");
      view.addObject("ex", exception);
      //視圖解析器拼串
      return view;
   }
   
}

2)自定義異常:ResponseStatusExceptionResolver—@ResponseStatus

自定義一個異常類,標註@ResponseStatus

@ResponseStatus(reason="用戶被拒絕登陸",value=HttpStatus.NOT_ACCEPTABLE)
public class UserNameNotFoundException extends RuntimeException {
   private static final long serialVersionUID = 1L;
}

測試代碼:

@RequestMapping("/handle02")
public String handle02(@RequestParam("username") String username) {
   if (!"admin".equals(username)) {
      System.out.println("登陸失敗....");
      throw new UserNameNotFoundException();
   }
   System.out.println("登陸成功!。。。");
   return "success";
}

錯誤頁面:
在這裏插入圖片描述

3)判斷是否SpringMVC自帶的異常DefaultHandlerExceptionResolver

前面兩個異常都沒有處理的話,就會到此異常來
Spring自己的異常:如:HttpRequestMethodNotSupportedException–規定必須以post請求方式訪問

@RequestMapping(value="/handle03",method=RequestMethod.POST)
public String handle03(){
   return "success";
}

3)SimpleMappingExceptionResolver:通過配置的方式進行異常處理

<context:component-scan base-package="com"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="prefix" value="/WEB-INF/pages/"></property>
   <property name="suffix" value=".jsp"></property>
</bean>
<mvc:default-servlet-handler/>
<mvc:annotation-driven></mvc:annotation-driven>

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
   <!-- exceptionMappings:配置哪些異常去哪些頁面 -->
   <property name="exceptionMappings">
      <props>
         <!-- key:異常全類名;value:要去的頁面視圖名 -->
         <prop key="java.lang.NullPointerException">myerror</prop>
      </props>
   </property>
   <!--指定錯誤信息取出時使用的key  -->
   <!-- 因爲在錯誤頁面用的是ex取錯誤信息,默認是exception,即如果不寫這一行代碼,則在頁面中用${exception}也能取出 -->
   <property name="exceptionAttribute" value="ex"></property> 
</bean>

9.總結

1)SpringMVC運行流程

1、所有請求到前端控制器(DispatcherServlet),DispatcherServlet調用doDispatch進行處理
2、根據HandlerMapping中保存的請求映射信息找到,處理當前請求的,處理器執行鏈(包含攔截器+處理器方法)
3、根據當前處理器找到他的HandlerAdapter(適配器)
4、攔截器的preHandle先執行
5、適配器執行目標方法,並返回ModelAndView
1)、ModelAttribute註解標註的方法提前運行
2)、執行目標方法的時候(確定目標方法用的參數)
1)、有註解
2)、沒註解:
1)、 看是否Model、Map以及其他的
2)、如果是自定義類型
1)、從隱含模型中看有沒有,如果有就從隱含模型中拿
2)、如果沒有,再看是否SessionAttributes標註的屬性,如果是從Session中拿,如果拿不到會拋異常
3)、都不是,就利用反射創建對象
6、攔截器的postHandle執行
7、處理結果;(頁面渲染流程)
1)、如果有異常使用異常解析器處理異常;處理完後還會返回ModelAndView
2)、調用render進行頁面渲染
1)、視圖解析器根據視圖名得到視圖對象
2)、視圖對象調用render方法;
3)、執行攔截器的afterCompletion;

2)Spring和SpringMVC整合

SpringMVC的配置文件就來配置和網站轉發邏輯以及網站功能有關的(視圖解析器,文件上傳解析器,支持ajax等等);
Spring的配置文件來配置和業務有關的(事務控制,數據源等等);
整合方法在springMVC配置文件中:可以合併配置文件;此時Spring爲父容器,SpringMVC爲子容器,父容器不能使用子容器組件,但子容器可以使用父容器組件
SpringMVC和Spring分容器;
Spring.xml管理業務邏輯組件;

<context:component-scan base-package="com">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>

SpringMVC.xml管理控制器組件;

<context:component-scan base-package="com" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章