SpringMVC--從理解SpringMVC執行流程到SSM框架整合

2022-09-11 21:02:08

前言

SpringMVC框架是SSM框架中繼Spring另一個重要的框架,那麼什麼是SpringMVC,如何用SpringMVC來整合SSM框架呢?下面讓我們詳細的瞭解一下.
注:在學習SpringMVC之前最好有JavaWeb的基礎,這樣才能更好的理解SpringMVC,如果不瞭解JavaWeb可以參考這篇文章JavaWeb基礎入門到上手專案,本篇文章參照於B站狂神說老師的SpringMVC視訊編寫,視訊原地址為【狂神說Java】SpringMVC最新教學IDEA版通俗易懂 ,大家記得一鍵三連啊!!!

1.1、什麼是MVC

  • MVC是模型(Model)、檢視(View)、控制器(Controller)的簡寫,是一種軟體設計規範。是將業務邏輯、資料、顯示分離的方法來組織程式碼。
  • MVC主要作用是降低了檢視與業務邏輯間的雙向偶合。
  • MVC不是一種設計模式,MVC是一種架構模式。當然不同的MVC存在差異。

Model(模型):資料模型,提供要展示的資料,因此包含資料和行為,可以認為是領域模型或JavaBean元件(包含資料和行為),不過現在一般都分離開來:Value Object(資料Dao) 和 服務層(行為Service)。也就是模型提供了模型資料查詢和模型資料的狀態更新等功能,包括資料和業務。

View(檢視):負責進行模型的展示,一般就是我們見到的使用者介面,客戶想看到的東西。

Controller(控制器):接收使用者請求,委託給模型進行處理(狀態改變),處理完畢後把返回的模型資料返回給檢視,由檢視負責展示。也就是說控制器做了個排程員的工作。

最典型的MVC就是JSP + servlet + javabean的模式。

1.2、Model1時代

在web早期的開發中,通常採用的都是Model1。
Model1中,主要分為兩層,檢視層和模型層。

Model1優點:架構簡單,比較適合小型專案開發;

Model1缺點:JSP職責不單一,職責過重,不便於維護;

1.3、Model2時代

Model2把一個專案分成三部分,包括檢視、控制、模型。

  1. 使用者發請求

  2. Servlet接收請求資料,並呼叫對應的業務邏輯方法

  3. 業務處理完畢,返回更新後的資料給servlet

  4. servlet轉向到JSP,由JSP來渲染頁面

  5. 響應給前端更新後的頁面

職責分析:

Controller:控制器

  • 取得表單資料
  • 呼叫業務邏輯
  • 轉向指定的頁面

Model:模型

  • 業務邏輯
  • 儲存資料的狀態

View:檢視

  • 顯示頁面

Model2這樣不僅提高的程式碼的複用率與專案的擴充套件性,且大大降低了專案的維護成本。Model 1模式的實現比較簡單,適用於快速開發小規模專案,Model1中JSP頁面身兼View和Controller兩種角色,將控制邏輯和表現邏輯混雜在一起,從而導致程式碼的重用性非常低,增加了應用的擴充套件性和維護的難度。Model2消除了Model1的缺點。

1.4、回顧Servlet

1.新建一個Maven工程當做父工程!pom依賴!

<!--匯入相應的依賴-->
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
</dependencies>

2.建立一個Moudle:springmvc-01-servlet , 新增Web app的支援!

3.匯入servlet 和 jsp 的 jar 依賴

<dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>servlet-api</artifactId>
   <version>2.5</version>
</dependency>
<dependency>
   <groupId>javax.servlet.jsp</groupId>
   <artifactId>jsp-api</artifactId>
   <version>2.2</version>
</dependency>

4.編寫一個Servlet類,用來處理使用者的請求

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.獲取前端引數
        String method = req.getParameter("method");
        if (method.equals("add")){
            req.getSession().setAttribute("msg","執行了add方法");
        }
        if (method.equals("delete")){
            req.getSession().setAttribute("msg","執行了delete方法");
        }

        //2.呼叫業務層
        //3.檢視轉發或者重定向
        req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,resp);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

5.編寫Hello.jsp,在WEB-INF目錄下新建一個jsp的資料夾,新建hello.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

${msg}

</body>
</html>

6.在web.xml中註冊Servlet

<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.qjd.servlet.HelloServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

7.設定Tomcat,並啟動測試

localhost:8080/user?method=add

localhost:8080/user?method=delete

MVC框架要做哪些事情

  • 將url對映到java類或java類的方法 .

  • 封裝使用者提交的資料 .

  • 處理請求–呼叫相關的業務處理–封裝響應資料 .

  • 將響應的資料進行渲染 . jsp / html 等表示層資料 .

說明:

常見的伺服器端MVC框架有:Struts、Spring MVC、ASP.NET MVC、Zend Framework、JSF;常見前端MVC框架:vue、angularjs、react、backbone;由MVC演化出了另外一些模式如:MVP、MVVM 等等…

2、什麼是SpringMVC

2.1、概述

Spring MVC是Spring Framework的一部分,是基於Java實現MVC的輕量級Web框架。

檢視官方檔案:https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.html

我們為什麼要學習SpringMVC呢?

Spring:大雜燴,我們可以將SpringMVC中所有要用到的bean註冊到Spring中

Spring MVC的特點:

  1. 輕量級,簡單易學

  2. 高效 , 基於請求響應的MVC框架

  3. 與Spring相容性好,無縫結合

  4. 約定優於設定

  5. 功能強大:RESTful、資料驗證、格式化、在地化、主題等

  6. 簡潔靈活

Spring的web框架圍繞DispatcherServlet [ 排程Servlet ] 設計。

DispatcherServlet的作用是將請求分發到不同的處理器。從Spring 2.5開始,使用Java 5或者以上版本的使用者可以採用基於註解形式進行開發,十分簡潔;

正因為SpringMVC好 , 簡單 , 便捷 , 易學 , 天生和Spring無縫整合(使用SpringIoC和Aop) , 使用約定優於設定 . 能夠進行簡單的junit測試 . 支援Restful風格 .例外處理 , 在地化 , 國際化 , 資料驗證 , 型別轉換 , 攔截器 等等…所以我們要學習 .

最重要的一點還是用的人多 , 使用的公司多 .

2.2、中心控制器

Spring的web框架圍繞DispatcherServlet設計。

DispatcherServlet的作用是將請求分發到不同的處理器。

從Spring 2.5開始,使用Java 5或者以上版本的使用者可以採用基於註解的controller宣告方式。

Spring MVC框架像許多其他MVC框架一樣, 以請求為驅動 , 圍繞一箇中心Servlet分派請求及提供其他功能DispatcherServlet是一個實際的Servlet (它繼承自HttpServlet 基礎類別)

(DispatcherServlet本質上還是Servlet)

SpringMVC的原理如下圖所示:

當發起請求時被前置的控制器攔截到請求,根據請求引數生成代理請求,找到請求對應的實際控制器,控制器處理請求,建立資料模型,存取資料庫,將模型響應給中心控制器,控制器使用模型與檢視渲染檢視結果,將結果返回給中心控制器,再將結果返回給請求者

原圖:

中文圖:

2.3、SpringMVC執行原理

圖為SpringMVC的一個較完整的流程圖,實線表示SpringMVC框架提供的技術,不需要開發者實現,虛線表示需要開發者實現。

DispatcherServlet表示前置控制器,是整個SpringMVC的控制中心

簡要分析執行流程

234:適配收到的請求到底是幹什麼的(根據hello找到處理器Handler)

5678:這個請求具體做什麼(找HelloController執行)

91011:檢視解析

1、DispatcherServlet表示前置控制器,是整個SpringMVC的控制中心。使用者發出請求,DispatcherServlet接收請求並攔截請求。

  1. ​ 我們假設請求的url為 : http://localhost:8080/SpringMVC/hello

  2. ​ 如上url拆分成三部分:

  3. http://localhost:8080 ------> 伺服器域名

  4. ​ SpringMVC ------> 部署在伺服器上的web站點

  5. ​ hello ------> 表示控制器

  6. ​ 通過分析,如上url表示為:請求位於伺服器localhost:8080上的SpringMVC站點的hello控制器。

2、HandlerMapping為處理器對映。DispatcherServlet呼叫HandlerMapping,HandlerMapping根據請求url查詢Handler。

3、HandlerExecution表示具體的Handler,其主要作用是根據url查詢控制器,如上url被查詢控制器為:hello。

4、HandlerExecution將解析後的資訊傳遞給DispatcherServlet,如解析控制器對映等。

5、HandlerAdapter表示處理器介面卡,其按照特定的規則去執行Handler。

6、Handler讓具體的Controller執行。

7、Controller將具體的執行資訊返回給HandlerAdapter,如ModelAndView。

8、HandlerAdapter將檢視邏輯名或模型傳遞給DispatcherServlet。

9、DispatcherServlet呼叫檢視解析器(ViewResolver)來解析HandlerAdapter傳遞的邏輯檢視名。

10、檢視解析器將解析的邏輯檢視名傳給DispatcherServlet。

11、DispatcherServlet根據檢視解析器解析的檢視結果,呼叫具體的檢視。

12、最終檢視呈現給使用者。

3、第一個MVC程式

3.1、設定版

  1. 新建一個Moudle , springmvc-02-hello , 新增web的支援!
  2. 確定匯入了SpringMVC 的依賴!
  3. 設定web.xml , 註冊DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--1.註冊DispatcherServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--關聯一個springmvc的組態檔:【servlet-name】-servlet.xml-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--啟動級別-1-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--/ 匹配所有的請求;(不包括.jsp)-->
    <!--/* 匹配所有的請求;(包括.jsp)-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
</web-app>

​ 4.編寫SpringMVC 的 組態檔!名稱:springmvc-servlet.xml : [servletname]-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 新增 處理對映器(可省略)-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!--新增 處理器介面卡(可省略)-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    <!--檢視解析器:DispatcherServlet給他的ModelAndView
      1.獲取了ModelAndView資料
      2.解析ModelAndView的檢視名字
      3.拼接檢視名字,找到對應的檢視
      4.將資料渲染到這個檢視上
     -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
        <!--字首-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--字尾-->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--Handler-->
    <bean id="/hello" class="com.qjd.controller.HelloController"/>

</beans>

5.編寫我們要操作業務Controller ,要麼實現Controller介面,要麼增加註解;需要返回一個ModelAndView,裝資料,封檢視;

//注意:這裡我們先匯入Controller介面
public class HelloController implements Controller {

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //ModelAndView 模型和檢視
        ModelAndView mv = new ModelAndView();

        //封裝物件,放在ModelAndView中。Model
        mv.addObject("msg","HelloSpringMVC!");
        //封裝要跳轉的檢視,放在ModelAndView中
        mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
        return mv;
    }
}

6.將自己的類交給SpringIOC容器,註冊bean

<!--Handler-->
<bean id="/hello" class="com.qjd.controller.HelloController"/>

7.寫要跳轉的jsp頁面,顯示ModelandView存放的資料,以及我們的正常頁面;

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

${msg}

</body>
</html>

8.設定Tomcat 啟動測試!

9.測試結果

可能遇到的問題:存取出現404,排查步驟:

  1. 檢視控制檯輸出,看一下是不是缺少了什麼jar包。

  2. 如果jar包存在,顯示無法輸出,就在IDEA的專案發布中,新增lib依賴!

  3. 重啟Tomcat 即可解決!

3.2、註解版

  1. 新建一個Moudle,springmvc-03-hello-annotation 。新增web支援!

  2. 由於Maven可能存在資源過濾的問題,我們將設定完善

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
    
  3. 在pom.xml檔案引入相關的依賴:主要有Spring框架核心庫、Spring MVC、servlet , JSTL等。我們在父依賴中已經引入了!

  4. 設定web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!--1.註冊servlet-->
        <servlet>
            <servlet-name>SpringMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--通過初始化引數指定SpringMVC組態檔的位置,進行關聯-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!-- 啟動順序,數位越小,啟動越早 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <!--所有請求都會被springmvc攔截 -->
        <servlet-mapping>
            <servlet-name>SpringMVC</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    
    </web-app>
    
  5. / 和 /* 的區別:

    • < url-pattern > / </ url-pattern > 不會匹配到.jsp, 只針對我們編寫的請求;即:.jsp 不會進入spring的 DispatcherServlet類 。

    • < url-pattern > /* </ url-pattern > 會匹配 *.jsp,會出現返回 jsp檢視 時再次進入spring的DispatcherServlet 類,導致找不到對應的controller所以報404錯。

  6. 新增Spring MVC組態檔

    在resource目錄下新增springmvc-servlet.xml組態檔,設定的形式與Spring容器設定基本類似,為了支援基於註解的IOC,設定了自動掃描包的功能,具體設定資訊如下:

    <?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">
    
        <!-- 自動掃描包,讓指定包下的註解生效,由IOC容器統一管理 -->
        <context:component-scan base-package="com.qjd.controller"/>
    
        <!-- 讓Spring MVC不處理靜態資源     .css  .js .html ... -->
        <mvc:default-servlet-handler />
        <!--
        支援mvc註解驅動
            在spring中一般採用@RequestMapping註解來完成對映關係
            要想使@RequestMapping註解生效
            必須向上下文中註冊DefaultAnnotationHandlerMapping
            和一個AnnotationMethodHandlerAdapter範例
            這兩個範例分別在類級別和方法級別處理。
            而annotation-driven設定幫助我們自動完成上述兩個範例的注入。
         -->
        <mvc:annotation-driven />
    
    
        <!-- 檢視解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!-- 字首 -->
            <property name="prefix" value="/WEB-INF/jsp/" />
            <!-- 字尾 -->
            <property name="suffix" value=".jsp" />
        </bean>
    
    </beans>
    

    在檢視解析器中我們把所有的檢視都存放在/WEB-INF/目錄下,這樣可以保證檢視安全,因為這個目錄下的檔案,使用者端不能直接存取。

    • 讓IOC的註解生效
    • 靜態資源過濾 :HTML . JS . CSS . 圖片 , 視訊 …
      • MVC的註解驅動
      • 設定檢視解析器
  7. 建立Controller

    package com.qjd.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("/HelloController")
    public class HelloController {
        @RequestMapping("/hello")
        public String hello(Model model) {
            //真實存取地址 : 專案名/HelloController/hello
    
            //封裝資料,向模型中新增屬性msg與值,可以在JSP頁面中取出並渲染
            model.addAttribute("msg","hello,springmvc annotation!");
    
            //會被檢視解析器處理,web-inf/jsp/hello.jsp
            return "hello";
        }
    }
    
    • @Controller是為了讓Spring IOC容器初始化時自動掃描到;

    • @RequestMapping是為了對映請求路徑,這裡因為類與方法上都有對映所以存取時應該是/HelloController/hello;

    • 方法中宣告Model型別的引數是為了把Action中的資料帶到檢視中;

    • 方法返回的結果是檢視的名稱hello,加上組態檔中的前字尾變成WEB-INF/jsp/hello.jsp

  8. 建立檢視層

    在WEB-INF/ jsp目錄中建立hello.jsp , 檢視可以直接取出並展示從Controller帶回的資訊;

    可以通過EL表示取出Model中存放的值,或者物件;

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    
    ${msg}
    
    </body>
    </html>
    
  9. 設定Tomcat執行

小結:

實現步驟其實非常的簡單:

  • 新建一個web專案

  • 匯入相關jar包

  • 編寫web.xml , 註冊DispatcherServlet

  • 編寫springmvc組態檔

  • 接下來就是去建立對應的控制類 , controller

  • 最後完善前端檢視和controller之間的對應

  • 測試執行偵錯.

使用springMVC必須設定的三大件:

處理器對映器、處理器介面卡、檢視解析器

通常,我們只需要手動設定檢視解析器,而處理器對映器和處理器介面卡只需要開啟註解驅動即可,而省去了大段的xml設定

4、Controller和RestFul

4.1、控制器Controller

  • 控制器複雜提供存取應用程式的行為,通常通過介面定義或註解定義兩種方法實現。

  • 控制器負責解析使用者的請求並將其轉換為一個模型。

  • 在Spring MVC中一個控制器類可以包含多個方法

  • 在Spring MVC中,對於Controller的設定方式有很多種

4.2、實現Controller介面

Controller是一個介面,在org.springframework.web.servlet.mvc包下,介面中只有一個方法;

//實現該介面的類獲得控制器功能
public interface Controller {
   //處理請求且返回一個模型與檢視物件
   ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}

測試

  1. 新建一個Moudle,springmvc-04-controller!

  2. springmvc-02-hellomvc中mvc的組態檔只留下 檢視解析器!

  3. 編寫一個Controller類,ControllerTest1

    //只要是實現了Controller介面的類,就說明這是一個控制器了
    public class ControllerTest1 implements Controller {
        @Override
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            ModelAndView mv = new ModelAndView();
    
    
            mv.addObject("msg","ControllerTest01");
            mv.setViewName("test");//跳轉到test.jsp
            return mv;
        }
    }
    
  4. 編寫完畢後,去Spring組態檔中註冊請求的bean;name對應請求路徑,class對應處理請求的類

    <?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">
    
    
        <!-- 自動掃描包,讓指定包下的註解生效,由IOC容器統一管理 -->
        <context:component-scan base-package="com.qjd.controller"/>
        <!-- 讓Spring MVC不處理靜態資源     .css  .js .html ... -->
        <mvc:default-servlet-handler />
    
        <!--
       支援mvc註解驅動
           在spring中一般採用@RequestMapping註解來完成對映關係
           要想使@RequestMapping註解生效
           必須向上下文中註冊DefaultAnnotationHandlerMapping
           和一個AnnotationMethodHandlerAdapter範例
           這兩個範例分別在類級別和方法級別處理。
           而annotation-driven設定幫助我們自動完成上述兩個範例的注入。
        -->
        <mvc:annotation-driven />
    
    
    <!-- 以上可以省略,是預設設定   -->
    
    
        <!--檢視解析器:DispatcherServlet給他的ModelAndView
          1.獲取了ModelAndView資料
          2.解析ModelAndView的檢視名字
          3.拼接檢視名字,找到對應的檢視
          4.將資料渲染到這個檢視上
          -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
            <!--字首-->
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <!--字尾-->
            <property name="suffix" value=".jsp"/>
        </bean>
    
        
        <bean name="/t1" class="com.qjd.controller.ControllerTest1"/>
    
    
    </beans>
    
  5. 寫前端test.jsp,注意在WEB-INF/jsp目錄下編寫,對應我們的檢視解析器

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
      $END$
      </body>
    </html>
    
  6. 設定Tomcat執行測試

說明:

  • 實現介面Controller定義控制器是較老的辦法
  • 缺點是:一個控制器中只有一個方法,如果要多個方法則需要定義多個Controller;定義的方式比較麻煩;

4.3、使用註解@Controller

不需要在springmvc-servlet.xml中註冊bean

  • @Controller註解型別用於宣告Spring類的範例是一個控制器(在講IOC時還提到了另外3個註解);

  • Spring可以使用掃描機制來找到應用程式中所有基於註解的控制器類,為了保證Spring能找到你的控制器,需要在組態檔中宣告元件掃描。

    <!-- 自動掃描指定的包,下面所有註解類交給IOC容器管理 -->
      <context:component-scan base-package="com.qjd.controller"/>
    
  • 增加一個ControllerTest2類,使用註解實現;

    @Controller
    //代表這個類會被Spring接管,自動註冊,被這個註解的類中的所有的方法,如果返回值是String,並且有具體頁面可以跳轉,那麼就會被檢視解析器解析
    public class ControllerTest2 {
        //對映存取路徑
        @RequestMapping("/t2")
        public String index(Model model){
            //Spring MVC會自動範例化一個Model物件用於向檢視中傳值
            model.addAttribute("msg", "ControllerTest2");
            //返回檢視位置
            return "test";
        }
    
        @RequestMapping("/t3")
        public String index2(Model model){
            //Spring MVC會自動範例化一個Model物件用於向檢視中傳值
            model.addAttribute("msg", "ControllerTest3");
            //返回檢視位置
            return "test";
        }
    }
    
    
  • 執行tomcat測試

可以發現,我們的兩個請求都可以指向一個檢視(test),但是頁面結果的結果是不一樣的,從這裡可以看出檢視是被複用的,而控制器與檢視之間是弱偶合關係。

註解方式是平時使用的最多的方式!

4.4、RequestMapping

@RequestMapping

@RequestMapping註解用於對映url到控制器類或一個特定的處理程式方法。可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。

@Controller
@RequestMapping("/c3")
public class ControllerTest3 {


    @RequestMapping("/t1")
    public String test1(Model model){
        model.addAttribute("msg","Hello");
        return "test";
    }
}

//http://localhost:8080/c3/t1
//等價為類上的@RequestMapping不寫,方法上的@RequestMapping為 @RequestMapping("/c3/t1")

4.5、RestFul 風格

概念:

Restful就是一個資源定位及資源操作的風格。不是標準也不是協定,只是一種風格。基於這個風格設計的軟體可以更簡潔,更有層次,更易於實現快取等機制。

特點:簡潔、高效、安全

功能

資源:網際網路所有的事物都可以被抽象為資源

資源操作:使用POST、DELETE、PUT、GET,使用不同方法對資源進行操作。

分別對應 新增、 刪除、修改、查詢。

傳統方式操作資源 :通過不同的引數來實現不同的效果!方法單一,post 和 get

使用RESTful操作資源 :可以通過不同的請求方式來實現不同的效果!如下:請求地址一樣,但是功能可以不同!

學習測試

  1. 在新建一個類 RestFulController

  2. 在Spring MVC中可以使用 @PathVariable 註解,讓方法引數的值對應繫結到一個URI模板變數上。

    @Controller
    public class RestFulController {
    
        //原來:http://localhost:8080/add?a=1&b=2
        //RestFul:http://localhost:8080/add/a/b
    
        //@RequestMapping(name = "/add/{a}/{b}",method = RequestMethod.GET)
        @GetMapping("/add/{a}/{b}")
        public String test1(@PathVariable int a, @PathVariable int b, Model model){
            int res = a + b;
            model.addAttribute("msg","結果1為"+res);
            return "test";
    
        }
    
        @PostMapping("/add/{a}/{b}")
        public String test2(@PathVariable int a, @PathVariable int b, Model model){
            int res = a + b;
            model.addAttribute("msg","結果2為"+res);
            return "test";
    
        }
    
    }
    

  1. 思考:使用路徑變數的好處?

    • 使路徑變得更加簡潔;
    • 獲得引數更加方便,框架會自動進行型別轉換。
    • 通過路徑變數的型別可以約束存取引數,如果型別不一樣,則存取不到對應的請求方法,如這裡存取是的路徑是/add/1/a,則路徑與方法不匹配,而不會是引數轉換失敗。
    • 安全,不會暴露引數
  2. 使用method屬性指定請求型別

    • 用於約束請求的型別,可以收窄請求範圍。指定請求謂詞的型別如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等
    • 我們使用瀏覽器位址列進行存取預設是Get請求,會報錯405:

小結:

Spring MVC 的 @RequestMapping 註解能夠處理 HTTP 請求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。

所有的位址列請求預設都會是 HTTP GET 型別的。

方法級別的註解變體有如下幾個:組合註解

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

@GetMapping 是一個組合註解,平時使用的會比較多!

它所扮演的是 @RequestMapping(method =RequestMethod.GET) 的一個快捷方式。

5、結果跳轉方式

5.1、ModelAndView

設定ModelAndView物件 , 根據view的名稱 , 和檢視解析器跳到指定的頁面 .

頁面 : {檢視解析器字首} + viewName +{檢視解析器字尾}

    <!--檢視解析器:DispatcherServlet給他的ModelAndView
      1.獲取了ModelAndView資料
      2.解析ModelAndView的檢視名字
      3.拼接檢視名字,找到對應的檢視
      4.將資料渲染到這個檢視上
      -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
        <!--字首-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--字尾-->
        <property name="suffix" value=".jsp"/>
    </bean>

對應的controller類

public class ControllerTest1 implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView();
        
        mv.addObject("msg","ControllerTest01");
        mv.setViewName("test");//跳轉到test.jsp
        return mv;
    }
}

5.2、ServletAPI(瞭解)

ServletAPI

通過設定ServletAPI , 不需要檢視解析器(不建議使用,瞭解即可) .

  1. 通過HttpServletResponse進行輸出
  2. 通過HttpServletResponse實現重定向
  3. 通過HttpServletResponse實現轉發
@Controller
public class ResultGo {

   @RequestMapping("/result/t1")
   public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
       rsp.getWriter().println("Hello,Spring BY servlet API");
  }

   @RequestMapping("/result/t2")
   public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
       rsp.sendRedirect("/index.jsp");
  }

   @RequestMapping("/result/t3")
   public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
       //轉發
       req.setAttribute("msg","/result/t3");
       req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
  }

}

5.3、SpringMVC(瞭解)

(1)通過SpringMVC來實現轉發和重定向 - 無需檢視解析器;

測試前,需要將檢視解析器註釋掉

  • 預設為forward轉發(也可以加上)

  • redirect轉發需特別加

    @Controller
    public class ResultSpringMVC {
       @RequestMapping("/rsm/t1")
       public String test1(){
           //轉發
           return "/index.jsp";
      }
    
       @RequestMapping("/rsm/t2")
       public String test2(){
           //轉發二
           return "forward:/index.jsp";
      }
    
       @RequestMapping("/rsm/t3")
       public String test3(){
           //重定向
           return "redirect:/index.jsp";
      }
    }
    
    

(2)通過SpringMVC來實現轉發和重定向 - 有檢視解析器;

重定向 , 不需要檢視解析器 , 本質就是重新請求一個新地方嘛 , 所以注意路徑問題.

可以重定向到另外一個請求實現 .

  • 預設為forward轉發(不可以加上)
  • redirect轉發需特別加
@Controller
public class ResultSpringMVC2 {
   @RequestMapping("/rsm2/t1")
   public String test1(){
       //轉發
       return "test";
  }

   @RequestMapping("/rsm2/t2")
   public String test2(){
       //重定向
       return "redirect:/index.jsp";
       //return "redirect:hello.do"; //hello.do為另一個請求/
  }

}

6、資料處理

6.1、處理提交資料

1、提交的域名稱和處理方法的引數名一致

@Controller
@RequestMapping("/user")
public class UserController {
    @GetMapping("/t1")
    public String test1(String name, Model model){

        //1.接收前端引數
        System.out.println("接收到前端的引數為"+name);

        //2.將返回的結果傳遞給前端,Model
        model.addAttribute("msg",name);

        //3.檢視跳轉
        return "test";

    }

    
}

2、提交的域名稱和處理方法的引數名不一致

@Controller
@RequestMapping("/user")
public class UserController {
    @GetMapping("/t1")
    public String test1(@RequestParam("username") String name, Model model){

        //1.接收前端引數
        System.out.println("接收到前端的引數為"+name);

        //2.將返回的結果傳遞給前端,Model
        model.addAttribute("msg",name);

        //3.檢視跳轉
        return "test";

    }

}

3、提交的是一個物件

要求提交的表單域和物件的屬性名一致 , 引數使用物件即可

實體類:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private int id;
    private String name;
    private int age;
}

UserController:

//前端接收的是一個物件  :  id,name,age
@GetMapping("/t2")
public String test2(User user,Model model){

    System.out.println(user);
    model.addAttribute("msg",user);
    
    return "test";
}

說明:如果使用物件的話,前端傳遞的引數名和物件名必須一致,否則就是null。

6.2、資料顯示到前端

第一種 : 通過ModelAndView

我們之前用的就是這種方式,這裡就不再過多贅述

public class ControllerTest1 implements Controller {

   public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
       //返回一個模型檢視物件
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","ControllerTest1");
       mv.setViewName("test");
       return mv;
  }
}

第二種 : 通過ModelMap

ModelMap

@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap modelMap){
   //封裝要顯示到檢視中的資料
   //相當於req.setAttribute("name",name);
   modelMap.addAttribute("name",name);
   System.out.println(name);
   return "hello";
}


第三種 : 通過Model

Model

@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
   //封裝要顯示到檢視中的資料
   //相當於req.setAttribute("name",name);
   model.addAttribute("msg",name);
   System.out.println(name);
   return "test";
}

6.3、對比

就對於新手而言簡單來說使用區別就是:

Model 只有寥寥幾個方法只適合用於儲存資料,簡化了新手對於Model物件的操作和理解;
ModelMap 繼承了 LinkedMap ,除了實現了自身的一些方法,同樣的繼承 LinkedMap 的方法和特性;
ModelAndView 可以在儲存資料的同時,可以進行設定返回的邏輯檢視,進行控制展示層的跳轉。

我們更多的時候都是使用Model

7、亂碼問題

測試步驟:

  1. 我們可以在首頁編寫一個提交的表單

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    
    <form action="/e/t1" method="post">
        <input type="text" name="name">
        <input type="submit">
    </form>
    
    
    </body>
    </html>
    
  2. 後臺編寫對應的處理類

    @Controller
    public class EncodingController {
        @PostMapping("/e/t1")
        public String test1(String name, Model model){
            model.addAttribute("msg",name);
    
            return "test";
        }
    
    }
    
  3. 輸入中文測試會發現亂碼

  1. 以前亂碼問題通過過濾器解決 , 而SpringMVC給我們提供了一個過濾器 , 可以在web.xml中設定 .

    修改了xml檔案需要重啟伺服器!

    <filter>
       <filter-name>encoding</filter-name>
       <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
       <init-param>
           <param-name>encoding</param-name>
           <param-value>utf-8</param-value>
       </init-param>
    </filter>
    <filter-mapping>
       <filter-name>encoding</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    注意:這裡寫/*,寫/的話過濾不了jsp頁面,不能解決亂碼

    但是我們發現 , 有些極端情況下.這個過濾器對get的支援不好 .

    處理方法 :

    1. 修改tomcat組態檔 :設定編碼!

      <Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
                connectionTimeout="20000"
                redirectPort="8443" />
      
    2. 自定義過濾器(萬能解決)

      package com.kuang.filter;
      
      import javax.servlet.*;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletRequestWrapper;
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
      import java.io.UnsupportedEncodingException;
      import java.util.Map;
      
      /**
      * 解決get和post請求 全部亂碼的過濾器
      */
      public class GenericEncodingFilter implements Filter {
      
         @Override
         public void destroy() {
        }
      
         @Override
         public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
             //處理response的字元編碼
             HttpServletResponse myResponse=(HttpServletResponse) response;
             myResponse.setContentType("text/html;charset=UTF-8");
      
             // 轉型為與協定相關物件
             HttpServletRequest httpServletRequest = (HttpServletRequest) request;
             // 對request包裝增強
             HttpServletRequest myrequest = new MyRequest(httpServletRequest);
             chain.doFilter(myrequest, response);
        }
      
         @Override
         public void init(FilterConfig filterConfig) throws ServletException {
        }
      
      }
      
      //自定義request物件,HttpServletRequest的包裝類
      class MyRequest extends HttpServletRequestWrapper {
      
         private HttpServletRequest request;
         //是否編碼的標記
         private boolean hasEncode;
         //定義一個可以傳入HttpServletRequest物件的建構函式,以便對其進行裝飾
         public MyRequest(HttpServletRequest request) {
             super(request);// super必須寫
             this.request = request;
        }
      
         // 對需要增強方法 進行覆蓋
         @Override
         public Map getParameterMap() {
             // 先獲得請求方式
             String method = request.getMethod();
             if (method.equalsIgnoreCase("post")) {
                 // post請求
                 try {
                     // 處理post亂碼
                     request.setCharacterEncoding("utf-8");
                     return request.getParameterMap();
                } catch (UnsupportedEncodingException e) {
                     e.printStackTrace();
                }
            } else if (method.equalsIgnoreCase("get")) {
                 // get請求
                 Map<String, String[]> parameterMap = request.getParameterMap();
                 if (!hasEncode) { // 確保get手動編碼邏輯只執行一次
                     for (String parameterName : parameterMap.keySet()) {
                         String[] values = parameterMap.get(parameterName);
                         if (values != null) {
                             for (int i = 0; i < values.length; i++) {
                                 try {
                                     // 處理get亂碼
                                     values[i] = new String(values[i]
                                            .getBytes("ISO-8859-1"), "utf-8");
                                } catch (UnsupportedEncodingException e) {
                                     e.printStackTrace();
                                }
                            }
                        }
                    }
                     hasEncode = true;
                }
                 return parameterMap;
            }
             return super.getParameterMap();
        }
      
         //取一個值
         @Override
         public String getParameter(String name) {
             Map<String, String[]> parameterMap = getParameterMap();
             String[] values = parameterMap.get(name);
             if (values == null) {
                 return null;
            }
             return values[0]; // 取回引數的第一個值
        }
      
         //取所有值
         @Override
         public String[] getParameterValues(String name) {
             Map<String, String[]> parameterMap = getParameterMap();
             String[] values = parameterMap.get(name);
             return values;
        }
      }
      
      

      一般情況下,SpringMVC預設的亂碼處理就已經能夠很好的解決了!

      然後在web.xml中設定這個過濾器即可!

      亂碼問題,需要平時多注意,在儘可能能設定編碼的地方,都設定為統一編碼 UTF-8!

8、Json互動處理

8.1、什麼是JSON?

  • JSON(JavaScript Object Notation, JS 物件標記) 是一種輕量級的資料交換格式,目前使用特別廣泛。
  • 採用完全獨立於程式語言的文字格式來儲存和表示資料。
  • 簡潔和清晰的層次結構使得 JSON 成為理想的資料交換語言。
  • 易於人閱讀和編寫,同時也易於機器解析和生成,並有效地提升網路傳輸效率。

在 JavaScript 語言中,一切都是物件。因此,任何JavaScript 支援的型別都可以通過 JSON 來表示,例如字串、數位、物件、陣列等。看看他的要求和語法格式:

  • 物件表示為鍵值對,資料由逗號分隔

  • 花括號儲存物件

  • 方括號儲存陣列

  • JSON 鍵值對是用來儲存 JavaScript 物件的一種方式,和 JavaScript 物件的寫法也大同小異,鍵/值對組合中的鍵名寫在前面並用雙引號 「」 包裹,使用冒號 : 分隔,然後緊接著值:

    {"name": "QinJiang"}
    {"age": "3"}
    {"sex": "男"}
    
    

    很多人搞不清楚 JSON 和 JavaScript 物件的關係,甚至連誰是誰都不清楚。其實,可以這麼理解:

    JSON 是 JavaScript 物件的字串表示法,它使用文字表示一個 JS 物件的資訊,本質是一個字串。

    var obj = {a: 'Hello', b: 'World'};        //這是一個物件,注意鍵名也是可以使用引號包裹的
    var json = '{"a": "Hello", "b": "World"}'; //這是一個 JSON 字串,本質是一個字串
    
    

8.2、JSON 和 JavaScript 物件互轉

  • 要實現從JSON字串轉換為JavaScript 物件,使用 JSON.parse() 方法:
var obj = JSON.parse('{"a": "Hello", "b": "World"}');
//結果是 {a: 'Hello', b: 'World'}

  • 要實現從JavaScript 物件轉換為JSON字串,使用 JSON.stringify() 方法:
var json = JSON.stringify({a: 'Hello', b: 'World'});
//結果是 '{"a": "Hello", "b": "World"}'

程式碼測試

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>


    <script type="text/javascript">

        //編寫一個JavaScript物件
        var user = {
            name : '坤坤',
            age : 18,
            sex : '男'
        };


        //將js物件轉換為json物件
        let json = JSON.stringify(user);

        //將json物件轉換為js物件

        let obj = JSON.parse(json);


        //console.log(user);
        console.log(json);
        console.log(obj);

    </script>

</head>
<body>

</body>
</html>

測試結果

8.3、Controller返回JSON資料

  • Jackson應該是目前比較好的json解析工具了

  • 當然工具不止這一個,比如還有阿里巴巴的 fastjson 等等。

  • 我們這裡使用Jackson,使用它需要匯入它的jar包;

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
    <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-databind</artifactId>
       <version>2.9.8</version>
    </dependency>
    
    

設定SpringMVC需要的設定

  • (web.xml)

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!--1.註冊DispatcherServlet:這個是springmvc的核心;請求分發器,前端控制器-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
    
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    
    
    
        <!-- 設定SpringMVC的亂碼過濾 -->
        <filter>
            <filter-name>encoding</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>utf-8</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>encoding</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    </web-app>
    
  • springmvc-servlet.xml

    <?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">
    
        <!-- 自動掃描包,讓指定包下的註解生效,由IOC容器統一管理 -->
        <context:component-scan base-package="com.qjd.controller"/>
    
        <!-- 讓Spring MVC不處理靜態資源     .css  .js .html ... -->
        <mvc:default-servlet-handler />
        <!--
        支援mvc註解驅動
            在spring中一般採用@RequestMapping註解來完成對映關係
            要想使@RequestMapping註解生效
            必須向上下文中註冊DefaultAnnotationHandlerMapping
            和一個AnnotationMethodHandlerAdapter範例
            這兩個範例分別在類級別和方法級別處理。
            而annotation-driven設定幫助我們自動完成上述兩個範例的注入。
         -->
        <mvc:annotation-driven />
    
    
        <!-- 檢視解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!-- 字首 -->
            <property name="prefix" value="/WEB-INF/jsp/" />
            <!-- 字尾 -->
            <property name="suffix" value=".jsp" />
        </bean>
    
    </beans>
    
  • 我們隨便編寫一個User的實體類,然後我們去編寫我們的測試Controller;

    //需要匯入lombok
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private int id;
        private String name;
        private int age;
    }
    
  • 這裡我們需要兩個新東西,一個是@ResponseBody,一個是ObjectMapper物件,我們看下具體的用法

    編寫一個Controller;

    @Controller
    public class UserController {
    
        @RequestMapping(value = "/j1",produces = "application/json;charset=utf-8")//produces解決亂碼
        @ResponseBody   //加上這個註解就不會走檢視解析器,會直接返回一個字串
        public String json1() throws JsonProcessingException {
            
            //jackson ObjectMapper
            ObjectMapper mapper = new ObjectMapper();
    
            //建立一個物件
            User user = new User(1,"坤坤",18);
            String str = mapper.writeValueAsString(user);
    
            return str;
        }
    }
    
  • 設定Tomcat , 啟動測試一下!

8.4、程式碼優化

亂碼統一解決

上一種方法比較麻煩,如果專案中有許多請求則每一個都要新增,可以通過Spring設定統一指定,這樣就不用每次都去處理了!

我們可以在springmvc的組態檔上新增一段訊息StringHttpMessageConverter轉換設定!

<mvc:annotation-driven>
   <mvc:message-converters register-defaults="true">
       <bean class="org.springframework.http.converter.StringHttpMessageConverter">
           <constructor-arg value="UTF-8"/>
       </bean>
       <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
           <property name="objectMapper">
               <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                   <property name="failOnEmptyBeans" value="false"/>
               </bean>
           </property>
       </bean>
   </mvc:message-converters>
</mvc:annotation-driven>

測試結果

返回json字串統一解決

  • @ResponseBody解決(每個方法都得加,不建議使用)

    @RequestMapping( "/j1")
    @ResponseBody   //加上這個註解就不會走檢視解析器,會直接返回一個字串
    public String json1() throws JsonProcessingException {
        
        //jackson ObjectMapper
        ObjectMapper mapper = new ObjectMapper();
    
        //建立一個物件
        User user = new User(1,"坤坤",18);
        String str = mapper.writeValueAsString(user);
    
        return str;
    }
    
  • @RestController(直接加到類上即可)

    @RestController
    public class UserController {
       @RequestMapping(value = "/j1")
       public String json1() throws JsonProcessingException {
           //建立一個jackson的物件對映器,用來解析資料
           ObjectMapper mapper = new ObjectMapper();
           //建立一個物件
           User user = new User(1, "秦疆一號", 12);
           //將我們的物件解析成為json格式
           String str = mapper.writeValueAsString(user);
           return str;
      }
    
    }
    
    

    8.5、測試集合輸出

    增加一個新的方法

    @RequestMapping("/j2")
    @ResponseBody
    public String json2() throws JsonProcessingException {
    
    
        ObjectMapper mapper = new ObjectMapper();
    
    
        //建立一個集合
    
        List<User> userList = new ArrayList<User>();
        User user1 = new User(1,"坤坤1",18);
        User user2 = new User(2,"坤坤2",18);
        User user3 = new User(3,"坤坤3",18);
        User user4 = new User(4,"坤坤4",18);
    
    
        userList.add(user1);
        userList.add(user2);
        userList.add(user3);
        userList.add(user4);
    
        String str = mapper.writeValueAsString(userList);
    
        return str;
    }
    

8.6、輸出時間物件

  • 增加一個新的方法

    @RequestMapping("/j3")
    public String json3() throws JsonProcessingException {
    
       ObjectMapper mapper = new ObjectMapper();
    
       //建立時間一個物件,java.util.Date
       Date date = new Date();
       //將我們的物件解析成為json格式
       String str = mapper.writeValueAsString(date);
       return str;
    }
    
    

    執行結果 :是時間戳的形式

解決方案:取消timestamps形式 , 自定義時間格式

    @RequestMapping("/j3")
    @ResponseBody
    public String json3() throws JsonProcessingException {


        ObjectMapper mapper = new ObjectMapper();

        //使用ObjectMapper來格式化日期,false代表不使用時間戳的形式
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        mapper.setDateFormat(sdf);

        Date date = new Date();


        sdf.format(date);

        //ObjectMapper,時間解析後的預設格式為:Timestamp,時間戳
        String str = mapper.writeValueAsString(date);

        return str;

    }

執行結果:

進階使用

將公共部分提取出來封裝成工具類

public class JsonUtils {
    public static String getJson(Object object){
        return getJson(object,"yyyy-MM-dd HH:mm:ss");
    }


    public static String getJson(Object object,String dateFormat){

        ObjectMapper mapper = new ObjectMapper();
        //使用ObjectMapper來格式化日期,false代表不使用時間戳的形式
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);

        mapper.setDateFormat(sdf);

        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        return null;

    }
}

再輸出時間物件是即可簡化程式碼:

@RequestMapping("/j3")
    public String json3(){

        Date date = new Date();

        return JsonUtils.getJson(date,"yyyy-MM-dd HH:mm:ss");
    }
}

輸出集合物件:

@RequestMapping("/j2")
    public String json2(){

       //建立一個集合

        List<User> userList = new ArrayList<User>();
        User user1 = new User(1,"坤坤1",18);
        User user2 = new User(2,"坤坤2",18);
        User user3 = new User(3,"坤坤3",18);
        User user4 = new User(4,"坤坤4",18);


        userList.add(user1);
        userList.add(user2);
        userList.add(user3);
        userList.add(user4);

        return JsonUtils.getJson(userlist);
    }
}

8.7、FastJson

  • fastjson.jar是阿里開發的一款專門用於Java開發的包,

  • 實現json物件與JavaBean物件的轉換,

  • 實現JavaBean物件與json字串的轉換,

  • 實現json物件與json字串的轉換。

  • 實現json的轉換方法很多,最後的實現結果都是一樣的。

    <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>fastjson</artifactId>
       <version>1.2.60</version>
    </dependency>
    
    @RequestMapping("/j4")
    @ResponseBody
    //@ResponseBody//他就不會走檢視解析器,會直接返回一個 字串
    public String json4(){
    
        User user1 = new User(1, "秦疆1號", 12);
        User user2 = new User(2, "秦疆2號", 12);
        User user3 = new User(3, "秦疆3號", 12);
        User user4 = new User(4, "秦疆4號", 12);
        User user5 = new User(5, "秦疆5號", 12);
    
        List<User> list = new ArrayList<User>();
        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);
        list.add(user5);
    
        System.out.println("*******Java物件 轉 JSON字串*******");
        String str1 = JSON.toJSONString(list);
        System.out.println("JSON.toJSONString(list)==>"+str1);
        String str2 = JSON.toJSONString(user1);
        System.out.println("JSON.toJSONString(user1)==>"+str2);
    
        System.out.println("\n****** JSON字串 轉 Java物件*******");
        User jp_user1=JSON.parseObject(str2,User.class);
        System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);
    
        System.out.println("\n****** Java物件 轉 JSON物件 ******");
        JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
        System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));
    
        System.out.println("\n****** JSON物件 轉 Java物件 ******");
        User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
        System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);
    
        return JSON.toJSONString(list);
    }
    

9、Ajax

9.1、簡介

  • Ajax即Asynchronous Javascript And XML(非同步JavaScript和XML
  • Ajax這個術語源自描述從基於 Web 的應用到基於資料的應用。
  • Ajax 不是一種新的程式語言,而是一種用於建立更好更快以及互動性更強的Web應用程式的技術。
  • 使用 JavaScript 向伺服器提出請求並處理響應而不阻塞使用者核心物件XMLHttpRequest。通過這個物件,您的 JavaScript 可在不過載頁面的情況與 Web 伺服器交換資料,即在不需要重新整理頁面的情況下,就可以產生區域性重新整理的效果。
  • Ajax 在瀏覽器與 Web 伺服器之間使用非同步資料傳輸(HTTP 請求),這樣就可使網頁從伺服器請求少量的資訊,而不是整個頁面。

AJAX 工作原理:

  • Ajax 是一種獨立於 Web 伺服器軟體的瀏覽器技術。 
  • Ajax 基於下列 Web 標準:
    • JavaScript、XML、HTML與 CSS 在 Ajax 中使用的 Web 標準已被良好定義,並被所有的主流瀏覽器支援。Ajax 應用程式獨立於瀏覽器和平臺。
    • Web 應用程式較桌面應用程式有諸多優勢;它們能夠涉及廣大的使用者,它們更易安裝及維護,也更易開發。
    • 不過,因特網應用程式並不像傳統的桌面應用程式那樣完善且友好。通過 Ajax,因特網應用程式可以變得更完善,更友好。
    • 增強B/S的體驗性(瀏覽器端與伺服器端)

9.2、偽造Ajax

我們可以使用前端的一個標籤來偽造一個ajax的樣子。iframe標籤

  1. 新建一個module :sspringmvc-06-ajax , 匯入web支援!

  2. 編寫一個 ajax-frame.html 使用 iframe 測試,感受下效果

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>iframe體驗頁面無重新整理</title>
    </head>
    <body>
    
    <script type="text/javascript">
    
        window.onload = function f() {
            var myDate = new Date();
            document.getElementById('currentTime').innerText = myDate.getTime();
        }
    
    
        function loadPage() {
            var targetURL = document.getElementById('url').value;
            console.log(targetURL);
            document.getElementById('iframePosition').src = targetURL;
        }
    
    </script>
    
    
    <div>
        <p>顯示時間(1970毫秒):<span id="currentTime"></span></p>
        <p>請輸入要載入的地址:</p>
        <p>
            <input type="text" id="url" value="https://www.bilibili.com/">
            <input type="button" value="提交" onclick="loadPage()">
    
        </p>
    
    </div>
    
    
    <div>
    
        <h3>
            載入頁面的位置
        </h3>
    
        <iframe style="width: 100%;height: 500px" id="iframePosition">
    
        </iframe>
    
    
    </div>
    </body>
    </html>
    

    測試結果:

利用AJAX可以做:

  • 註冊時,輸入使用者名稱自動檢測使用者是否已經存在。
  • 登陸時,提示使用者名稱密碼錯誤
  • 刪除資料行時,將行ID傳送到後臺,後臺在資料庫中刪除,資料庫刪除成功後,在頁面DOM中將資料行也刪除。
  • …等等

9.3、jQuery與Ajax

  • Ajax的核心是xhr,xhr為向伺服器傳送請求和解析伺服器響應提供了介面,能夠以非同步的方式從伺服器獲取新資料

  • jQuery提供多個與AJAX有關的方法:

    • 通過jQuery AJAX方法,您能夠使用HTTP Get和HTTP Post從遠端伺服器上請求文字、HTML、XML或JSON一同時您能夠把這些外部資料直接載入網頁的被選元素中。

    • Query Ajax-本質就是XMLHttpRequest,對他進行了封裝,方便呼叫!

      jQuery.ajax(...)
            部分引數:
                  url:請求地址
                  data:要傳送的資料
              success:成功之後執行的回撥函數(全域性)
                error:失敗之後執行的回撥函數(全域性)
            
      

使用jQuery需要先匯入jQuery的js檔案

匯入jquery , 可以使用線上的CDN , 也可以下載匯入

<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.1.1.min.js"></script>

我們來個簡單的測試,使用最原始的HttpServletResponse處理 , .最簡單 , 最通用(滑鼠失去焦點的時候非同步重新整理)

  1. 設定web.xml 和 springmvc的組態檔【記得靜態資源過濾和註解驅動設定上】

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
    
        <!--1.註冊DispatcherServlet:這個是springmvc的核心;請求分發器,前端控制器-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:application.xml</param-value>
            </init-param>
    
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    
    
        <!-- 設定SpringMVC的亂碼過濾 -->
        <filter>
            <filter-name>encoding</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>utf-8</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>encoding</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
    </web-app>
    
    <?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">
    
        <!-- 自動掃描包,讓指定包下的註解生效,由IOC容器統一管理 -->
        <context:component-scan base-package="com.qjd.controller"/>
    
        <!-- 靜態資源過濾    -->
        <mvc:default-servlet-handler/>
    
        <!-- 註解支援   -->
        <mvc:annotation-driven/>
    
    
        <!-- 檢視解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!-- 字首 -->
            <property name="prefix" value="/WEB-INF/jsp/" />
            <!-- 字尾 -->
            <property name="suffix" value=".jsp" />
        </bean>
    
    </beans>
    
  2. 編寫一個AjaxController

    @RestController
    public class AjaxController {
        @RequestMapping("/t1")
        public String test(){
            return "hello";
    
        }
    
        @RequestMapping("/a1")
        public void a1(String name, HttpServletResponse response) throws IOException {
    
            System.out.println("a1:param=>"+name);
            if("kunkun".equals(name)){
                response.getWriter().println("true");
            }else {
                response.getWriter().println("false");
            }
    
        }
    }
    
  3. 匯入jquery , 可以使用線上的CDN , 也可以下載匯入(注意這裡匯入不成功會出現400錯誤)

  4. 編寫index.jsp測試

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
    
        <!--jQuery引入-->
    <%--    <script src="http://code.jquery.com/jquery-3.6.0.min.js"></script>--%>
        <script src = "${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>
    
        <script>
          function a(){
            $.post({
              url:"${pageContext.request.contextPath}/a1",
              data:{"name":$("#username").val()},
              success:function (data){
                alert(data);
              },
              error:function (){
                alert("error");
              }
            })
          }
    
    
    
        </script>
      </head>
      <body>
    
      <%--失去焦點的時候,發起一個請求(攜帶資訊)到後臺 --%>
      使用者名稱:<input type="text" id="username" onblur="a()">
    
    
    
      </body>
    </html>
    
  5. 啟動tomcat測試!開啟瀏覽器的控制檯,當我們滑鼠離開輸入框的時候,可以看到發出了一個ajax的請求!是後臺返回給我們的結果!測試成功!

Ajax把主動權交給了前端

9.4、Springmvc實現

在頁面點選載入資料按鈕即可顯示出資料(XHR非同步請求)

  1. 實體類user(使用了lombok外掛,可以自己寫實現類方法)

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private String name;
        private int age;
        private String sex;
    }
    
  2. 我們來獲取一個集合物件,展示到前端頁面

    @RequestMapping("/a2")
    public List<User> a2(){
        List<User> userList = new ArrayList<>();
    
        //新增資料
        userList.add(new User("張三",10,"男"));
        userList.add(new User("李四",12,"男"));
        userList.add(new User("王五",13,"男"));
    
        return userList;
    }
    
  3. 前端頁面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
        <!--jQuery引入-->
        <%--    <script src="http://code.jquery.com/jquery-3.6.0.min.js"></script>--%>
        <script src = "${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>
    
        <script>
    
            $(function (){
                $("#btn").click(function (){
                    $.post({
                        url:"${pageContext.request.contextPath}/a2",
                        success:function(data){
                            //console.log(data);
                            let html = "";
                            for (let i = 0; i < data.length; i++) {
                                html += "<tr>" +
                                    "<td>" + data[i].name + "</td>" +
                                    "<td>" + data[i].age + "</td>" +
                                    "<td>" + data[i].sex + "</td>" +
    
                                    "</tr>"
    
                            }
                            $("#content").html(html);
    
                        }
    
                    });
    
                })
            });
    
    
        </script>
    
    </head>
    <body>
    
    <input type="button" value="載入資料" id="btn">
    
    <table>
    
        <tr>
            <td>姓名</td>
            <td>年齡</td>
            <td>性別</td>
        </tr>
    
        <tbody id="content">
        <%--資料:後臺    --%>
        </tbody>
    
    </table>
    
    </body>
    </html>
    
  4. 測試結果

9.5、註冊提示效果

  1. 我們寫一個Controller(請求處理)

    @RequestMapping("/a3")
    public String a3(String name,String pwd){
        String msg = "";
        if (name != null) {
            //admin  這些資料應該在資料庫中查詢
            if ("admin".equals(name)){
                msg = "ok";
            }else {
                msg = "使用者名稱有誤";
            }
        }
        if (pwd != null) {
            //123456  這些資料應該在資料庫中查詢
            if ("123456".equals(pwd)){
                msg = "ok";
            }else {
                msg = "密碼有誤";
            }
        }
        return msg;
    }
    
  2. 前端頁面Login.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
        <!--jQuery引入-->
        <%--    <script src="http://code.jquery.com/jquery-3.6.0.min.js"></script>--%>
        <script src = "${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>
    
        <script>
            //失去焦點
            function a1(){
                $.post({
                    url:"${pageContext.request.contextPath}/a3",
                    data:{"name":$("#name").val()},
                    success:function (data) {
                      if(data.toString() == "ok"){
                          $("#userInfo").css("color","green");
                      }else {
                          $("#userInfo").css("color","red");
                      }
                      $("#userInfo").html(data);
                    }
                })
            }
    
            function a2(){
                $.post({
                    url:"${pageContext.request.contextPath}/a3",
                    data:{"pwd":$("#pwd").val()},
                    success:function (data) {
                        if(data.toString() == "ok"){
                            $("#pwdInfo").css("color","green");
                        }else {
                            $("#pwdInfo").css("color","red");
                        }
                        $("#pwdInfo").html(data);
    
                    }
                })
            }
        </script>
    
    
    </head>
    <body>
    
    <p>
        使用者名稱:<input type="text" id="name" onblur="a1()"/>
        <span id="userInfo"></span>
    </p>
    <p>
        密碼:<input type="text" id="pwd" onblur="a2()"/>
        <span id="pwdInfo"></span>
    </p>
    
    
    </body>
    </html>
    
  3. 處理亂碼問題

    <mvc:annotation-driven>
        <!--JSON亂碼問題設定        -->
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    
  4. 測試結果

9.6、獲取百度介面Demo

<!DOCTYPE HTML>
<html>
<head>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   <title>JSONP百度搜尋</title>
   <style>
       #q{
           width: 500px;
           height: 30px;
           border:1px solid #ddd;
           line-height: 30px;
           display: block;
           margin: 0 auto;
           padding: 0 10px;
           font-size: 14px;
      }
       #ul{
           width: 520px;
           list-style: none;
           margin: 0 auto;
           padding: 0;
           border:1px solid #ddd;
           margin-top: -1px;
           display: none;
      }
       #ul li{
           line-height: 30px;
           padding: 0 10px;
      }
       #ul li:hover{
           background-color: #f60;
           color: #fff;
      }
   </style>
   <script>

       // 2.步驟二
       // 定義demo函數 (分析介面、資料)
       function demo(data){
           var Ul = document.getElementById('ul');
           var html = '';
           // 如果搜尋資料存在 把內容新增進去
           if (data.s.length) {
               // 隱藏掉的ul顯示出來
               Ul.style.display = 'block';
               // 搜尋到的資料迴圈追加到li裡
               for(var i = 0;i<data.s.length;i++){
                   html += '<li>'+data.s[i]+'</li>';
              }
               // 迴圈的li寫入ul
               Ul.innerHTML = html;
          }
      }

       // 1.步驟一
       window.onload = function(){
           // 獲取輸入框和ul
           var Q = document.getElementById('q');
           var Ul = document.getElementById('ul');

           // 事件滑鼠擡起時候
           Q.onkeyup = function(){
               // 如果輸入框不等於空
               if (this.value != '') {
                   // ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆JSONPz重點☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
                   // 建立標籤
                   var script = document.createElement('script');
                   //給定要跨域的地址 賦值給src
                   //這裡是要請求的跨域的地址 我寫的是百度搜尋的跨域地址
                   script.src = 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+this.value+'&cb=demo';
                   // 將組合好的帶src的script標籤追加到body裡
                   document.body.appendChild(script);
              }
          }
      }
   </script>
</head>

<body>
<input type="text" id="q" />
<ul id="ul">

</ul>
</body>
</html>

10、攔截器

10.1、概述

  • SpringMVC的處理器攔截器類似於Servlet開發中的過濾器Filter,用於對處理器進行預處理和後處理。
  • 開發者可以自己定義一些攔截器來實現特定的功能。

過濾器與攔截器的區別:攔截器是AOP思想的具體應用。

過濾器

  • servlet規範中的一部分,任何javaweb工程都可以使用

  • 在url-pattern中設定了/*之後,可以對所有要存取的資源進行攔截

攔截器

  • 攔截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用

  • 攔截器只會攔截存取的控制器方法, 如果存取的是jsp/html/css/image/js是不會進行攔截的(自帶靜態資源過濾)

10.2、自定義攔截器

  1. 新建一個Moudule , springmvc-Interceptor , 新增web支援

  2. 設定web.xml 和 springmvc-servlet.xml 檔案

  3. 編寫一個攔截器(必須實現 HandlerInterceptor 介面)

    public class MyInterceptor implements HandlerInterceptor {
        //return true; 執行下一個攔截器,放行
        //return false; 不執行下一個攔截器,攔截(卡在攔截器,不能執行請求)
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("===========處理前===========");
            return true;
        }
    
        //紀錄檔
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
            System.out.println("===========處理後===========");
        }
    
        //紀錄檔
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
            System.out.println("===========清理===========");
        }
    }
    
  4. 在springmvc的組態檔中設定攔截器

    <!--攔截器設定  -->
    <mvc:interceptors>
        <mvc:interceptor>
    <!--包括這個請求下面的所有請求-->
            <mvc:mapping path="/**"/>
            <bean class="com.qjd.config.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
  5. 編寫一個Controller,接收請求

    @RestController
    public class TestController {
    
        @RequestMapping("/t1")
        public String test(){
            System.out.println("TestController->test()執行了");
    
            return "ok";
        }
    }
    
  6. 啟動tomcat 測試一下!(http://localhost:8888/t1)

10.3、驗證使用者是否登入 (認證使用者)

實現思路:

  • 有一個登陸頁面,需要寫一個controller存取頁面。

  • 登陸頁面有一提交表單的動作。需要在controller中處理。判斷使用者名稱密碼是否正確。如果正確,向session中寫入使用者資訊。返回登陸成功。

  • 攔截使用者請求,判斷使用者是否登陸。如果使用者已經登陸。放行, 如果使用者未登陸,跳轉到登陸頁面

測試:

  1. 編寫一個登陸頁面 login.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    
    
    <%--在web-inf下面的所有頁面或者資源,只能通過controller或者Servlet進行存取--%>
    <h1>登入頁面</h1>
    
    <form action="${pageContext.request.contextPath}/user/login" method="post">
        使用者名稱:<input type="text" name="username">
        密碼:<input type="text" name="password">
        <input type="submit" value="提交">
    </form>
    
    </body>
    </html>
    
  2. 編寫一個Controller處理請求

    @Controller
    @RequestMapping("/user")
    public class LoginController {
    
        @RequestMapping("/login")
        public String login(String username, String password, HttpSession session, Model model){
            //把使用者的資訊存在session中
            session.setAttribute("userLoginInfo",username);
            model.addAttribute("username",username);
            return "main";
        }
    
        @RequestMapping("/goOut")
        public String goOut(String username, String password, HttpSession session, Model model){
    
            session.removeAttribute("userLoginInfo");
            return "main";
        }
    
    
        @RequestMapping("/main")
        public String main(){
            return "main";
        }
    
        @RequestMapping("/goLogin")
        public String login(){
            return "login";
        }
    }
    
  3. 編寫一個登陸成功的頁面 main.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    
    <h1>首頁</h1>
    <p>
        <span>${username}</span>
    </p>
    
    <p>
        <a href="${pageContext.request.contextPath}/user/goOut">登出</a>
    </p>
    
    </body>
    </html>
    
  4. 在 index 頁面上測試跳轉!啟動Tomcat 測試,未登入也可以進入主頁!

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
    
      <body>
      <h1><a href="${pageContext.request.contextPath}/user/goLogin">登入頁面</a></h1>
      <h1><a href="${pageContext.request.contextPath}/user/main">首頁</a></h1>
    
    
      </body>
    </html>
    
  5. 編寫使用者登入攔截器

    public class LoginInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            HttpSession session = request.getSession();
            //放行:判斷什麼情況下登入
    
            //登入頁面也會放行
            if (request.getRequestURI().contains("goLogin")) {
                return true;
            }
            if (request.getRequestURI().contains("login")) {
                return true;
            }
            if (session.getAttribute("userLoginInfo") != null) {
                return true;
            }
            //判斷什麼情況下沒有登入
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
            return false;
        }
    
    
    
    }
    
  6. 在Springmvc的組態檔中註冊攔截器

    <!--攔截器設定  -->
    <mvc:interceptors>
        <mvc:interceptor>
    <!--包括這個請求下面的所有請求-->
            <mvc:mapping path="/**"/>
            <bean class="com.qjd.config.MyInterceptor"/>
        </mvc:interceptor>
    
        <mvc:interceptor>
            <!--包括這個請求下面的所有請求-->
            <mvc:mapping path="/user/**"/>
            <bean class="com.qjd.config.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
  7. 再次重啟Tomcat測試!

    (1)

    (2)

11、檔案上傳和檔案下載

11.1、準備工作

  • 檔案上傳是專案開發中最常見的功能之一 ,springMVC 可以很好的支援檔案上傳。
  • SpringMVC上下文中預設沒有裝配MultipartResolver,因此預設情況下其不能處理檔案上傳工作。如果想使用Spring的檔案上傳功能,則需要在上下文中設定MultipartResolver。
  • 前端表單要求:為了能上傳檔案,必須將表單的method設定為POST,並將enctype設定為multipart/form-data。只有在這樣的情況下,瀏覽器才會把使用者選擇的檔案以二進位制資料傳送給伺服器;

對錶單中的 enctype 屬性做個詳細的說明:

  • application/x-www=form-urlencoded:預設方式,只處理表單域中的 value 屬性值,採用這種編碼方式的表單會將表單域中的值處理成 URL 編碼方式。
  • multipart/form-data:這種編碼方式會以二進位制流的方式來處理表單資料,這種編碼方式會把檔案域指定檔案的內容也封裝到請求引數中,不會對字元編碼。
  • text/plain:除了把空格轉換為 「+」 號外,其他字元都不做編碼處理,這種方式適用直接通過表單傳送郵件。
<%--enctype="multipart/form-data"  代表以二進位制流的形式提交  --%>
  <form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
    <input type="file" name="file"/>
    <input type="submit" value="upload">
  </form>

一旦設定了enctype為multipart/form-data,瀏覽器即會採用二進位制流的方式來處理表單資料,而對於檔案上傳的處理則涉及在伺服器端解析原始的HTTP響應。在2003年,Apache Software Foundation釋出了開源的Commons FileUpload元件,其很快成為Servlet/JSP程式設計師上傳檔案的最佳選擇。

  • Servlet3.0規範已經提供方法來處理檔案上傳,但這種上傳需要在Servlet中完成。
  • 而Spring MVC則提供了更簡單的封裝。
  • Spring MVC為檔案上傳提供了直接的支援,這種支援是用隨插即用的MultipartResolver實現的。
  • Spring MVC使用Apache Commons FileUpload技術實現了一個MultipartResolver實現類:
  • CommonsMultipartResolver。因此,SpringMVC的檔案上傳還需要依賴Apache Commons FileUpload的元件。

11.2、檔案上傳

  1. 匯入檔案上傳的jar包,commons-fileupload , Maven會自動幫我們匯入他的依賴包 commons-io包;

    <!--檔案上傳-->
    <dependency>
       <groupId>commons-fileupload</groupId>
       <artifactId>commons-fileupload</artifactId>
       <version>1.3.3</version>
    </dependency>
    <!--servlet-api匯入高版本的-->
    <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>javax.servlet-api</artifactId>
       <version>4.0.1</version>
    </dependency>
    
    
  2. 設定bean:multipartResolver

    <!--檔案上傳設定-->
    <bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 請求的編碼格式,必須和jSP的pageEncoding屬性一致,以便正確讀取表單的內容,預設為ISO-8859-1 -->
        <property name="defaultEncoding" value="utf-8"/>
        <!-- 上傳檔案大小上限,單位為位元組(10485760=10M) -->
        <property name="maxUploadSize" value="10485760"/>
        <property name="maxInMemorySize" value="40960"/>
    </bean>
    

    CommonsMultipartFile 的 常用方法:

    1. String getOriginalFilename():獲取上傳檔案的原名

    2. InputStream getInputStream():獲取檔案流

    3. void transferTo(File dest):將上傳檔案儲存到一個目錄檔案中

  3. 編寫前端頁面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
    <%--enctype="multipart/form-data"  代表以二進位制流的形式提交  --%>
      <form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
        <input type="file" name="file"/>
        <input type="submit" value="upload">
      </form>
    
    <a href="${pageContext.request.contextPath}/statics/1.jpg">點選下載</a>
    
      </body>
    </html>
    
  4. Controller

    這個controller包含問價能上傳的兩種方法(fileUpload,fileUpload2),檔案下載的一種方法(downloads)

    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.commons.CommonsMultipartFile;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    import java.net.URLEncoder;
    
    
    @RestController
    public class FilerController {
    
        //@RequestParam("file") 將name=file控制元件得到的檔案封裝成CommonsMultipartFile 物件
        //批次上傳CommonsMultipartFile則為陣列即可
        @RequestMapping("/upload")
        public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {
    
            //獲取檔名 : file.getOriginalFilename();
            String uploadFileName = file.getOriginalFilename();
    
            //如果檔名為空,直接回到首頁!
            if ("".equals(uploadFileName)){
                return "redirect:/index.jsp";
            }
            System.out.println("上傳檔名 : "+uploadFileName);
    
            //上傳路徑儲存設定
            String path = request.getServletContext().getRealPath("/upload");
            //如果路徑不存在,建立一個
            File realPath = new File(path);
            if (!realPath.exists()){
                realPath.mkdir();
            }
            System.out.println("上傳檔案儲存地址:"+realPath);
    
            InputStream is = file.getInputStream(); //檔案輸入流
            OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //檔案輸出流
    
            //讀取寫出
            int len=0;
            byte[] buffer = new byte[1024];
            while ((len=is.read(buffer))!=-1){
                os.write(buffer,0,len);
                os.flush();
            }
            os.close();
            is.close();
            return "redirect:/index.jsp";
        }
    
        /*
         * 採用file.Transto 來儲存上傳的檔案
         */
        @RequestMapping("/upload2")
        public String  fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
    
            //上傳路徑儲存設定
            String path = request.getServletContext().getRealPath("/upload");
            File realPath = new File(path);
            if (!realPath.exists()){
                realPath.mkdir();
            }
            //上傳檔案地址
            System.out.println("上傳檔案儲存地址:"+realPath);
    
            //通過CommonsMultipartFile的方法直接寫檔案(注意這個時候)
            file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));
    
            return "redirect:/index.jsp";
        }
    
        @RequestMapping(value="/download")
        public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
            //要下載的圖片地址
            String  path = request.getServletContext().getRealPath("/upload");
            String  fileName = "1.jpg";
    
            //1、設定response 響應頭
            response.reset(); //設定頁面不快取,清空buffer
            response.setCharacterEncoding("UTF-8"); //字元編碼
            response.setContentType("multipart/form-data"); //二進位制傳輸資料
            //設定響應頭
            response.setHeader("Content-Disposition",
                    "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));
    
            File file = new File(path,fileName);
            //2、 讀取檔案--輸入流
            InputStream input=new FileInputStream(file);
            //3、 寫出檔案--輸出流
            OutputStream out = response.getOutputStream();
    
            byte[] buff =new byte[1024];
            int index=0;
            //4、執行 寫出操作
            while((index= input.read(buff))!= -1){
                out.write(buff, 0, index);
                out.flush();
            }
            out.close();
            input.close();
            return null;
        }
    
    }
    
  5. 測試結果

    (1)檔案上傳

因為Controller使用的@RestController,所以返回的是字串

(2)檔案下載

輸入download下載檔案:


ok,下載成功!

到這裡關於SpringMVC的知識就結束了,並且SSM框架也結束了,如果任何文章有遺漏或錯誤歡迎大家提出,我會第一時間修改。關於SSM框架,大家如果想要深入理解SSM框架,就要多去看開源的專案,在專案中更深入理解SSM.
最後,關於SSM整合--簡易書籍管理系統大家可以看下面的文章:SSM框架整合---簡易書籍管理系統