SprinMvc快速入門

王延領發表於2021-11-01

1.spring mvc

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

img

檢視官方文件:https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.html#spring-web

1.1.三層結構

B/S架構系統標準的三層架構包括表現層業務層持久層,每一層各司其職, 接下來我們就說說每層都負責哪些方面。

表現層

也就是我們常說的web層。它負責接收客戶端請求,向客戶端響應結果,通常客戶端使用http協議請求web 層,web 需要接收 http 請求,完成 http 響應。

  1. 表現層又包括展示層和控制層:控制層負責接收請求,展示層負責結果的展示。

  2. 表現層依賴業務層,接收到客戶端請求一般會呼叫業務層進行業務處理,並將處理結果響應給客戶端。

  3. 表現層的設計一般都使用 MVC 模型。(MVC 是表現層的設計模型,和其他層沒有關係)

業務層:

也就是我們常說的 service 。它負責業務邏輯處理,和我們開發專案的需求息息相關。

  1. web 層依賴業務層,但是業務層不依賴 web 層。

  2. 業務層在業務處理時可能會依賴持久層,如果要對資料持久化需要保證事務一致性。(也就是我們說的,事務應該放到業務層來控制)

持久層

也就是我們是常說的 dao 。負責資料持久化,包括資料層即資料庫和資料訪問層,資料庫是對資料進行持久化的載體,資料訪問層是業務層和持久層互動的介面,業務層需要通過資料訪問層將資料持久化到資料庫中。通俗的講,持久層就是和資料庫互動,對資料庫表進行曾刪改查的。

通過分層更好的實現了各個部分的職責,在每一層將再細化出不同的框架,分別解決各層關注的問題。三層架構與SSM的關係示意圖如下,其中SpringMVC屬於表現層框架,MyBatis屬於持久層框架,而Spring不屬於任何一層,是用來整合其它框架的。

image-20210918164605392

1.2.mvc 模型

MVC全名是Model View Controller,是模型(model)-檢視(view)-控制器(controller)的縮寫,是一種用於設計建立 Web 應用程式表現層的模式。MVC 中每個部分各司其職:

Model(模型)

指的就是我們的資料模型,一般情況下用於封裝資料。

View(檢視)

指的就是我們的 jsp 或者 html等頁面,一般用於展示資料的,其是依據模型資料建立的。

Controller(控制器)

是應用程式中處理使用者互動的部分,用來處理程式邏輯的。例如引數校驗等。

image-20210918164819509

1.3.Spring MVC

1.3.1.特點:

  1. 清晰的角色劃分

    前端控制器(DispatcherServlet)

    請求到處理器對映(HandlerMapping)

    處理器介面卡(HandlerAdapter)

    檢視解析器(ViewResolver)

    處理器或頁面控制器(Controller)

    驗證器( Validator)

    命令物件(Command 請求引數繫結到的物件就叫命令物件)

    表單物件(Form Object 提供給表單展示和提交到的物件就叫表單物件)。

  2. 分工明確,而且擴充套件點相當靈活,可以很容易擴充套件,雖然幾乎不需要。

  3. 由於命令物件就是一個 POJO,無需繼承框架特定 API,可以使用命令物件直接作為業務物件。

  4. 和 Spring 其他框架無縫整合,是其它 Web 框架所不具備的。

  5. 可適配,通過 HandlerAdapter 可以支援任意的類作為處理器。

  6. 可定製性,HandlerMapping、ViewResolver 等能夠非常簡單的定製。

  7. 功能強大的資料驗證、格式化、繫結機制。

  8. 利用 Spring 提供的 Mock 物件能夠非常簡單的進行 Web 層單元測試。

  9. 本地化、主題的解析的支援,使我們更容易進行國際化和主題的切換。

  10. 強大的 JSP 標籤庫,使 JSP 編寫更容易。

  11. 還有比如RESTful風格的支援、簡單的檔案上傳、約定大於配置的契約式程式設計支援、基於註解的零配置支援,資料驗證、格式化、本地化、主題等等。

  12. 用的人多。

1.3.2.執行流程

gzyl

1.4.SpringMVC 和 Struts2

共同點

  1. 它們都是表現層框架,都是基於 MVC 模型編寫的。

  2. 它們的底層都離不開原始 ServletAPI。

  3. 它們處理請求的機制都是一個核心控制器。

區別:

  1. Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter

  2. Spring MVC 是基於方法設計的,而 Struts2 是基於類,Struts2 每次執行都會建立一個動作類。所以 Spring MVC 會稍微比 Struts2 快些。

  3. Spring MVC 使用更加簡潔,同時還支援 JSR303, 處理 ajax 的請求更方便(JSR303 是一套 JavaBean 引數校驗的標準,它定義了很多常用的校驗註解,我們可以直接將這些註解加在我們 JavaBean 的屬性上面,就可以在需要校驗的時候進行校驗了。)

  4. Struts2 的 OGNL 表示式使頁面的開發效率相比 Spring MVC 更高些,但執行效率並沒有比 JSTL 提升,尤其是 struts2 的表單標籤,遠沒有 html 執行效率高

2.spring mvc入門demo

2.1.配置版

建立model,新增目錄java,resources.

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

  1. 新建一個web專案
  2. 匯入相關jar包
  3. 編寫web.xml , 註冊DispatcherServlet
  4. 編寫springmvc配置檔案
  5. 接下來就是去建立對應的控制類 , controller
  6. 最後完善前端檢視和controller之間的對應
  7. 測試執行除錯.

2.1.1.新增依賴

<dependencies>
    <!--spring-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!--spring-web-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!--spring-mvc-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!--servletAPI-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
</dependencies>

2.1.2.配置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>

2.1.3.新增資原始檔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">

    <!--新增 處理對映器-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!--新增 處理器介面卡-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    <!--檢視解析器:DispatcherServlet給他的ModelAndView-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
        <!--字首-->
        <property name="prefix" value="/WEB-INF/page/"/>
        <!--字尾-->
        <property name="suffix" value=".jsp"/>
    </bean>
    <!--將自己的類交給SpringIOC容器,註冊bean-->
    <!--Handler-->
    <bean id="/hello" class="com.wyl.HelloController"/>
</beans>

使用springMVC必須配置的三大件:

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

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

2.1.4.新增軟體包 com.wyl.controller,新增controller

package com.wyl.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @建立人 王延領
 * @建立時間 2021/10/15
 * 描述
 **/
public class HelloController implements Controller{
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        //ModelAndView模型和檢視
        ModelAndView mv = new ModelAndView();
        //封裝物件,放在ModeAndView
        mv.addObject("msg","王延領學習java");
        //封裝要跳轉的檢視,放在ModelAndView中
        mv.setViewName("hello");
        return mv;
    }
}

2.1.5. 新增對映

 <!--將自己的類交給SpringIOC容器,註冊bean-->
    <!--Handler-->
    <bean id="/hello" class="com.wyl.HelloController"/>

2.1.6.新增頁面

image-20211019104451195

2.1.7.tomcat執行

image-20211019104740239

2.2.註解版

2.2.1.新增依賴

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--spring-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!--spring-web-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!--spring-mvc-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!--servletAPI-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>RELEASE</version>
          <scope>compile</scope>
      </dependency>
  </dependencies>

2.2.1.配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
 <!--不要web-app,不然model 返回失效-->
 <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">
 
    <!--註冊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 > 
	不會匹配到.jsp, 只針對我們編寫的請求;即:.jsp 不會進入spring的 DispatcherServlet類 。
< url-pattern > /* </ url-pattern > 
	會匹配 *.jsp,會出現返回 jsp檢視 時再次進入spring的DispatcherServlet 類,導致找不到對應的controller所以報404錯-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
 </web-app>

2.2.3.新增資原始檔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.wyl.controller"/>
   <!-- 讓Spring MVC不處理靜態資源 -->
   <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>

2.2.4.新增軟體包 com.wyl.controller,新增controller

package com.wyl.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @建立人 王延領
 * @建立時間 2021/10/18
 * 描述
 **/
@Controller
@RequestMapping("/hello")
public class HelloController {
    @RequestMapping("/sayHello")
    public String hello(Model model) {
        //會被檢視解析器處理
        return "hello";
    }
}

2.1.5.新增頁面

<%--
  Created by IntelliJ IDEA.
  User: 17144
  Date: 2021/10/15
  Time: 23:55
  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>
成功!!!
</body>
</html>

3.控制器

3.1.@Controller

  • @Controller註解型別用於宣告Spring類的例項是一個控制器;

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

    <!-- 自動掃描指定的包,下面所有註解類交給IOC容器管理 -->
    <context:component-scan base-package="com.wyl.controller"/>
    
    //@Controller註解的類會自動新增到Spring上下文中
    @Controller
    public class ControllerTest2{
    
       //對映訪問路徑
       @RequestMapping("/t2")
       public String index(Model model){
           //Spring MVC會自動例項化一個Model物件用於向檢視中傳值
           model.addAttribute("msg", "ControllerTest2");
           //返回檢視位置
           return "test";
      }
    }
    

3.2.@RequestMapping

3.2.1.RequestMapping註解:

* 作用:

* 用於建立請求URL和處理請求方法之間的對應關係

* 它可以作用在方法、類上

* 作用來類上:作用在類上時,類上的路徑作為一級目錄

* 在訪問方法時需要加類上的路徑

* 作用在方法上:指定這個方法要攔截處理哪個URL請求

* 屬性:

* value:與path屬性的作用是相同的,當只有value一個屬性時,屬性可以省略,直接寫屬性值

* path:與value屬性作用一致,用於指定請求的URL

* method:用於指定請求的方式,比如POST、GET等使用RequestMethod列舉

* params:用於指定限制請求引數的條件,它支援簡單的表示式

* 要求請求引數的key和value必須和配置的一模一樣

* headers:用於指定限制請求訊息頭的條件

@Controller
@RequestMapping("/User")
public class RequestMappingDemoController {
    @RequestMapping("SetView")
    public String SetView(){
        System.out.println("Hello SpringMvc");
        //返回邏輯檢視名
        return "success";
    }
    //類上加了@RequestMapping註解後,此方法攔截的URL變成了:user/testRequestMapping
    @RequestMapping("/testRequestMapping")
    public String testRequestMapping(){
        System.out.println("測試RequestMapping註解。。");
        //返回邏輯檢視名
        return "success";
    }

    //指定此方法只能由POST請求訪問
    @RequestMapping(path = {"/testRequestMappingMethod"},method = {RequestMethod.POST})
    public String testRequestMappingMethod(){
        System.out.println("測試RequestMapping註解的method屬性。。");
        //返回邏輯檢視名
        return "success";
    }
    //指定此方法必須帶有name屬性,且屬性值必須為admin
    //http://localhost:8080/User/testRequestMappingParams?name=admin
    @RequestMapping(path = {"/testRequestMappingParams"},params = {"name=admin"})
    public String testRequestMappingParams(){
        System.out.println("測試RequestMapping註解的params屬性。。");
        //返回邏輯檢視名
        return "success";
    }
    //指定請求頭中必須包含accept屬性
    @RequestMapping(path = {"/testRequestMappingHeaders"},headers = {"accept"})
    public String testRequestMappingHeaders(){
        System.out.println("測試RequestMapping註解的headers屬性。。");
        //返回邏輯檢視名
        return "success";
    }
  }

3.2.2.RestFul風格

    //對映訪問路徑
   @RequestMapping("/commit/{p1}/{p2}")
   public String index(@PathVariable int p1, @PathVariable int p2, Model model){
       
       int result = p1+p2;
       //Spring MVC會自動例項化一個Model物件用於向檢視中傳值
       model.addAttribute("msg", "結果:"+result);
       //返回檢視位置
       return "test";
       
  }   
  • REST(英文:Representational State Transfer,簡稱REST)描述了一個架構樣式的網路系統,比如 web 應用程式。它首次出現在 2000 年 Roy Fielding 的博士論文中,他是 HTTP 規範的主要編寫者之一。在目前主流的三種Web服務互動方案中,REST相比於SOAP(Simple Object Access protocol,簡單物件訪問協議)以及XML-RPC更加簡單明瞭,無論是對URL的處理還是對Payload的編碼,REST都傾向於用更加簡單輕量的方法設計和實現。值得注意的是REST並沒有一個明確的標準,而更像是一種設計的風格。

  • 它本身並沒有什麼實用性,其核心價值在於如何設計出符合REST風格的網路介面。

  • restful的優點 :

    • 它結構清晰、符合標準、易於理解、擴充套件方便,所以正得到越來越多網站的採用。
  • restful的特性:

    • 資源(Resources):

      • 網路上的一個實體,或者說是網路上的一個具體資訊。 它可以是一段文字、一張圖片、一首歌曲、一種服務,總之就是一個具體的存在。可以用一個URI(統一資源定位符)指向它,每種資源對應一個特定的 URI 。要 獲取這個資源,訪問它的URI就可以,因此 URI 即為每一個資源的獨一無二的識別符。
    • 表現層(Representation):

      • 把資源具體呈現出來的形式,叫做它的表現層 (Representation)。 比如,文字可以用 txt 格式表現,也可以用 HTML 格式、XML 格式、JSON 格式表現,甚至可以採用二進位制格式。
    • 狀態轉化(State Transfer):

      • 每 發出一個請求,就代表了客戶端和伺服器的一次互動過程。 HTTP協議,是一個無狀態協議,即所有的狀態都儲存在伺服器端。因此,如果客戶端想要操作伺服器,必須通過某種手段,讓伺服器端發生“狀態轉化”(State Transfer)。而這種轉化是建立在表現層之上的,所以就是 “表現層狀態轉化”。具體說,就是 HTTP 協議裡面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET 用來獲取資源,POST 用來新建資源,PUT 用來更新資源,DELETE 用來刪除資源。

3.2.3.組合註解

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

@GetMapping 是一個組合註解@RequestMapping(method =RequestMethod.GET) 的一個快捷方式

//原來的:http://localhost:8080/add?a=1&b=2
    //RestFul風格:http://localhost:8080/add/a/b
    // @RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)
    @GetMapping("/add/{a}/{b}")
    public String test1(@PathVariable int a, @PathVariable int b, Model model) {//@PathVariable路徑變數
        int res = a + b;
        model.addAttribute("msg", "結果為:" + res);
        return "test";
    }

    // @RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.Post)
    @PostMapping("/add/{a}/{b}")
    public String test2(@PathVariable int a, @PathVariable int b, Model model) {//@PathVariable路徑變數
        int res = a + b;
        model.addAttribute("msg", "結果為:" + res);
        return "test";
    }

3.3.結果跳轉

3.3.1.ModelAndView

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

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

<!-- 檢視解析器 -->
<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 {

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

3.3.2.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);
  }

}

3.3.3.SpringMVC

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

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

@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";
  }
}

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

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

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

@Controller
public class ResultSpringMVC2 {
   @RequestMapping("/rsm2/t1")
   public String test1(){
       //轉發
       return "test";
  }

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

3.4.處理提交資料

3.4.1.提交的域名稱和處理方法的引數名一致

提交資料 : http://localhost:8080/hello?name=wyl

處理方法 :

@RequestMapping("/hello")
public String hello(String name){
   System.out.println(name);
   return "test";
}

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

提交資料 : http://localhost:8080/hello?username=wyl

處理方法 :

/**
* 請求引數繫結
* 請求的引數中如果有username屬性的話,
* SpringMVC會自動將引數傳入與方法引數列表對應的入參中
* @return
*/
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
   System.out.println(name);
   return "test";
}

3.4.3.提交的是一個物件

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

1、實體類

public class User {
   private int id;
   private String name;
   private int age;
   //構造
   //get/set
   //tostring()
}

2、提交資料 : http://localhost:8080/mvc04/user?name=wyl&id=1&age=18

3、處理方法

/**
*1、接收到的前端使用者傳遞引數,判斷引數的名字,假設名字直接在方法上,可以直接使用
*2、假設傳遞的是一個物件user,就會匹配user物件的欄位名;如果欄位一致則OK,否則匹配不到
*/
@RequestMapping("/user")
public String user(User user){
   System.out.println(user);
   return "hello";
}

3.3.4.獲取原生ServletAPI物件

提交資料 : http://localhost:8080/hello?username=wyl&password=123456

處理方法

/**
* 獲取原生ServletAPI物件
* 需要哪個物件,在方法入參處定義就好了
* @param request
* @param response
* @return
*/
    @RequestMapping("/hello")
    public String hello(HttpServletRequest request, HttpServletResponse response){
        System.out.println("獲取原生ServletAPI物件。。。");
        System.out.println("request:"+request);
        System.out.println("從request物件中獲取的使用者名稱:"+request.getParameter("username"));
        System.out.println("從request物件中獲取的密碼:"+request.getParameter("password"));
        System.out.println("response:"+response);
        System.out.println("session:"+request.getSession());
        System.out.println("application:"+request.getSession().getServletContext());
        //返回邏輯檢視名
        return "success";
    }

3.5.返回結果

3.5.1.ModelAndView

public class ControllerTest1 implements Controller {

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

3.5.2.ModelMap

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

3.5.3.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";
}

Model 只有寥寥幾個方法只適合用於儲存資料,簡化了新手對於Model物件的操作和理解;

ModelMap 繼承了 LinkedMap ,除了實現了自身的一些方法,同樣的繼承 LinkedMap 的方法和特性;

ModelAndView 可以在儲存資料的同時,可以進行設定返回的邏輯檢視,進行控制展示層的跳轉。

4.異常處理

4.1.異常處理的思路

系統中異常包括兩類: 預期異常和執行時異常 RuntimeException,前者通過捕獲異常從而獲取異常資訊,後者主要通過規範程式碼開發、測試通過手段減少執行時異常的發生。

系統的dao、service、controller 出現都通過throws Exception 向上丟擲,最後由springmvc 前端控制器交由異常處理器進行異常處理,如下圖:

image-20211031203211440

4.2.編寫異常類和錯誤頁面

/**
 * 自定義異常
 */
public class CustomException extends Exception {
  private String message;

  public CustomException(String message) {
    this.message = message;
  }

  public String getMessage() {
    return message;
  }
}
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!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>執行失敗</title>
</head>

<body>
    執行失敗!
    ${message }
</body>
</html>

4.3.自定義異常處理器

/**
 * 自定義異常處理器
 */
public class CustomExceptionResolver implements HandlerExceptionResolver {

  @Override
  public ModelAndView resolveException(
    HttpServletRequest request,
    HttpServletResponse response,
    Object handler,
    Exception ex
  ) {
    ex.printStackTrace();
    CustomException customException = null;

    //如果丟擲的是系統自定義異常則直接轉換
    if (ex instanceof CustomException) {
      customException = (CustomException) ex;
    } else {
      //如果丟擲的不是系統自定義異常則重新構造一個系統錯誤異常。
      customException = new CustomException("系統錯誤,請與系統管理 員聯絡!");
    }
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("message", customException.getMessage());
    modelAndView.setViewName("error");
    return modelAndView;
  }
}

4.4.

<!-- 配置自定義異常處理器 -->
 <bean id="handlerExceptionResolver" 
        class="com.wyl.exception.CustomExceptionResolver" />

5.json處理

ajax我經常用到,傳的資料是json資料,json資料又有物件,陣列。所有總結下springmvc獲取前端傳來的json資料方式

5.1.Controller接受JSON資料

作用:

@ResponseBody註解用於將Controller的方法返回的物件,通過springmvc提供的HttpMessageConverter介面轉換為指定格式的資料如:json,xml等,通過Response響應給客戶端。

本例子應用:

@ResponseBody註解實現將Controller方法返回java物件轉換為json響應給客戶端。

5.1.1.以RequestParam接收

前端傳來的是json資料不多時:[id:id],可以直接用@RequestParam來獲取值

@Autowired
private AccomodationService accomodationService;

@RequestMapping(value = "/update")
@ResponseBody
public String updateAttr(@RequestParam ("id") int id) {
    int res=accomodationService.deleteData(id);
    return "success";
}

5.1.2.以實體類方式接收

前端傳來的是一個json物件時:{【id,name】},可以用實體類直接進行自動繫結

@Autowired
private AccomodationService accomodationService;

    @RequestMapping(value = "/add")
    @ResponseBody
    public String addObj(@RequestBody Accomodation accomodation) {
        this.accomodationService.insert(accomodation);
        return "success";
    }

5.1.3.以Map接收

前端傳來的是一個json物件時{【id,name】},可以用Map來獲取

@Autowired
private AccomodationService accomodationService;

@RequestMapping(value = "/update")
@ResponseBody
public String updateAttr(@RequestBody Map<String, String> map) {
    if(map.containsKey("id"){
        Integer id = Integer.parseInt(map.get("id"));
    }
    if(map.containsKey("name"){
        String objname = map.get("name").toString();
    }
    // 操作 ...
    return "success";
}

5.1.4.以List接收

當前端傳來這樣一個json陣列[{id,name},{id,name},{id,name},...]時,用List接收

@Autowired
private AccomodationService accomodationService;

@RequestMapping(value = "/update")
@ResponseBody
//引數前面必須又@RequestBody
public String updateAttr(@RequestBody List<Accomodation> list) {
    for(Accomodation accomodation:list){
        System.out.println(accomodation.toString());
    }
    return "success";
}

ajax請求

var testList=[];
var user={};
user.id=1;
user.name='jack';
testList.push(user);
var user2={};
user2.id=2;
user2.name='tom';
testList.push(user2);
$.ajax({
    // headers必須新增,否則會報415錯誤
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    },
  type: 'POST',
  dataType: "json", //表示返回值型別,不必須
  data: JSON.stringify(testList),
  url: '/test/postList',
  success: function(){
      alert('success');
  }
  
});

需要注意點:1、引數是陣列型別

      2、傳入data時,轉換 JSON.stringify(testList)

      3、必須有headers:

{ 'Accept': 'application/json',
  'Content-Type': 'application/json'
}

5.2.Controller返回JSON資料

5.2.1.Jackson

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>

Controller

這裡我們需要兩個新東西,一個是@ResponseBody,一個是ObjectMapper物件,我們看下具體的用法

public class UserController {

   @RequestMapping("/json1")
   @ResponseBody
   public String json1() throws JsonProcessingException {
       //建立一個jackson的物件對映器,用來解析資料
       ObjectMapper mapper = new ObjectMapper();
       //建立一個物件
       User user = new User("wyl", 3, "男");
       //將我們的物件解析成為json格式
       String str = mapper.writeValueAsString(user);
       //由於@ResponseBody註解,這裡會將str轉成json格式返回;十分方便
       return str;
  }
}

在類上直接使用 @RestController ,這樣子,裡面所有的方法都只會返回 json 字串了,不用再每一個都新增@ResponseBody !我們在前後端分離開發中,一般都使用 @RestController ,十分便捷!

@RestController
public class UserController {

   //produces:指定響應體返回型別和編碼
   @RequestMapping(value = "/json1")
   public String json1() throws JsonProcessingException {
       //建立一個jackson的物件對映器,用來解析資料
       ObjectMapper mapper = new ObjectMapper();
       //建立一個物件
       User user = new User("wyl", 3, "男");
       //將我們的物件解析成為json格式
       String str = mapper.writeValueAsString(user);
       //由於@ResponseBody註解,這裡會將str轉成json格式返回;十分方便
       return str;
  }

}

抽取為工具類

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.text.SimpleDateFormat;

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();
       //ps:Jackson 預設是會把時間轉成timestamps形式(取消timestamps形式 , 自定義時間格式)
       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;
  }
}

demo

@RequestMapping("/jsonUnit")
public String json5() throws JsonProcessingException {
   Date date = new Date();
   String json = JsonUtils.getJson(date);
   return json;
}

5.2.2.FastJson

fastjson.jar是阿里開發的一款專門用於Java開發的包,可以方便的實現json物件與JavaBean物件的轉換,實現JavaBean物件與json字串的轉換,實現json物件與json字串的轉換。實現json的轉換方法很多,最後的實現結果都是一樣的。

fastjson 的 pom依賴!

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.60</version>
</dependency>

fastjson 三個主要的類:

JSONObject 代表 json 物件

  • JSONObject實現了Map介面, 猜想 JSONObject底層操作是由Map實現的。
  • JSONObject對應json物件,通過各種形式的get()方法可以獲取json物件中的資料,也可利用諸如size(),isEmpty()等方法獲取"鍵:值"對的個數和判斷是否為空。其本質是通過實現Map介面並呼叫介面中的方法完成的。

JSONArray 代表 json 物件陣列

  • 內部是有List介面中的方法來完成操作的。

JSON代表 JSONObject和JSONArray的轉化

  • JSON類原始碼分析與使用
  • 仔細觀察這些方法,主要是實現json物件,json物件陣列,javabean物件,json字串之間的相互轉化。

程式碼測試,我們新建一個FastJsonDemo 類

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.kuang.pojo.User;

import java.util.ArrayList;
import java.util.List;

public class FastJsonDemo {
   public static void main(String[] args) {
       //建立一個物件
       User user1 = new User("wyl1號", 3, "男");
       User user2 = new User("wyl2號", 3, "男");
       User user3 = new User("wyl3號", 3, "男");
       User user4 = new User("wyl4號", 3, "男");
       List<User> list = new ArrayList<User>();
       list.add(user1);
       list.add(user2);
       list.add(user3);
       list.add(user4);

       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);
  }
}

6.攔截器

6.1.過濾器與攔截器的區別

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

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

過濾器

  • servlet規範中的一部分,任何java web工程都可以使用
  • 在url-pattern中配置了/*之後,可以對所有要訪問的資源進行攔截

攔截器

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

  • 攔截器只會攔截訪問的控制器方法, 如果訪問的是jsp/html/css/image/js是不會進行攔截的

6.2.自定義攔截器

我們要想自定義攔截器,要求必須實現HandlerInterceptor 介面。

6.2.1. 編寫一個普通類實現HandlerInterceptor 介面

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {

   //在請求處理的方法之前執行
   //如果返回true執行下一個攔截器
   //如果返回false就不執行下一個攔截器
   public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
       System.out.println("------------處理前------------");
       return true;
  }

   //在請求處理方法執行之後執行
   public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
       System.out.println("------------處理後------------");
  }

   //在dispatcherServlet處理後執行,做清理工作.
   public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
       System.out.println("------------清理------------");
  }
}

6.1.2. 配置攔截器

 <!--關於攔截器的配置-->
<mvc:interceptors>
   <mvc:interceptor>
       <!--/** 包括路徑及其子路徑-->
       <!--/admin/* 攔截的是/admin/add等等這種 , /admin/add/user不會被攔截-->
       <!--/admin/** 攔截的是/admin/下的所有-->
       <mvc:mapping path="/**"/>
       <!--bean配置的就是攔截器-->
       <bean class="com.wyl.interceptor.MyInterceptor"/>
   </mvc:interceptor>
</mvc:interceptors>

6.2.3.控制器

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

//測試攔截器的控制器
@Controller
public class InterceptorController {

   @RequestMapping("/interceptor")
   @ResponseBody
   public String testFunction() {
       System.out.println("控制器中的方法執行了");
       return "hello";
  }
}

6.2.3.前端 index.jsp

<a href="${pageContext.request.contextPath}/interceptor">攔截器測試</a>

image-20211031205339521

6.3.攔截器的細節

6.3.1. 攔截器的放行

放行的含義是指,如果有下一個攔截器就執行下一個,如果該攔截器處於攔截器鏈的最後一個,則執行控制器中的方法。

6.3.2.攔截器中方法的說明

public interface HandlerInterceptor {
  /**
   * preHandle方法是controller方法執行前攔截的方法,按攔截器定義順序呼叫
   *    return true放行,執行下一個攔截器,如果沒有攔截器,執行controller中的方法進行業務處理。
   *    return false不放行,不會執行controller中的方法或呼叫其他的元件。
   */
  default boolean preHandle(
    HttpServletRequest request,
    HttpServletResponse response,
    Object handler
  )
    throws Exception {
    return true;
  }

  /**
   * postHandle是controller方法執行後執行的方法,在JSP檢視執行前,按攔截器定義逆序呼叫。
   *     可以使用request或者response跳轉到指定的頁面
   *     如果指定了跳轉的頁面,那麼controller方法跳轉的頁面將不會顯示。
   */
  default void postHandle(
    HttpServletRequest request,
    HttpServletResponse response,
    Object handler,
    @Nullable ModelAndView modelAndView
  )
    throws Exception {}

  /**
   * afterCompletion方法是在JSP執行後執行,按攔截器定義逆序呼叫
   *      注意:因為結果頁面已經返回完了,不能在該方法使用request或者response再跳轉頁面,但可以在該方法中進行一些資源清理的操作。
   */
  default void afterCompletion(
    HttpServletRequest request,
    HttpServletResponse response,
    Object handler,
    @Nullable Exception ex
  )
    throws Exception {}
}

6.3.3. 攔截器的作用路徑

作用路徑可以通過在配置檔案中配置。

<!-- 配置攔截器的作用範圍 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" /><!-- 用於指定對攔截的url -->
        <mvc:exclude-mapping path="" /><!-- 用於指定排除的url-->
        <bean id="handlerInterceptorDemo1" 
                class="com.wyl.interceptor.HandlerInterceptorDemo1">
         </bean>
    </mvc:interceptor>
</mvc:interceptors>

6.3.4. 多個攔截器的執行順序

多個攔截器是按照配置的順序決定的。

image-20211031210341808

6.3.4.1. 攔截器1 的程式碼

public class HandlerInterceptorDemo1 implements HandlerInterceptor {

  @Override
  public boolean preHandle(
    HttpServletRequest request,
    HttpServletResponse response,
    Object handler
  )
    throws Exception {
    System.out.println("攔截器 1: preHandle 攔截器攔截了");
    return true;
  }

  @Override
  public void postHandle(
    HttpServletRequest request,
    HttpServletResponse response,
    Object handler,
    ModelAndView modelAndView
  )
    throws Exception {
    System.out.println("攔截器 1: postHandle 方法執行了");
  }

  @Override
  public void afterCompletion(
    HttpServletRequest request,
    HttpServletResponse response,
    Object handler,
    Exception ex
  )
    throws Exception {
    System.out.println("攔截器 1: afterCompletion 方法執行了");
  }
}

6.3.4.2. 攔截器2 的程式碼:

public class HandlerInterceptorDemo2 implements HandlerInterceptor {

  @Override
  public boolean preHandle(
    HttpServletRequest request,
    HttpServletResponse response,
    Object handler
  )
    throws Exception {
    System.out.println("攔截器2: preHandle 攔截器攔截了");
    return true;
  }

  @Override
  public void postHandle(
    HttpServletRequest request,
    HttpServletResponse response,
    Object handler,
    ModelAndView modelAndView
  )
    throws Exception {
    System.out.println("攔截器2: postHandle 方法執行了");
  }

  @Override
  public void afterCompletion(
    HttpServletRequest request,
    HttpServletResponse response,
    Object handler,
    Exception ex
  )
    throws Exception {
    System.out.println("攔截器2: afterCompletion 方法執行了");
  }
}

6.3.4.3.配置攔截器的作用範圍

  <mvc:interceptor>
    <mvc:mapping *path*="/**" />*<!--* *用於指定對攔截的**url -->*
    <bean *id*="handlerInterceptorDemo1" *class*="com.wyl.interceptor.HandlerInterceptorDemo1"></bean>
  </mvc:interceptor>
  <mvc:interceptor>
    <mvc:mapping *path*="/**" />   
      <bean *id*="handlerInterceptorDemo2" *class*="com.wyl.interceptor.HandlerInterceptorDemo2"></bean>
  </mvc:interceptor>

image-20211031211536627

6.3.4.4.中斷流程測試

攔截器2 返回false

image-20211031211651464

7.上傳檔案

image-20211031200658387

7.1.匯入檔案上傳的jar包

<!--檔案上傳
commons-io 不屬於檔案上傳元件的開發jar 檔案,但Commons-fileupload 元件從1.1 版本開始,它 
工作時需要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>

7.2. 編寫檔案上傳的jsp 頁面

<h3>檔案上傳</h3>
<form action="user/fileupload" method="post" enctype="multipart/form-data">
    選擇檔案:<input type="file" name="upload" /><br />
    <input type="submit" value="上傳檔案" />
</form>
  1. 至少有一個檔案選擇域

  2. 以POST方式提交

  3. 表單的enctype必須為 multipart/form-data

7.3.控制器(非SpringMVC版)

package com.wyl.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;

@Controller
public class FileController {
   //@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";
  }
}

7.4.SpringMVC配置資訊

SpringMVC框架提供了MultipartFile物件,該物件表示上傳的檔案,要求變數名稱必須和表單file標籤的name屬性名稱相同。

<!-- 配置檔案解析器物件,要求id名稱必須是multipartResolver -->
<bean id="multipartResolver" 
       class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 設定上傳檔案的最大尺寸為5MB -->
    <property name="maxUploadSize">
        <value>5242880</value>
    </property>
    <!-- property name="maxUploadSize" value="10485760"/ -->
</bean>

7.5.控制器程式碼(SpringMVC版)

/**
* SpringMVC方式的檔案上傳
*/
@RequestMapping(value="/fileupload2")
public String fileupload2(HttpServletRequest request,MultipartFile upload) throws Exception {
System.out.println("SpringMVC方式的檔案上傳...");
    // 先獲取到要上傳的檔案目錄
    String path = request.getSession().getServletContext().getRealPath("/uploads");
    // 建立File物件,一會向該路徑下上傳檔案
    File file = new File(path);
    // 判斷路徑是否存在,如果不存在,建立該路徑
    if(!file.exists()) {
        file.mkdirs();
    }

    // 獲取到上傳檔案的名稱
    String filename = upload.getOriginalFilename();
    String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
    // 把檔案的名稱唯一化
    filename = uuid+"_"+filename;
    // 上傳檔案
    upload.transferTo(new File(file,filename));
    return "success";
}

7.6.跨伺服器

準備兩個tomcat 伺服器,其中一個作為檔案伺服器,一個作為應用伺服器。

image-20211031202525522

7.6.1.在檔案伺服器tomcat的web.xml中修改tomcat配置,允許讀寫操作。

image-20211031202713533

7.6.2.匯入包

<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-core</artifactId>
    <version>1.18.1</version>
</dependency>
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-client</artifactId>
    <version>1.18.1</version>
</dependency>

7.6.3.控制器

**
* SpringMVC跨伺服器方式的檔案上傳
*/
@RequestMapping(value="/fileupload3")
public String fileupload3(MultipartFile upload) throws Exception {
    System.out.println("SpringMVC跨伺服器方式的檔案上傳...");
    // 定義圖片伺服器的請求路徑
    String path = "http://localhost:9090/day02_springmvc5_02image/uploads/";
    // 獲取到上傳檔案的名稱
    String filename = upload.getOriginalFilename();
    String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
    // 把檔案的名稱唯一化
    filename = uuid+"_"+filename;
    
    // 向圖片伺服器上傳檔案
    // 建立客戶端物件(sun公司提供的jersey 包)
    Client client = Client.create();
    // 連線圖片伺服器
    WebResource webResource = client.resource(path+filename);
    // 上傳檔案 //String result = resource.put(String.class,uploadFile.getBytes());  
    webResource.put(upload.getBytes());
    return "success";
}

7.7.下載

檔案下載步驟:

1、設定 response 響應頭

2、讀取檔案 -- InputStream

3、寫出檔案 -- OutputStream

4、執行操作

5、關閉流 (先開後關)

程式碼實現:

@RequestMapping(value="/download")
public String downloads(HttpServletResponse response ,HttpServletRequest request) throws Exception{
   //要下載的圖片地址
   String  path = request.getServletContext().getRealPath("/upload");
   String  fileName = "基礎語法.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;
}

前端

<a href="/download">點選下載</a>

測試,檔案下載OK,大家可以和我們之前學習的JavaWeb原生的方式對比一下,就可以知道這個便捷多了!

攔截器及檔案操作在我們開發中十分重要,一定要學會使用!

8.整合ssm

8.1.前期準備

環境:

  • IDEA
  • MySQL 8.0.18
  • Tomcat 9
  • Maven 3.6

建立資料庫:

CREATE DATABASE `wyl`;

USE `wyl`;

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
  `gender` int(255) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

image-20211026144016031

8.1.專案搭建

8.1.1.建立專案

8.1.2.匯入相關的pom依賴

 <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!-- 依賴:junit,資料庫驅動,連線池,servlet,jsp,mybatis,mybatis-spring,spring -->
    <!--資料庫驅動-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.18</version>
    </dependency>
    <!-- 資料庫連線池 -->
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
    </dependency>
    <!--Servlet - JSP -->
    <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>
    <!-- mybatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.5</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.6</version>
    </dependency>
    <!--spring-->
    <!--Spring-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.8.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.8.RELEASE</version>
    </dependency>
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>RELEASE</version>
          <scope>compile</scope>
      </dependency>

  </dependencies>

8.1.2.靜態資源匯出

image-20211026144643571

8.1.3.專案結構

image-20211026144812649

8.1.4.配置

8.1.4.1.mybatis

database.properties(資料庫配置檔案)

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://127.0.0.1:3306/wyl?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
jdbc.username=root
jdbc.password=wyl190204-

mybatis-config.xml(核心配置檔案)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--轉為駝峰-->
<!--    <settings>-->
<!--        <setting name="mapUnderscoreToCamelCase" value="true"/>-->
<!--    </settings>-->
    <!--別名-->
    <typeAliases>
        <package name="com.wyl.pojo"/>
    </typeAliases>
<!--對映器-->
    <mappers>
        <mapper class="com.wyl.dao.UsersMapper"></mapper>
    </mappers>
</configuration>

8.1.4.2.spring

1、配置Spring整合MyBatis,我們這裡資料來源使用c3p0連線池;

2、我們去編寫Spring整合Mybatis的相關的配置檔案;spring-dao.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"
       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">

    <!-- 配置整合mybatis -->
    <!-- 1.關聯資料庫檔案 -->
    <context:property-placeholder location="classpath:database.properties"/>

    <!-- 2.資料庫連線池 -->
    <!--資料庫連線池
       dbcp 半自動化操作 不能自動連線
       c3p0 自動化操作(自動的載入配置檔案 並且設定到物件裡面)
   -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 配置連線池屬性 -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- c3p0連線池的私有屬性 -->
        <property name="maxPoolSize" value="30"/>
        <property name="minPoolSize" value="10"/>
        <!-- 關閉連線後不自動commit -->
        <property name="autoCommitOnClose" value="false"/>
        <!-- 獲取連線超時時間 -->
        <property name="checkoutTimeout" value="10000"/>
        <!-- 當獲取連線失敗重試次數 -->
        <property name="acquireRetryAttempts" value="2"/>
    </bean>

    <!-- 3.配置SqlSessionFactory物件 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入資料庫連線池 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置MyBaties全域性配置檔案:mybatis-config.xml -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!-- 4.配置掃描Dao介面包,動態實現Dao介面注入到spring容器中 -->
    <!--解釋 :https://www.cnblogs.com/jpfss/p/7799806.html-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 注入sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- 給出需要掃描Dao介面包 -->
        <property name="basePackage" value="com.wyl.dao"/>
    </bean>
</beans>

spring-service.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"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context.xsd">

   <!-- 掃描service相關的bean -->
   <context:component-scan base-package="com.kuang.service" />

   <!--BookServiceImpl注入到IOC容器中-->
   <bean id="BookServiceImpl" class="com.kuang.service.BookServiceImpl">
       <property name="bookMapper" ref="bookMapper"/>
   </bean>

   <!-- 配置事務管理器 -->
   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <!-- 注入資料庫連線池 -->
       <property name="dataSource" ref="dataSource" />
   </bean>

</beans>

8.1.4.3.spring-mvc

web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<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">
  <display-name>Archetype Created Web Application</display-name>
  <!--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:applicationContext.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>
  <filter>
    <filter-name>filter</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>filter</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">
    <!--掃描註解所在包-->
    <context:component-scan base-package="com.wyl.controller"/>
    <!-- 讓Spring MVC不處理靜態資源 -->
    <mvc:default-servlet-handler/>
    <!--
    支援mvc註解驅動
        在spring中一般採用@RequestMapping註解來完成對映關係
        要想使@RequestMapping註解生效
        必須向上下文中註冊DefaultAnnotationHandlerMapping
        和一個AnnotationMethodHandlerAdapter例項
        這兩個例項分別在類級別和方法級別處理。
        而annotation-driven配置幫助我們自動完成上述兩個例項的注入。
     -->
    <mvc:annotation-driven/>
    <!--配置JSP檢視解析器-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置檔案所在目錄-->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <!--配置檔案的字尾名-->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

8.1.1.4.整合

applicationContext.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">
    <import resource="spring-dao.xml"/>
    <import resource="spring-service.xml"/>
    <import resource="springmvc-servlet.xml"/>
</beans>

8.2.程式碼實現

8.2.1.pojo

package com.wyl.pojo;

import com.oracle.webservices.internal.api.databinding.DatabindingMode;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @建立人 王延領
 * @建立時間 2021/10/25
 * 描述
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
    private int id;
    private String username;
    private String password;
    private String email;
    private int gender;
}

8.2.2.dao

UsersMapper.java

import com.wyl.pojo.Users;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @建立人 王延領
 * @建立時間 2021/10/25
 * 描述
 **/
public interface UsersMapper {
    //增
    int addUser(Users user);
    //刪
    int delUser(int id);
    //改
    int updateUser(Users user);
    //查
    Users queryUser(int id);
    //集合
    List<Users> queryUsers();
    //查
    List<Users> queryUserByName(String name);
}

UsersMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.wyl.dao.UsersMapper">
    <insert id="addUser" parameterType="Users">
        insert into users(id,username,password,email,gender) values (#{id},#{username},#{password},#{email},#{gender});
    </insert>
    <delete id="delUser" parameterType="int">
        delete from users where id=#{id}
    </delete>
    <update id="updateUser" parameterType="Users">
        update users set username=#{username},password=#{password},email=#{email},gender=#{gender} where id=#{id}
    </update>
    <select id="queryUser" resultType="Users">
        select * from users where id=#{id}
    </select>
<select id="queryUsers"  resultType="Users">
    SELECT * FROM users
</select>
    <select id="queryUserByName" resultType="Users">
        select * from users where username like '%${name}%'
    </select>

</mapper>

8.2.3.service

UsersService

package com.wyl.service;

import com.wyl.pojo.Users;

import java.util.List;

/**
 * @建立人 王延領
 * @建立時間 2021/10/25
 * 描述
 **/
public interface UsersService {
    //增
    int addUser(Users user);
    //刪
    int delUser(int id);
    //改
    int updateUser(Users user);
    //查
    Users queryUser(int id);
    //集合
    List<Users> queryUsers();
    //查
    List<Users> queryUserByName(String name);
}

UsersServiceImpl

package com.wyl.service.impl;

import com.wyl.dao.UsersMapper;
import com.wyl.pojo.Users;
import com.wyl.service.UsersService;
import org.omg.CORBA.PUBLIC_MEMBER;

import java.util.List;

/**
* @建立人 王延領
* @建立時間 2021/10/25
* 描述
**/
public class UsersServiceImpl implements UsersService {
   private UsersMapper userMapper;

   public void setUserMapper(UsersMapper userMapper) {
       this.userMapper = userMapper;
   }

   @Override
   public int addUser(Users user) {
       int i = userMapper.addUser(user);
       return i;
   }

   @Override
   public int delUser(int id) {
       int i = userMapper.delUser(id);
       return i;
   }

   @Override
   public int updateUser(Users user) {
       int i = userMapper.updateUser(user);
       return i;
   }

   @Override
   public Users queryUser(int id) {
       return userMapper.queryUser(id);
   }

   @Override
   public List<Users> queryUsers() {
       return userMapper.queryUsers();
   }

   @Override
   public List<Users> queryUserByName(String name)
   {
   return userMapper.queryUserByName(name);
   }
}

8.2.4.controller

package com.wyl.controller;

import com.wyl.pojo.Users;
import com.wyl.service.UsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Comparator;
import java.util.List;

/**
 * @建立人 王延領
 * @建立時間 2021/10/25
 * 描述
 **/
@Controller
@RequestMapping("/user")
public class UsersController {
    @Autowired
    @Qualifier("UsersServiceImpl")
    private UsersService userService;

    @RequestMapping("/userList")
    public String userList(Model model) {
        List<Users> users = userService.queryUsers();
        users.sort(Comparator.comparingInt(Users::getId));
        model.addAttribute("list", users);
        return "userList";
    }
    @RequestMapping("/toAddUser")
    public String toAddUser() {
        return "addUser";
    }



    @PostMapping("/addUser")
    public String addUser(Users user) {
        System.out.println("addUser =>" + user);
        int i = userService.addUser(user);
        if (i > 0) {
            return "redirect:/user/userList";
        }
        return "error";
    }

    @RequestMapping("/toUpdateUser")
    public String toUpdateUser(int id, Model model) {
        Users user = userService.queryUser(id);
        model.addAttribute("user", user);
        return "updateUser";
    }

    @PostMapping("/updateUser")
    public String updateUser(Users user) {
        System.out.println("updateUser =>" + user);
        int i = userService.updateUser(user);
        if (i > 0) {
            return "redirect:/user/userList";
        }
        return "error";
    }

    @GetMapping("/deleteUser")
    public String deleteUser(int id) {
        System.out.println("deleteUser =>" + id);
        int i = userService.delUser(id);
        if (i > 0) {
            return "redirect:/user/userList";
        }
        return "error";
    }
    /**
     * 查詢全部書籍,並且返回到一個書籍展示頁面
     */
    @RequestMapping("/byName")
    public String ByName(String name, Model model) {
        List<Users> users = userService.queryUserByName(name);
        if (users == null || users.size() == 0) {
            model.addAttribute("error", "未查詢到使用者");
            return "userList";
        }

        System.out.println("users =>" + users);

        // 排序
        users.sort(Comparator.comparingInt(Users::getId));

        model.addAttribute("list", users);
        return "userList";
    }

}

8.3.page

userList.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: 17144
  Date: 2021/10/25
  Time: 16:04
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>展示頁面</title>
  <!-- 引入 Bootstrap -->
  <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
  <div class="row clearfix">
    <div class="col-md-12">
      <div class="page-header">
        <h1>
          <small>使用者列表 ------ 顯示所有使用者</small>
        </h1>
      </div>
    </div>

    <div class="row">
      <div class="col-md-4">
        <a class="btn btn-primary" href="${pageContext.request.contextPath}/user/toAddUser">新增使用者</a>
      </div>
      <div class="col-md-2"></div>
      <div class="col-md-6 right">
        <form class="navbar-form navbar-right" method="get" action="${pageContext.request.contextPath}/user/byName">
          <div class="form-group">
            <input type="text" class="form-control" placeholder="Search" name="name">
          </div>
          <button type="submit" class="btn btn-default">Submit</button>
        </form>
      </div>
    </div>

  </div>
  <div class="row clearfix">
    <div class="col-md-12">
      <table class="table table-hover table-striped">
        <thead>
        <tr>
          <th>編號</th>
          <th>名稱</th>
          <th>密碼</th>
          <th>郵箱</th>
          <th>操作</th>
        </tr>
        </thead>
        <tbody>
        <c:forEach var="user" items="${list}">
          <tr>
            <td>${user.id}</td>
            <td>${user.username}</td>
            <td>${user.password}</td>
            <td>${user.email}</td>
            <td><a href="${pageContext.request.contextPath}/user/toUpdateUser?id=${user.id}">修改</a> | <a href="${pageContext.request.contextPath}/user/deleteUser?id=${user.id}">刪除</a></td>
          </tr>
        </c:forEach>
        </tbody>
      </table>
    </div>
  </div>

  <div>${error}</div>
</div>
</body>
</html>

aaUser.jsp

<%--
  Created by IntelliJ IDEA.
  User: 17144
  Date: 2021/10/25
  Time: 16:09
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>新增頁面</title>
  <!-- 引入 Bootstrap -->
  <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
  <div class="row clearfix">
    <div class="col-md-12">
      <div class="page-header">
        <h1>
          <small>新增使用者</small>
        </h1>
      </div>
    </div>
  </div>
  <form action="${pageContext.request.contextPath}/user/addUser" method="post">
    姓名<input type="text" name="username" required><br>
    密碼<input type="text" name="password" required><br>
    郵箱<input type="text" name="email"><br>
    年齡<input type="text" name="gender"><br>
    <input type="submit" value="新增">
  </form>
</div>
</body>
</html>

updateUser.jsp

<%--
  Created by IntelliJ IDEA.
  User: 17144
  Date: 2021/10/25
  Time: 16:12
  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>
<div class="container">

  <div class="row clearfix">
    <div class="col-md-12">
      <div class="page-header">
        <h1>
          <small>修改使用者</small>
        </h1>
      </div>
    </div>

  </div>
  <form action="${pageContext.request.contextPath}/user/updateUser" method="post">
    <input type="hidden" name="id" value="${user.id}">
    名稱<input type="text" name="username" value="${user.username}"><br>
    密碼<input type="text" name="password" value="${user.password}"><br>
    郵箱<input type="text" name="email"  value="${user.email}"><br>
    年齡<input type="text" name="gender"  value="${user.gender}"><br>
    <input type="submit" value="修改">
  </form>
</div>
</body>
</html>

error.jsp

<%--
  Created by IntelliJ IDEA.
  User: 17144
  Date: 2021/10/25
  Time: 16:04
  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>ERROR頁面</h1>
</body>
</html>

image-20211026152545180