SpringMVC核心技術

1、請求轉發和重定向

當處理器對請求處理完畢後,向其它資源進行跳轉時,有兩種跳轉方式:請求轉發與重定向。而根據所要跳轉的資源類型,又可分爲兩類:跳轉到頁面與跳轉到其它處理器html

注意,對於請求轉發的頁面,能夠是WEB-INF中頁面;而重定向的頁面,是不能爲WEB-INF中頁的。由於重定向至關於用戶再次發出一次請求,而用戶是不能直接訪問 WEB-INF 中資源的java

SpringMVC 框架把原來 Servlet 中的請求轉發和重定向操做進行了封裝。如今可使用簡單的方式實現轉發和重定向。web

forward:表示轉發,實現 request.getRequestDispatcher("xx.jsp").forward()spring

redirect:表示重定向,實現 response.sendRedirect("xxx.jsp")數據庫

1. 請求轉發

處理器方法返回 ModelAndView 時,需在 setViewName()指定的視圖前添加 forward:,且此時的視圖再也不與視圖解析器一同工做,這樣能夠在配置瞭解析器時指定不一樣位置的視圖。視圖頁面必須寫出相對於項目根的路徑。forward 操做不須要視圖解析器。
處理器方法返回 String,在視圖路徑前面加入 forward: 視圖完整路徑數組

仍是第一個案例中瀏覽器

/**
     * 處理器方法返回ModelAndView,實現轉發forward
     * 語法: setViewName("forward:視圖文件完整路徑")
     * forward特色: 不和視圖解析器一同使用,就當項目中沒有視圖解析器,不受視圖解析器的限制了
     */

    @RequestMapping(value = "/doForward.do")
    public ModelAndView doForward(String name , Integer age){

        ModelAndView mv = new ModelAndView();

        mv.addObject("myname",name);
        mv.addObject("myage",age);

//        mv.setViewName("show");

        //顯式轉發
//        mv.setViewName("forward:/WEB-INF/view/show.jsp");


        // 配置了視圖解析器,可是這個文件不在view目錄之下就可使用這樣的方法,由於forward不受視圖解析器的限制
        mv.setViewName("forward:/hello.jsp");

        return mv;
    }

2. 請求重定向

在處理器方法返回的視圖字符串的前面添加 redirect:,則可實現重定向跳轉spring-mvc

/**
     * 處理器方法返回ModelAndView,實現重定向redirect
     * 語法:setViewName("redirect:視圖完整路徑")
     * redirect特色: 不和視圖解析器一同使用,就當項目中沒有視圖解析器
     *
     * 框架對重定向的操做:
     * 1.框架會把Model中的簡單類型的數據,轉爲string使用,做爲hello.jsp的get請求參數使用。
     *   目的是在 doRedirect.do 和 hello.jsp 兩次請求之間傳遞數據
     *
     * 2.在目標hello.jsp頁面可使用參數集合對象 ${param}獲取請求參數值
     *    ${param.myname}
     *
     * 3.重定向不能訪問/WEB-INF資源
     */

    @RequestMapping(value = "/doRedirect.do")
    public ModelAndView doRedirect(String name , Integer age){

        ModelAndView mv = new ModelAndView();

        mv.addObject("myname",name);
        mv.addObject("myage",age);


        //重定向
        mv.setViewName("redirect:/hello.jsp");
        // 至關於這樣,內部做爲get請求,經過param能夠獲取的到
        //http://localhost:8080/08_forword/hello.jsp?myname=lisi&myage=22


        //重定向不能訪問/WEB-INF資源
//        mv.setViewName("redirect:/WEB-INF/view/show.jsp");

        return mv;
    }

2、異常處理

SpringMVC 框架處理異常的經常使用方式:使用@ExceptionHandler 註解處理異常tomcat

使用註解@ExceptionHandler 能夠將一個方法指定爲異常處理方法。該註解只有一個可選屬性 value,爲一個 Class<?>數組,用於指定該註解的方法所要處理的異常類,即所要匹配的異常服務器

而被註解的方法,其返回值能夠是 ModelAndView、String,或 void,方法名隨意,方法參數能夠是 Exception 及其子類對象、HttpServletRequest、HttpServletResponse 等。系統會自動爲這些方法參數賦值

1. 異常處理步驟:

  1. 新建maven web項目
  2. 加入依賴
  3. 新建一個自定義異常類 MyUserException , 再定義它的子類NameException ,AgeException
  4. 在controller拋出NameException , AgeException
  5. 建立一個普通類,做用全局異常處理類
    • 在類的上面加入@ControllerAdvice
    • 在類中定義方法,方法的上面加入@ExceptionHandler
  6. 建立處理異常的視圖頁面
  7. 建立springmvc的配置文件
    • 組件掃描器 ,掃描@Controller註解
    • 組件掃描器,掃描@ControllerAdvice所在的包名
    • 聲明註解驅動

1. 自定義異常類

MyUserException是父類

package com.md.exception;

/**
 * @author MD
 * @create 2020-08-14 20:31
 */
public class MyUserException extends Exception {
    public MyUserException() {
        super();
    }

    public MyUserException(String message) {
        super(message);
    }
}

//-------------------------------

package com.md.exception;

/**
 * 當用戶的年齡有異常拋出AgeException
 * @author MD
 * @create 2020-08-14 20:33
 */
public class AgeException extends MyUserException {
    public AgeException() {
        super();
    }

    public AgeException(String message) {
        super(message);
    }
}
//----------------------------
package com.md.exception;

/**
 * 當用戶的姓名有異常拋出NameException
 * @author MD
 * @create 2020-08-14 20:32
 */
public class NameException extends MyUserException {
    public NameException() {
        super();
    }

    public NameException(String message) {
        super(message);
    }
}

2. 修改 Controller 拋出異常

/**
     *此時拋出的異常是下面兩個異常的父類
     */
    @RequestMapping(value = "/some.do")
    public ModelAndView doSome(String name , Integer age) throws MyUserException {

        ModelAndView mv = new ModelAndView();

        mv.addObject("myname",name);
        mv.addObject("myage",age);
        // 根據請求參數拋出異常
        if (!"md".equals(name)){
            throw new NameException("姓名不正確");

        }

        if (age == null || age > 100){
            throw new AgeException("年齡有誤");
        }

        mv.setViewName("show");


        return mv;
    }

3. 定義全局異常處理類

package com.md.handler;

import com.md.exception.AgeException;
import com.md.exception.NameException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author MD
 * @create 2020-08-14 20:38
 */


/**
 * @ControllerAdvice : 控制器加強(也就是說給控制器類增長功能--異常處理功能)
 *  位置:在類的上面。
 *  特色:必須讓框架知道這個註解所在的包名,須要在springmvc配置文件聲明組件掃描器。
 *  指定@ControllerAdvice所在的包名
 */
@ControllerAdvice
public class GlobalException {

    //定義方法,處理髮生的異常
    /*
        處理異常的方法和控制器方法的定義同樣, 能夠有多個參數,能夠有ModelAndView,
        String, void,對象類型的返回值

        形參:Exception,表示Controller中拋出的異常對象。
        經過形參能夠獲取發生的異常信息。

        @ExceptionHandler(異常的class):表示異常的類型,當發生此類型異常時,
        由當前方法處理
     */


    @ExceptionHandler(value = NameException.class)
    public ModelAndView doNameException(Exception ex) {
        // 處理NameException異常

        /*
           異常發生處理邏輯:
           1.須要把異常記錄下來, 記錄到數據庫,日誌文件。
             記錄日誌發生的時間,哪一個方法發生的,異常錯誤內容。
           2.發送通知,把異常的信息經過郵件,短信,微信發送給相關人員。
           3.給用戶友好的提示。
         */

        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "姓名是md,其餘用戶不能訪問");
        mv.addObject("ex", ex);
        mv.setViewName("nameError");

        return mv;
    }




    @ExceptionHandler(value = AgeException.class)
    public ModelAndView doAgeException(Exception ex) {
        // 處理AgeException異常

        /*
           異常發生處理邏輯:
           1.須要把異常記錄下來, 記錄到數據庫,日誌文件。
             記錄日誌發生的時間,哪一個方法發生的,異常錯誤內容。
           2.發送通知,把異常的信息經過郵件,短信,微信發送給相關人員。
           3.給用戶友好的提示。
         */

        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "年齡過大");
        mv.addObject("ex", ex);
        mv.setViewName("AgeError");

        return mv;
    }


    // 這個只能有一個,也就是萬能的異常處理方法
    // 處理其餘異常,NameException、AgeException以外的異常,也就是除了自定義以外的異常都能處理
    @ExceptionHandler
    public ModelAndView doOtherException(Exception ex) {
        // 處理OtherException異常

        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "404");
        mv.addObject("ex", ex);
        mv.setViewName("defaultError");

        return mv;
    }

}

4. 定義異常響應頁面

nameError.jsp

<%--
  Created by IntelliJ IDEA.
  User: MD
  Date: 2020/8/14
  Time: 20:54
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>nameError.jsp</h1>

    <h2>提示:${msg}</h2>

    <h2>系統異常信息:${ex.message}</h2>
</body>
</html>
<h1>ageError.jsp</h1>

<h2>提示:${msg}</h2>

<h2>系統異常信息:${ex.message}</h2>

<%--和上面都同樣,這裏就省略了--%>

<h1>defaultError.jsp</h1>

<h2>提示:${msg}</h2>

<h2>系統異常信息:${ex.message}</h2>

5. springmvc的配置文件

<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">


    <!--聲明組件掃描器-->
    <context:component-scan base-package="com.md.controller"/>


    <!--聲明springmvc框架中的視圖解析器,幫助開發人員設置視圖文件路徑-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前綴:視圖文件的路徑-->
        <property name="prefix" value="/WEB-INF/view/" />

        <!--後綴:視圖文件的擴展名-->
        <property name="suffix" value=".jsp"/>

    </bean>



    
    
    <!-- 處理異常的兩個步驟
	1. 註冊組件掃描器,也便是@ControllerAdvice註解所在的包名
	2. 註冊註解驅動,

-->
    <context:component-scan base-package="com.md.handler"/>
    <mvc:annotation-driven/>



</beans>

這樣就能夠了

3、攔截器

SpringMVC 中的 Interceptor 攔截器是很是重要和至關有用的,它的主要做用是攔截指定的用戶請求,並進行相應的預處理與後處理。

  • 攔截器是springmvc中的一種,須要實現HandlerInterceptor接口。
  • 攔截器和過濾器相似,功能方向側重點不一樣。 過濾器是用來過濾器請求參數,設置編碼字符集等工做。
  • 攔截器是攔截用戶的請求,作請求作判斷處理的。
  • 攔截器是全局的,能夠對多個Controller作攔截, 一個項目中能夠有0個或多個攔截器, 他們在一塊兒攔截用戶的請求。
  • 攔截器經常使用在:用戶登陸處理,權限檢查, 記錄日誌。

1. 攔截器使用步驟

  1. 新建maven web項目
  2. 加入依賴
  3. 建立Controller類
  4. 建立一個普通類,做爲攔截器使用
    • 實現HandlerInterceptor接口
    • 實現接口中的三個方法
  5. 建立show.jsp
  6. 建立springmvc的配置文件
    • 組件掃描器 ,掃描@Controller註解
    • 聲明攔截器,並指定攔截的請求uri地址

2. 一個攔截器的執行

Controller類

@RequestMapping(value = "/some.do")
    public ModelAndView doSome(String name , Integer age) {
        System.out.println("-------MyController的doSome()");

        ModelAndView mv = new ModelAndView();

        mv.addObject("myname",name);
        mv.addObject("myage",age);

        mv.setViewName("show");

        return mv;
    }

仍是在以前的項目中,定義個普通類,做爲攔截器

package com.md.handler;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

/**
 * 攔截器類,攔截用戶的請求
 * @author MD
 * @create 2020-08-14 21:18
 */
public class MyInterceptor implements HandlerInterceptor {
// 快捷鍵:ctrl + O
    private long btime = 0;


    /*
     * preHandle叫作預處理方法。
     *   重要:是整個項目的入口,門戶。 當preHandle返回true 請求能夠被處理。
     *        preHandle返回false,請求到此方法就截止。
     *
     * 參數:
     *  Object handler : 被攔截的控制器對象
     * 返回值boolean
     *   true:請求是經過了攔截器的驗證,能夠執行處理器方法
     *   執行順序
            攔截器: preHandle()
            -------MyController的doSome()
            攔截器: postHandle()
            攔截器: afterCompletion()
         *
     *   false:請求沒有經過攔截器的驗證,請求到達攔截器就截止了。 請求沒有被處理
     *   僅僅輸出這一句話,
     *      攔截器: preHandle()
     *
     *
     *  特色:
     *   1.方法在控制器方法(MyController的doSome)以前先執行的。
     *     用戶的請求首先到達此方法
     *
     *   2.在這個 方法中能夠獲取請求的信息, 驗證請求是否符合要求。
     *     能夠驗證用戶是否登陸, 驗證用戶是否有權限訪問某個鏈接地址(url)。
     *      若是驗證失敗,能夠截斷請求,請求不能被處理。
     *      若是驗證成功,能夠放行請求,此時控制器方法才能執行。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        btime = System.currentTimeMillis();
        System.out.println("攔截器: preHandle()");


        // 計算的業務邏輯,根據計算的結果,返回true或者false

        //  給瀏覽器一個反饋
//        request.getRequestDispatcher("/tips.jsp").forward(request,response);
//
//        return false;

        return true;
    }


    /*
         postHandle:後處理方法。
         參數:
          Object handler:被攔截的處理器對象MyController
          ModelAndView modelAndView:處理器方法的返回值

          特色:
           1.在處理器方法以後執行的(MyController.doSome())
           2.可以獲取處處理器方法的返回值ModelAndView,能夠修改ModelAndView中的
           數據和視圖,能夠影響到最後的執行結果。
           3.主要是對原來的執行結果作二次修正,

           ModelAndView mv = MyController.doSome();
           postHandle(request,response,handler,mv);
       */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("攔截器: postHandle()");

        // 對原來的doSome執行的結果進行調整
        if (modelAndView != null){
            modelAndView.addObject("mydate" , new Date());
            modelAndView.setViewName("other");
        }
    }


    /*
          afterCompletion:最後執行的方法
          參數
            Object handler:被攔截器的處理器對象
            Exception ex:程序中發生的異常
          特色:
           1.在請求處理完成後執行的。框架中規定是當你的視圖處理完成後,對視圖執行了forward。就認爲請求處理完成。
           2.通常作資源回收工做的, 程序請求過程當中建立了一些對象,在這裏能夠刪除,把佔用的內存回收。
         */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("攔截器: afterCompletion()");
        long etime = System.currentTimeMillis();
        System.out.println("計算從preHandle到請求處理完成的時間:"+(etime - btime ));
    }
}

建立springmvc的配置文件

<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">


    <!--聲明組件掃描器-->
    <context:component-scan base-package="com.md.controller"/>




    <!--聲明springmvc框架中的視圖解析器,幫助開發人員設置視圖文件路徑-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前綴:視圖文件的路徑-->
        <property name="prefix" value="/WEB-INF/view/" />

        <!--後綴:視圖文件的擴展名-->
        <property name="suffix" value=".jsp"/>

    </bean>




    <!-- 聲明攔截器: 攔截器能夠一個或者多個-->
    <mvc:interceptors>
        <!--聲明第一個攔截器-->
        <mvc:interceptor>
            <!-- 指定攔截的請求url地址, page就是url地址,可使用通配符 ** , 表示任意的字符,文件或者多級目錄-->
            <!-- http://localhost:8080/user/xxx-->

            <!--<mvc:mapping path="/user/**"/>-->


            <mvc:mapping path="/**"/>
            <!--聲明攔截器對象-->
            <bean class="com.md.handler.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

</beans>

攔截器中方法與處理器方法的執行順序

3. 多個攔截器的執行

再定義一個攔截器

package com.md.handler;

public class MyInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("111111111111攔截器: preHandle()");


        return true;
    }



    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("11111111111111111攔截器: postHandle()");


    }



    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("111111111111111攔截器: afterCompletion()");

    }
}
//---------------------------------------

package com.md.handler;
public class MyInterceptor2 implements HandlerInterceptor {
// 快捷鍵:ctrl + O

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("2222222222222222攔截器: preHandle()");


        return false;
    }



    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("2222222222222222攔截器: postHandle()");


    }



    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("122222222222222222攔截器: afterCompletion()");

    }
}

//---------------------------------------

多個攔截器的註冊,在springmvc的配置文件

<!-- 聲明攔截器: 攔截器能夠一個或者多個

        在框架中保存是在一個ArrayList  , 先聲明的在前面,
    -->
    <mvc:interceptors>
        <!--聲明第一個攔截器-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <!--聲明攔截器對象-->
            <bean class="com.md.handler.MyInterceptor"/>
        </mvc:interceptor>



        <!--聲明第二個攔截器-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <!--聲明攔截器對象-->
            <bean class="com.md.handler.MyInterceptor2"/>
        </mvc:interceptor>

    </mvc:interceptors>


    <!-- 返回的都是true,執行的順序-->

    <!--111111111111攔截器: preHandle()-->
    <!--2222222222222222攔截器: preHandle()-->
    <!-- -&#45;&#45;&#45;&#45;&#45;&#45;MyController的doSome()-->
    <!--2222222222222222攔截器: postHandle()-->
    <!--11111111111111111攔截器: postHandle()-->
    <!--122222222222222222攔截器: afterCompletion()-->
    <!--111111111111111攔截器: afterCompletion()-->


    <!-- 第一個true,第一個false,執行順序-->
    <!--111111111111攔截器: preHandle()-->
    <!--2222222222222222攔截器: preHandle()-->
    <!--111111111111111攔截器: afterCompletion()-->


    <!-- 返回的都是false,執行的順序-->
    <!--111111111111攔截器: preHandle()-->

</beans>

當有多個攔截器時,造成攔截器鏈。攔截器鏈的執行順序,與其註冊順序一致。須要再次強調一點的是,當某一個攔截器的 preHandle()方法返回 true 並被執行到時,會向一個專門的方法棧中放入該攔截器的 afterCompletion()方法

4. 攔截器和過濾器的區別

  1. 過濾器是servlet中的對象, 攔截器是框架中的對象
  2. 過濾器實現Filter接口的對象, 攔截器是實現HandlerInterceptor
  3. 過濾器是用來設置request,response的參數,屬性的,側重對數據過濾的,
  4. 攔截器是用來驗證請求的,能截斷請求。
  5. 過濾器是在攔截器以前先執行的。
  6. 過濾器是tomcat服務器建立的對象、攔截器是springmvc容器中建立的對象
相關文章
相關標籤/搜索