SpringMVC學習筆記

2020-08-12 20:06:00

springMVC舊版文件地址:https://docs.spring.io/spring/docs/4.3.24.RELEASE/spring-framework-reference/html/overview.html#overview-distribution-zip
 

1、回顧MVC

1.1 什麼是MVC

  • MVC是模型(Model)、檢視(View)、控制器(Controller)的簡寫,是一種軟體設計規範

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

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

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

  • 主要作用:降低檢視與業務邏輯間的雙向耦合

  • MVC不是一種設計模式,而是一種架構模式,不同的MVC存在差異

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-COH3shYY-1597068814893)(E:/Typora/image-20200602150623114.png)]
 
MVC框架要做的事情:

  • 將url對映到java類或java類的方法
  • 封裝使用者提交的數據
  • 處理請求 -> 呼叫相關的業務處理 -> 封裝相應數據
  • 渲染響應的.jsp、html等表示層數據
     

常見的伺服器端MVC框架:Struts、Spring MVC、ASP.NET MVC、Zend Framework、JSF

常見的前端MVC框架:vue、angularjs、react、backbone

由MVC演化出的另外一些模式:MVP、MVVM等
 

1.2 Model1時代

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sP4KjbPQ-1597068814900)(E:/Typora/image-20200602151212445.png)]

  • 優點:架構簡單,適合小型專案開發
  • 缺點:JSP職責不單一,職責過重不便於維護
     

1.3 Mode2時代

  • 在Model2中,一個專案是分爲三部分的:檢視、控制、模型

  • 執行過程:

    1. 使用者發請求
    2. Servlet接收請求數據,並呼叫對應的業務邏輯方法
    3. 業務處理完畢,返回更新後的數據給servlet
    4. servlet轉向JSP,由JSP來渲染介面
    5. 響應給前端更新後的頁面

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M1zRTBME-1597068814905)(E:/Typora/image-20200602151949341.png)]

  • 職責分析:

    • Controller:取得表單數據、呼叫業務邏輯、轉向指定的頁面

    • Model:業務邏輯、儲存數據的狀態

    • View:顯示頁面

  • 優點:不僅提高了程式碼的複用率和專案的擴充套件性,而且大大降低了專案的維護成本
     

面試題:自己所做的專案架構是設計好的還是演進的?

演進的!!!任何一個專案的發展都是從最初的架構簡單的小專案逐漸演化,更改架構

Alibaba演進過程:最初是一個小型的PHP專案,隨着使用者量增多,PHP無法承載大的併發量,改爲Java架構。當時因爲給IBM、Oracle支付費用太多,一個叫王堅的博士提出了去IOE,對數據庫的使用又回到了MySql,但是Ali對MySql又做了很多優化,後來發展出了AliSql和AliRedis等。

 

2、回顧Servlet

  1. 新建一個空的Maven工程,新增Web框架支援,匯入依賴
<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>
  1. 編寫一個Servlet類實現doGet和doPost方法
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getParameter("method");
        if(method.equals("add")){
            req.getSession().setAttribute("msg","執行了add方法");
        }
        if(method.equals("delete")){
            req.getSession().setAttribute("msg","執行了delete方法");
        }

        req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  1. 編寫跳轉介面jsp
  2. 在web.xml中註冊servlet
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.Nana.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 設定Tomcat,啓動測試
  • localhost:8080/hello?method=add
  • localhost:8080/hello?method=delete
     

3、SpringMVC概述

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

  • 特點:

    • 輕量級、簡單易學
    • 高效、是基於請求響應的MVC框架
    • 與Spring相容性好,無縫結合
    • 約定大於設定
    • 功能強大,提供RESTful、數據驗證、格式化、在地化、主題等功能
    • 簡潔靈活
  • Spring的web框架圍繞DispatcherServlet[排程Servlet]設計

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

  • 從Spring2.5開始,使用Java5或者以上版本的使用者可以採用基於註解形式進行開發,十分簡潔
     

4、HelloSpringMVC

設定版:理解原理使用

  1. 新建Moudle,新增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">

    <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>
        
        <!--啓動級別爲1-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <!--匹配所有請求-->
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  1. 編寫SpringMVC的組態檔spring-mvc-servlet.xml

    注意:官方名稱要求:[servletname]-servlet

    <?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">
        
    </beans>
    
  2. 在servlet.xml中新增處理對映器

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
  1. 在servlet.xml中新增處理器適配器
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
  1. 在servlet.xml中新增檢視解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>
  1. 編寫操作業務Controller,實現Controller介面或者增加註 加注解
public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView();

        mv.addObject("msg", "HelloSpringMVC");
        mv.setViewName("hello");

        return mv;
    }
}
  1. 在servlet.xml中將自己的類交給SpringIOC容器,註冊bean
<bean id="/hello" class="com.Nana.controller.HelloController"/>
  1. 編寫跳轉介面hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

${msg}

</body>
</html>
  1. 設定Tomcat,啓動測試,頁面會輸出HelloSpringMVC
    -localhost:8080/hello
     

注意:如果是先建立空的maven專案再新增web支援,可能會出現404錯誤

排查步驟:

  1. 檢視控制檯輸出,看是否缺少jar包
  2. 如果jar包存在顯示無法輸出,就在專案發佈中新增lib依賴

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bt1WKx8Z-1597068814912)(E:/Typora/image-20200603153056021.png)]

  1. 重新啓動Tomcat即可解決

如果還有其他的問題,請記得沒有什麼是重建一個專案解決不了的
 

註解版:實際開發中使用

  1. 建立空的maven專案,新增web框架支援,匯入lib
  2. 編寫web框架,作用是系結springmvc-servlet.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">

    <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>

</web-app>
  1. 編寫springmvc-servlet.xml
<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">

    <!--掃描指定包下的元件,使得@COntroller註解生效,由IOC容器統一管理-->
    <context:component-scan base-package="com.Nana.controller"/>
    
    <!--使得SpringMVC不處理靜態資源,比如.css、.html、.js、.mp3、.mp4等-->
    <mvc:default-servlet-handler/>
    
    <!--要使得@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>
  1. 編寫跳轉頁面hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

${msg}

</body>
</html>
  1. 寫業務實現類
/*代表這個類會被Spring接管,被這個註解的類中的所有方法,如果返回型別是String,並且有具體頁面可以跳轉,就會被檢視解析器解析*/
@Controller
public class HelloController {

    /*存取對映地址*/
    @RequestMapping("/test")
    public String hello(Model model){
        /*向模型中新增屬性與值,可以在jsp頁面中取出*/
        model.addAttribute("msg", "你跳轉成功啦");
        return "hello";
    }
}

 

5、SpringMVC執行原理

SpringMVC原理圖:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NUomkZIp-1597068814916)(E:/Typora/image-20200603171917896.png)]
 
SpringMVC流程圖:實線表示SpringMVC框架提供的技術,無須開發者實現,虛線表示需要開發者實現

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IgC7Uqr4-1597068814918)(E:/Typora/image-20200603172042238.png)]

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

假設請求的url爲http://localhost:8080/SpringMVC/hello

此url可拆分爲三部分:

  • http://localhost:8080是伺服器的域名
  • SpringMVC是部署在伺服器上的web站點
  • hello表示控制器

綜上,此url表示請求位於伺服器localhost:8080上的SpringMVC站點的hello控制器

  1. HandlerMapping爲處理器對映,由DispatcherServlet呼叫。使用HandlerMapping.HandlerMapping會根據請求url查詢Handler

  2. HandlerExecution表示具體的Handler,主要作用是根據url查詢控制器

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

  4. HandlerAdapter表示處理器適配器,作用是按照特定的規則去執行Handler

  5. Handler讓具體的Controller執行

  6. Controller將具體的執行資訊返回給HandlerAdapter,如ModelAndView

  7. HandlerAdapter將試圖邏輯名或模型傳遞給DispatcherServlet

  8. DispatcherServlet呼叫檢視解析器(ViewResolver)來解析HandlerAdapter傳遞的邏輯檢視名

  9. 檢視解析器將解析的邏輯檢視名傳給DispaptcherServletServlelt

  10. DispatcherServlet根據檢視解析器解析的檢視結果,呼叫具體的檢視

  11. 最終檢視呈現給使用者

 

6、@Controller和@RequestMapping

@Controller

  • 作用:負責解析使用者的請求並將其轉換爲一個模型

  • 實現:可以通過介面定義或註解定義

    • 介面中只有一個方法

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mTv5MLSg-1597068814922)(E:/Typora/image-20200603190043085.png)]

@RequestMapping

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

7、RestFul風格

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

  • 功能:

    • 資源:網際網路所有的事物都可以被抽象爲資源
    • 資源操作:使用POST、DELETE、PUT、GET方法對資源進行操作
  • 與傳統方式操作資源對比:

    • 傳統方式:通過不同的參數來實現不同的效果,方法單一

      • http://127.0.0.1/item/queryItem.action?id=1 查詢GET
      • http://127.0.0.1/item/saveItem.action?id=1 新增POST
      • http://127.0.0.1/item/updateItem.action?id=1 更新POST
      • http://127.0.0.1/item/deleteItem.action?id=1 刪除DELETE
    • RestFul方式:可以通過不同的請求方式來實現不同的效果,比如:地址相同功能不同

      • http://127.0.0.1/item/1 查詢GET
      • http://127.0.0.1/item/ 新增POST
      • http://127.0.0.1/item/1 更新POST
      • http://127.0.0.1/item/1 刪除DELETE
         

傳統方式從前端獲得參數:

@Controller
public class RestFulController {
    @RequestMapping("/h1")
    public String test(int a, int b, Model model){
        int ans=a+b;
        model.addAttribute("msg","a+b的和爲:"+ans);
        return "hello";
    }
}

存取:http://localhost:8080/h1?a=1&b=2

RestFul風格獲得參數:實參形參名稱相同,如果不相同可以使用@PathVariable("")給形參取別名

@Controller
public class RestFulController {
    @RequestMapping("/h1/{a}/{b}")
    public String test(@PathVariable int a, @PathVariable int b, Model model){
        int ans=a+b;
        model.addAttribute("msg","a+b的和爲:"+ans);
        return "hello";
    }
}

存取:http://localhost:8080/h1/1/2
 

RestFul相同地址不同功能的三種實現方式:

@RequestMapping(value="/h1/{a}/{b}" method=RequestMethod.GET)

@RequestMapping(path="/h1/{a}/{b}" method=RequestMethod.GET)

@GetMapping("/h1/{a}/{b}")

 

8、重定向和轉發

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>
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    ModelAndView mv = new ModelAndView();

    mv.addObject("msg", "HelloSpringMVC");
    mv.setViewName("hello");

    return mv;
}

ServletAPI:通過設定ServletAPI,不需要檢視解析器

@Controller
public class ModelTest1 {
    @RequestMapping("/h1")
    public void test1(HttpServletRequest req, HttpServletResponse resp){
        resp.getWriter().println("test");
    }
    
    @RequestMapping("/h2")
    public void test2(HttpServletRequest req, HttpServletResponse resp){
        resp.sendRedirect("/WEB-INF/jsp/hello.jsp");
    }
    
    @RequestMapping("/h3")
    public void test3(HttpServletRequest req, HttpServletResponse resp){
        req.setAttribute("msg","test")
        req.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(req,resp);
    }
}

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

本質是重新請求一個地方,注意路徑問題

@Controller
public class ResultSpringMVC{
    @RequestMapping("/h1")
    public String test1(){
        return "/WEB-INF/jsp/hello.jsp"
    }
    
    @RequestMapping("/h2")
    public String test2(){
        return "forward:/WEB-INF/jsp/hello.jsp"
    }
    
    @RequestMapping("/h3")
    public String test3(){
        return "redirect:/WEB-INF/jsp/hello.jsp"
    }
}

 

9、接收請求參數及數據處理

1. 提交數據和處理方法參數名一致

提交數據:http://localhost:8080/test?hello=test

處理方法:

@Controller
public class UserController {
    @RequestMapping("/test")
    public void test(String hello){
        System.out.println(hello);
    }
}

後臺輸出:test
 

2. 提交數據和處理方法參數名不一致

提交數據:http://localhost:8080/test?userHello=test

處理方法:

@Controller
public class UserController {
    @RequestMapping("/test")
    public void test(@RequestParam("userHello")String hello){
        System.out.println(hello);
    }
}

後臺輸出:test
 

3.提交的是一個物件

提交數據:http://localhost:8080/user?id=1&name=%E6%B5%B7%E7%87%95%E9%85%B1&age=21

處理方法:

@Controller
public class UserController {
    @RequestMapping("/user")
    public void test(User user){
        System.out.println(user);
    }
}

後臺輸出:User{id=1, name=‘海燕醬’, age=21}

注意:

  1. 可以直接接收物件,但是參數名要和物件屬性名一直,如果不一致接收到的就爲null
  2. 實體類中一定要寫get和set方法,還有有參和無參構造方法,否則接收到的也爲null
     

10、數據顯示到前端

1.通過ModelAndView

public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    ModelAndView mv = new ModelAndView();

    mv.addObject("msg", "HelloSpringMVC");
    mv.setViewName("hello");

    return mv;
}

 

2.通過Model

@RequestMapping("/test")
public String hello(Model model){
    model.addAttribute("msg", "HelloSpringMVC");
    return "hello";
}

 

3.通過ModelMap

@RequestMapping("/test")
public String hello(ModelMap model){
    model.addAttribute("msg", "HelloSpringMVC");
    return "hello";
}

 

三種方式對比:

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

11、亂碼問題

測試程式碼:hello頁面只負責取msg數據

<form action="/e1" method="post">
    <input type="text" name="name">
    <input type="submit">
</form>
@Controller
public class EncodingController {
    @RequestMapping("/e1")
    public String test(String name, Model model){
        model.addAttribute("msg",name);
        return "hello";
    }
}

測試數據:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-90kuJnhp-1597068814924)(E:/Typora/image-20200604103852614.png)]

測試結果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mUVPOD7n-1597068814926)(E:/Typora/image-20200604103917935.png)]
 
解決方式一:自己寫編碼過濾器

public class EncodingFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException { }
    
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        filterChain.doFilter(servletRequest,servletResponse);
    }
    
    public void destroy() { }
}
<filter>
    <filter-name>encoding</filter-name>
    <filter-class>com.Nana.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-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>

缺點:某些極端情況下,這個過濾器對get支援不好
 

解決方式三:使用別人寫好的過濾器

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

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

    package com.Nana.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;
      }
    }
    
  3. 在web.xml中設定這個過濾器
     

/和/*區別:

  • /會匹配到/springmvc這樣的路徑型url,不會匹配到模式爲*.jsp這樣的後綴型url

  • 會匹配所有的url:路徑型的和後綴型的url(包括/springmvc,.jsp,.js和*.html等)

  • 兩個符號要慎重選用
     

12、JSON

JSON(JavaScript Object Notation,JS物件標記)是一種輕量級的數據交換格式,目前使用特別廣泛

特點:

  • 採用完全獨立於程式語言的文字格式來儲存和表示數據

  • 簡潔和清晰的層次結構使得JSON成爲理想的數據交換語言

  • 易於人閱讀和編寫,同時也易於機器解析和生成,並有效地提升網路傳輸效率

數據型別:

  • 在JavaScript語言中,一切都是物件。任何JavaScript支援的型別都可以通過JSON來表示
  • 字串、數位、物件、陣列等

要求和語法格式:

  • 物件表示爲鍵值對,數據由逗號分隔
  • 花括號儲存物件
  • 方括號儲存陣列
{"name":"海燕醬"}
{"age":"21"}

和JavaScript物件關係:

  • JSON是JavaScript物件的字串表示,使用文字表示一個JS物件的資訊,本質是一個字串
var obj = {a: 'Hello', b: 'World'}; //這是一個物件,注意鍵名也是可以使用引號包裹的
var json = '{"a": "Hello", "b": "World"}'; //這是一個 JSON 字串,本質是一個字串
  • JavaScript物件和JSON物件相互轉換測試程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--script標籤沒有自閉合-->
    <script type="text/javascript">
        //編寫一個JavaScript物件
        var user = {
            name:"海燕醬",
            age:3,
            sex:"女"
        };

        //將js物件轉換爲json物件
        var json = JSON.stringify(user);
        console.log(json);

        //將json物件轉換爲js物件
        var obj = JSON.parse(json);
        console.log(obj);
    </script>

</head>
<body>

</body>
</html>

​ 測試結果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hdQgUEd2-1597068814930)(E:/Typora/image-20200604122219041.png)]

 

Jackson使用:

  1. 匯入Jackson所需依賴
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.0</version>
</dependency>
  1. 編寫Controller
/*如果使用@RestController可以使得所有的方法都返回json字串,不用每個方法都加@ResponseBody了*/
@Controller
public class UserController {
    @RequestMapping("/u1")
    /*@ResponseBody不會走檢視解析器,會直接返回一個字串*/
    @ResponseBody
    public String json() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        User user = new User("海燕醬", 21, "女");
        String str = mapper.writeValueAsString(user);

        return str;
    }
}
  1. 測試結果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p0F1pqEd-1597068814932)(E:/Typora/image-20200604130757643.png)]
 

中文亂碼問題解決:

方式一:在對映的時候改變編碼方式

@RequestMapping(value ="/u1",produces = "application/json;charset=utf-8")

測試結果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FsgYvENK-1597068814934)(E:/Typora/image-20200604132145266.png)]

 

方法二:在springmvc-servlet中進行統一設定

<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>

 
Fastjson三個主要的類:

  • JSONObject代表json物件
    • JSONObject實現了Map介面, 猜想 JSONObject底層操作是由Map實現的
    • JSONObject對應json物件,通過各種形式的get()方法可以獲取json物件中的數據,也可利用諸如size(),isEmpty()等方法獲取"鍵:值"對的個數和判斷是否爲空。其本質是通過實現Map介面並呼叫介面中的方法完成的
  • JSONArray代表json物件陣列
    • 內部是有List介面中的方法來完成操作的
  • JSON代表JSONObject和JSONArray的轉化
    • 在JSON類原始碼中可以看到JSON類主要是實現json物件、json物件陣列、javabean物件、json字串之間的相互轉化
       

Fastjson使用:

  1. 匯入fastjson依賴
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.70</version>
</dependency>
  1. 編寫Controller
@RestController
public class UserController {
    @RequestMapping(value ="/u1",produces = "application/json;charset=utf-8")
    public String json() throws JsonProcessingException {
        //建立一個物件
        User user1 = new User("海燕醬1號", 3, "女");
        User user2 = new User("海燕醬2號", 3, "女");
        User user3 = new User("海燕醬3號", 3, "女");
        User user4 = new User("海燕醬4號", 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);

        return "hello";
    }
}
  1. 測試結果[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VSdctDv0-1597068814936)(E:/Typora/image-20200604140816403.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZCKxqKFW-1597068814938)(E:/Typora/image-20200604141003643.png)]
 

13、整合SSM

  1. 建表建庫
CREATE DATABASE `ssmbuild`;

USE `ssmbuild`;

DROP TABLE IF EXISTS `books`;

CREATE TABLE `books` (
`bookID` INT(10) NOT NULL AUTO_INCREMENT COMMENT '書id',
`bookName` VARCHAR(100) NOT NULL COMMENT '書名',
`bookCounts` INT(11) NOT NULL COMMENT '數量',
`detail` VARCHAR(200) NOT NULL COMMENT '描述',
KEY `bookID` (`bookID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT  INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`)VALUES
(1,'Java',1,'從入門到放棄'),
(2,'MySQL',10,'從刪庫到跑路'),
(3,'Linux',5,'從進門到進牢');
  1. 建立空的Maven專案,在pom.xml中匯入需要的依賴,設定靜態資源過濾
<dependencies>
    <!--Junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!--數據庫驅動-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</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.2</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.2</version>
    </dependency>

    <!--Spring-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    
    <!--低版本會報錯,報錯的話升版本然後多啓動幾次就可以了-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
    </dependency>
</dependencies>
<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>
  1. 建立基本的目錄結構,編寫基礎的mybatis組態檔mybatis-config.xml和spring組態檔applictaionContext.xml檔案

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nu36fGnp-1597068814941)(E:/Typora/image-20200604171816637.png)]

<?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>

</configuration>
<?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">

</beans>
  1. 編寫表相對應的實體類Books.java和數據庫組態檔db.properties
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Books {
    private int bookID;
    private String bookName;
    private int bookCounts;
    private String details;
}
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
  1. 編寫Dao層操作數據庫的BookMapper.interface和BookMapper.xml
public interface BookMapper {
    int addBook(Books book);

    int deleteBookById(@Param("bookId")int id);

    int updateBook(Books book);

    Books queryBookById(@Param("bookId")int id);

    List<Books> queryAllBook();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper>

    <insert id="addBook" parameterType="Books">
        insert into books(bookName,bookCounts,detail)
        values(#{bookName},#{bookCounts},#{detail})
    </insert>

    <delete id="deleteBookById" parameterType="int">
        delete from books where bookID=#{bookId}
    </delete>

    <update id="updateBook" parameterType="Books">
        update books set bookNamed=#{bookName},bookCounts=#{bookCounts},detail=#{detail}
        where bookID=#{bookID}
    </update>

    <select id="queryBookById" parameterType="int">
        select * from books where bookID=#{bookId}
    </select>

    <select id="queryAllBook">
        select * from books
    </select>
</mapper>
  1. 在mybatis核心組態檔中給所有實體類取別名,以及註冊Mapper
<typeAliases>
    <package name="com.Nana.pojo"/>
</typeAliases>

<mappers>
    <mapper class="com.Nana.dao.BookMapper"/>
</mappers>
  1. 編寫業務層BookService.java和BookServiceImpl.java,主要作用是呼叫Dao層
public interface BookService {
    int addBook(Books book);

    int deleteBookById(int id);

    int updateBook(Books book);

    Books queryBookById(int id);

    List<Books> queryAllBook();
}
public class BookServiceImpl implements BookService{
    BookMapper bookMapper;

    public void setBookMapper(BookMapper bookMapper) {
        this.bookMapper = bookMapper;
    }

    public int addBook(Books book) {
        return bookMapper.addBook(book);
    }

    public int deleteBookById(int id) {
        return bookMapper.deleteBookById(id);
    }

    public int updateBook(Books book) {
        return updateBook(book);
    }

    public Books queryBookById(int id) {
        return bookMapper.queryBookById(id);
    }

    public List<Books> queryAllBook() {
        return bookMapper.queryAllBook();
    }
}

整合Mybatis層結束


  1. Spring操作Dao層組態檔spring-dao.xml(==注意:==要和applicationContext在同一個上下文)
<?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">

    <!--1.關聯數據庫組態檔-->
    <context:property-placeholder location="classpath:db.properties"/>

    <!--2.設定連線池
          dbcp:半自動化操作,不能自動連線
          c3p0:自動化操作(自動化的載入組態檔,並且可以自動設定到物件中)
          druid:hikari
    -->
    <bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="user" value="root"/>
        <property name="password" value="123456"/>

        <!--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"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!--4.設定dao介面掃描包,動態實現Dao介面可以注入到Spring容器中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.Nana.dao"/>
    </bean>
</beans>
  1. Spring操作Service層組態檔spring-service.xml(==注意:==要和applicationContext在同一個上下文)
<?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">

    <!--1.掃描service下的包-->
    <context:component-scan base-package="com.Nana.service"/>

    <!--2.將所有的業務類注入到Spring-->
    <bean id="BookServiceImpl" class="com.Nana.service.BookServiceImpl">
        <property name="bookMapper" ref="bookMapper"/>
    </bean>

    <!--3.宣告式事務設定-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="datasource"/>
    </bean>

    <!--4.aop事務支援-->
</beans>

整合Spring層結束


  1. Spring-MVC組態檔spring-mvc.xml(==注意:==要和applicationContext在同一個上下文)
<?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:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--1.註解驅動-->
    <mvc:annotation-driven/>
    <!--2.靜態資源過濾-->
    <mvc:default-servlet-handler/>
    <!--3.掃描包controller-->
    <context:component-scan base-package="com.Nana.controller"/>
    <!--4.檢視解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>
  1. 在專案中新增web框架的支援,設定web.xml
<!--設定DispatcherServlet-->
<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:applicationContext.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>

<!--亂碼過濾-->
<filter>
    <filter-name>encodingFilter</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>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!--session存活時間-->
<session-config>
    <session-timeout>15</session-timeout>
</session-config>
  1. 將三個組態檔都匯入到applicationContext.xml中
<import resource="spring-dao.xml"/>
<import resource="spring-service.xml"/>
<import resource="spring-mvc.xml"/>

整合SpringMVC層結束


 

14、數據增刪改查及前端頁面展示

  1. 編寫數據增刪改查的Dao層和Service層
//BookMapper.java
public interface BookMapper {
    int addBook(Books book);

    int deleteBookById(@Param("bookId")int id);

    int updateBook(Books book);

    Books queryBookById(@Param("bookId")int id);

    List<Books> queryAllBook();
}
<!--BookMapper.xml-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.Nana.dao.BookMapper">

    <insert id="addBook" parameterType="Books">
        insert into books(bookName,bookCounts,detail)
        values(#{bookName},#{bookCounts},#{detail})
    </insert>

    <delete id="deleteBookById" parameterType="int">
        delete from books where bookID=#{bookId}
    </delete>

    <update id="updateBook" parameterType="Books">
        update books set bookName=#{bookName},bookCounts=#{bookCounts},detail=#{detail}
        where bookID=#{bookID}
    </update>

    <select id="queryBookById" parameterType="int" resultType="Books">
        select * from books where bookID=#{bookId}
    </select>

    <select id="queryAllBook" resultType="Books">
        select * from books
    </select>
</mapper>
//BookService.java
public interface BookService {
    int addBook(Books book);

    int deleteBookById(int id);

    int updateBook(Books book);

    Books queryBookById(int id);

    List<Books> queryAllBook();
}
//BookServiceImpl.java
public class BookServiceImpl implements BookService{
    BookMapper bookMapper;

    public void setBookMapper(BookMapper bookMapper) {
        this.bookMapper = bookMapper;
    }

    public int addBook(Books book) {
        return bookMapper.addBook(book);
    }

    public int deleteBookById(int id) {
        return bookMapper.deleteBookById(id);
    }

    public int updateBook(Books book) {
        return bookMapper.updateBook(book);
    }

    public Books queryBookById(int id) {
        return bookMapper.queryBookById(id);
    }

    public List<Books> queryAllBook() {  return bookMapper.queryAllBook(); }

    public Books queryBookByName(String bookName){return bookMapper.queryBookByName(bookName);}
}
  1. 查詢所有書籍

使用BootStrap視覺化佈局系統可以輔助前端程式碼編寫

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>書籍展示</title>
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

    <div class="container">
        <div class="row clearfix">
            <div class="col-md-12 column">
                <div class="page-header">
                    <h1>
                        <small>書籍列表--------顯示所有書籍</small>
                    </h1>
                </div>
            </div>

            <div class="row">
                <div class="col-md-4 column">
                    <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增書籍</a>
                    <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">顯示全部書籍</a>
                </div>
                <div class="col-md-8 column">
                    <form class="form-inline" action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float:right">
                        <span style="color:red; font-weight: bold">${error}</span>
                        <input type="text" name="queryBookName" class="form-control" placeholder="請輸入要查詢的書籍名稱">
                        <input type="submit" value="查詢" class="btn btn-primary">
                    </form>
                </div>
            </div>
        </div>

        <div class="row clearfix">
            <div class="col-md-12 column">
                <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="book" items="${list}">
                            <tr>
                                <td>${book.bookID}</td>
                                <td>${book.bookName}</td>
                                <td>${book.bookCounts}</td>
                                <td>${book.detail}</td>
                                <td>
                                    <a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.bookID}">修改</a>
                                    &nbsp | &nbsp
                                    <a href="${pageContext.request.contextPath}/book/deleteBook?id=${book.bookID}">刪除</a>
                                </td>
                            </tr>
                        </c:forEach>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    
</body>
</html>
@Controller
@RequestMapping("/book")
public class BookController {
    @Autowired
    @Qualifier("BookServiceImpl")
    private BookService bookService;

    @RequestMapping("/allBook")
    public String list(Model model){
        List<Books> list = bookService.queryAllBook();
        model.addAttribute("list",list);
        return "allBook";
    }
}
  1. 增加書籍資訊
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

    <div class="container">
        <div class="row clearfix">
            <div class="col-md-12 column">
                <div class="page-header">
                    <h1>
                        <small>新增書籍</small>
                    </h1>
                </div>
            </div>
        </div>

        <form action="/book/addBook" method="post">
            <div class="form-group">
                <label>書籍名稱:</label>
                <input type="text" name="bookName" class="form-control" required="required">
            </div>
            <div class="form-group">
                <label>書籍數量:</label>
                <input type="text" name="bookCounts" class="form-control" required="required">
            </div>
            <div class="form-group">
                <label>書籍描述:</label>
                <input type="text" name="detail" class="form-control" required="required">
            </div>
            <div class="form-group">
                <input type="submit" class="form-control" value="提交">
            </div>
        </form>
    </div>

</body>
</html>
@RequestMapping("/toAddBook")
public String toAddPaper(){
    return "addBook";
}

@RequestMapping("/addBook")
public String addBook(Books book){
    bookService.addBook(book);
    return "redirect:/book/allBook";
}
  1. 修改書籍資訊
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

    <div class="container">
        <div class="row clearfix">
            <div class="col-md-12 column">
                <div class="page-header">
                    <h1>
                        <small>修改書籍</small>
                    </h1>
                </div>
            </div>
        </div>
    
        <form action="/book/updateBook" method="post">
            <input type="hidden" name="bookID" value="${QBook.bookID}">
            <div class="form-group">
                <label>書籍名稱:</label>
                <input type="text" name="bookName" value=${QBook.bookName} class="form-control" required="required">
            </div>
            <div class="form-group">
                <label>書籍數量:</label>
                <input type="text" name="bookCounts" value="${QBook.bookCounts}" class="form-control" required="required">
            </div>
            <div class="form-group">
                <label>書籍描述:</label>
                <input type="text" name="detail" value="${QBook.detail}" class="form-control" required="required">
            </div>
            <div class="form-group">
                <input type="submit" class="form-control" value="修改">
            </div>
        </form>
    </div>

</body>
</html>
@RequestMapping("/toUpdateBook")
public String toUpdateBook(int id ,Model model){
    Books book = bookService.queryBookById(id);
    model.addAttribute("QBook",book);
    return "updateBook";
}

@RequestMapping("/updateBook")
public String updateBook(Books book){
    bookService.updateBook(book);
    return "redirect:/book/allBook";
}
  1. 刪除書籍資訊
@RequestMapping("/deleteBook")
public String toDeleteBook(int id, Model model){
    bookService.deleteBookById(id);
    return "redirect:/book/allBook";
}
  1. 根據名字查詢書籍(橫向擴充套件業務)
//BookMapper.java
Books queryBookByName(String bookName);
<!--BookMapper.xml-->
<select id="queryBookByName" resultType="Books">
    select * from books where bookName=#{bookName}
</select>
//BookService.java
Books queryBookByName(String bookName);
//BookServiceImpl.java
public Books queryBookByName(String bookName){return bookMapper.queryBookByName(bookName);}
//BookController.java
@RequestMapping("/queryBook")
public String queryBook(String queryBookName, Model model){
    Books book=bookService.queryBookByName(queryBookName);
    List<Books> list = new ArrayList<Books>();
    list.add(book);
    if(book==null){
        list=bookService.queryAllBook();
        model.addAttribute("error","未找到");
    }
    model.addAttribute("list",list);
    return "allBook";
}

 

15、Ajax

AJAX(Asynchronous JavaScript and XML,非同步的JavaScript和XML)。是一種在無需重新載入整個網頁的情況下,能夠更新部分網頁的技術。不是一種新的程式語言,而是一種用於建立更好更快以及互動性更強的Web應用程式的技術

  • 發展:在2005年,Google通過其Google Suggest使Ajax變得流行起來
  • 應用:谷歌和百度搜尋方塊
  • 與傳統網頁對比:
    • 傳統網頁(不用Ajax技術的網頁),想要更新內容或者提交一個表單,需要重新載入整個網頁
    • 新型網頁(使用Ajax技術的網頁),通過在後台伺服器進行少量的數據交換,可以實現非同步區域性更新
  • 優點:Ajax技術使得使用者可以建立接近本地桌面應用的直接、高可用、更豐富、更動態的Web用戶介面
     

15.1 使用前端iframe標籤僞造Ajax

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>僞造Ajax</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>請輸入要載入的地址:<span id="currentTime"></span></p>
    <p>
        <input type="text" id="url" value="https://www.baidu.com/">
        <input type="button" value="提交" onclick="loadPage()">
    </p>
</div>
<div>
    <h3> 載入頁面的位置: </h3>
    <iframe style="width:100%; height:500px" id="iframePosition"></iframe>
</div>
</body>
</html>

 

15.2 使用jQuery實現Ajax

Ajax的核心是XMLHttpRequest物件(XHR),XHR爲向伺服器發送請求和解析伺服器響應提供了介面,能夠以非同步方式從伺服器獲取新數據

  • jQuery Ajax作用:能夠使用HTTP Get和HTTP Post從遠端伺服器上請求文字、HTML、XML或JSON,同時可以把外部數據直接載入網頁的被選元素中

  • jQuery Ajax本質:對XMLHttpRequest進行了封裝,便於呼叫

  • jQuery Ajax部分參數:

jQuery.ajax(...)
            url:請求地址
            type:請求方式,GET、POST(1.9.0之後用method)
        headers:請求頭
            data:要發送的數據
    contentType:即將發送資訊至伺服器的內容編碼型別(預設: "application/x-www-form-urlencoded; charset=UTF-8")
          async:是否非同步
        timeout:設定請求超時時間(毫秒)
      beforeSend:發送請求前執行的函數(全域性)
        complete:完成之後執行的回撥函數(全域性)
        success:成功之後執行的回撥函數(全域性)
          error:失敗之後執行的回撥函數(全域性)
        accepts:通過請求頭髮送給伺服器,告訴伺服器當前用戶端可接受的數據型別
        dataType:將伺服器端返回的數據轉換成指定型別
          "xml": 將伺服器端返回的內容轉換成xml格式
          "text": 將伺服器端返回的內容轉換成普通文字格式
          "html": 將伺服器端返回的內容轉換成普通文字格式,在插入DOM中時,如果包含JavaScript標籤,則會嘗試去執行。
        "script": 嘗試將返回值當作JavaScript去執行,然後再將伺服器端返回的內容轉換成普通文字格式
          "json": 將伺服器端返回的內容轉換成相應的JavaScript物件
        "jsonp": JSONP 格式使用 JSONP 形式呼叫函數時,如 "myurl?callback=?" jQuery 將自動替換 ? 爲正確的函數名,以執行回撥函數

 
jQuery.ajax()測試:

  1. 設定SpringMVC所需的applicationContext.xml和web.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.Nana.controller"/>
    <mvc:default-servlet-handler/>
    
    <!--處理JSON亂碼問題-->
    <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>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <property name="prefix" value="/WEB-INF"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>
<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:applicationContext.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>

<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>
  1. 下載jQuery,並將其引入到前端jsp頁面中

下載網址:https://jquery.com/download/

<script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.js"></script>
  1. 編寫前端介面
<head>
  <title>$Title$</title>
  <script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.js"></script>

  <script>
    function a() {
      $.post({
        url:"${pageContext.request.contextPath}/t1",
        data:{"name":$("#username").val()},
        success:function (data) {
          alert(data);
        }
      })
    }
  </script>
</head>
<body>
使用者名稱:<input type="text" id="username" οnblur="a()">
</body>
  1. 編寫controller層
@RestController
public class AjaxController {
    @RequestMapping("/t1")
    public void test(String name, HttpServletResponse resp) throws IOException {
        System.out.println(name);
        if("海燕醬".equals(name)){
            resp.getWriter().print("true");
        }
        else{
            resp.getWriter().print("false");
        }
    }
}
  1. 啓動測試

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GuVbOnzd-1597068814945)(E:/Typora/image-20200605160000866.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BzUmDF8a-1597068814947)(E:/Typora/image-20200605160021200.png)]
 
Ajax非同步載入數據測試:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.js"></script>

    <script>
        $(function () {
            $("#btn").click(function () {
                $.post("${pageContext.request.contextPath}/t2",function (data) {
                    var 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>
@RequestMapping("/t2")
public List<User> test2(){
    List<User> list = new ArrayList<User>();

    list.add(new User("Java",1,"男"));
    list.add(new User("前端",1,"女"));
    list.add(new User("運維",1,"男"));

    return list;
}

啓動測試:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Myo68vW-1597068814950)(E:/Typora/image-20200605212352965.png)]
 

Ajax動態驗證使用者名稱密碼測試:

<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<html>
<head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.js"></script>

    <script>
        function a1() {
            $.post({
                url:"${pageContext.request.contextPath}/t3",
                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}/t3",
                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" οnblur="a1()">
        <span id="userInfo"></span>
    </p>
    <p>
        密碼:<input type="text" id="pwd" οnblur="a2()">
        <span id="pwdInfo"></span>
    </p>
</body>
</html>
@RequestMapping("/t3")
public String test3(String name, String pwd){
    String msg="";
    if(name!=null){
        if("admin".equals(name)){
            msg = "ok";
        }
        else{
            msg = "使用者名稱有誤";
        }
    }
    if(pwd!=null){
        if("123456".equals(pwd)){
            msg = "ok";
        }
        else{
            msg = "密碼有誤";
        }
    }
    return msg;
}

測試結果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CgVBKm4c-1597068814952)(E:/Typora/image-20200605223339722.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TzZ2WBag-1597068814954)(E:/Typora/image-20200605225004949.png)]
 

16、攔截器

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

  • 與過濾器區別:

    • 過濾器:
      • servlet規範中的一部分,任何java web工程都可以使用
      • 在url-pattern中設定了/*之後,可以對所有要存取的資源進行攔截
    • 攔截器
      • SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能 纔能使用
      • 只會攔截存取的控制器方法(@Controlle註釋的方法)
  • 自定義攔截器必須實現HandlerInterceptor介面
     

自定義攔截器使用:

  1. 新建一個工程,新增web支援,匯入lib依賴
  2. 設定web.xml和applicationContext.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">

    <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:applicationContext.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>

    <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">

    <context:component-scan base-package="com.Nana.controller"/>
    <mvc:default-servlet-handler/>
    <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>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <property name="prefix" value="/WEB-INF"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>
  1. 編寫一個實現了HandlerInceptor的攔截器
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("------------清理------------");
    }
}
  1. 在applicationContext中註冊攔截器
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.Nana.config.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>
  1. 編寫網址對映
@RestController
public class TestController {
    @RequestMapping("/t1")
    public String test(){
        System.out.println("方法執行了");
        return "hello";
    }
}
  1. 啓動測試

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jW33jzVC-1597068814956)(E:/Typora/image-20200606113823953.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eWXJUCz2-1597068814959)(E:/Typora/image-20200606114130573.png)]
 

使用者登錄攔截實現:

  1. 編寫首頁index.jsp、登錄頁面login.jsp、登陸成功頁面success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>$Title$</title>
</head>
<body>
    <h1>首頁</h1>
    <hr>
    <a href="${pageContext.request.contextPath}/user/jumplogin">登錄</a>
    <a href="${pageContext.request.contextPath}/user/jumpSuccess">成功頁面</a>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>

<h1>登錄頁面</h1>
<hr>

<body>
    <form action="${pageContext.request.contextPath}/user/login">
        使用者名稱:<input type="text" name="username"> <br>
        密碼:<input type="password" name="pwd"> <br>
        <input type="submit" value="提交">
    </form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>登錄成功頁面</h1>
    <hr>
        ${user}
    <a href="${pageContext.request.contextPath}/user/logout">註銷</a>
</body>
</html>
  1. 編寫過濾器
public class LoginInterceptor implements HandlerInterceptor {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        // 如果是登陸頁面則放行
        System.out.println("uri: " + request.getRequestURI());
        if (request.getRequestURI().contains("login")) {
            return true;
        }

        HttpSession session = request.getSession();

        // 如果使用者已登陸也放行
        if(session.getAttribute("user") != null) {
            return true;
        }

        // 使用者沒有登陸跳轉到登陸頁面
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
        return false;
    }

    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { }

    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { }
}
  1. 註冊過濾器
<mvc:interceptor>
    <mvc:mapping path="/**"/>
    <bean id="loginInterceptor" class="com.Nana.config.LoginInterceptor"/>
</mvc:interceptor>
  1. 啓動測試:使用者未登錄或者註銷之後無法直接進入到登錄成功頁面頁面
     

17、檔案上傳和下載

在2003年,Apache Software Foundation發佈了開源的Commons FileUpload元件,很快成爲Servlet/JSP程式設計師上傳檔案的最佳選擇。Servlet3.0規範已經提供方法來處理檔案上傳,但這種上傳需要在Servlet中完成,SpringMVC則進行了簡單的封裝。

SpringMVC使用FileUpload技術實現了一個MultipartResolver實現類CommonMultipartResolver進行檔案上傳,MultipartResolver是隨插即用的

  • CommonsMultipartFile 的 常用方法:
    • String getOriginalFilename():獲取上傳檔案的原名
    • InputStream getInputStream():獲取檔案流
    • void transferTo(File dest):將上傳檔案儲存到一個目錄檔案中
       

檔案上傳實現:

  1. 匯入檔案上傳所需要的jar包
<!--檔案上傳-->
<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>
  1. 在applicationContext.xml中設定multipartResolver

SpringMVC可以很好地支援檔案上傳,但是SpringMVC上下文中預設沒有裝配MultipartPart,因此預設情況下不能處理檔案上傳工作。如果想使用此功能,需在上下文中設定MultipartResolver

注意:bean的id必須爲multipartResolver,否則上傳檔案會報400錯誤

<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>
  1. 編寫前端頁面

注意:如果需要上傳檔案的話,必須將表單的method設定爲POST,並將enctype設定爲multipart/form-data。只有在這樣的情況下,瀏覽器纔會把使用者選擇的檔案以二進制數據發送給伺服器

表單中enctype屬性介紹:

  • application/x-www=form-urlencoded:預設方式,只處理表單域中的 value 屬性值,採用這種編碼方式的表單會將表單域中的值處理成 URL 編碼方式

  • multipart/form-data:這種編碼方式會以二進制流的方式來處理表單數據,這種編碼方式會把檔案域指定檔案的內容也封裝到請求參數中,不會對字元編碼

  • text/plain:除了把空格轉換爲 「+」 號外,其他字元都不做編碼處理,這種方式適用直接通過表單發送郵件

<form action="/upload" enctype="multipart/form-data" method="post">
 <input type="file" name="file"/>
 <input type="submit" value="upload">
</form>
  1. 編寫Controller
@Controller
public class FileController {
    //@RequestParam("file") 將name=file控制元件得到的檔案封裝成CommonsMultipartFile 物件
    //批次上傳CommonsMultipartFile則爲陣列即可
    @RequestMapping("/upload")
    public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {

        //獲取檔名
        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";
    }
}

 

檔案上傳儲存實現:

  1. 修改檔案上傳步驟4
@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";
}
  1. 修改表單提交路徑
  2. 啓動測試:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2FKp714P-1597068814961)(E:/Typora/image-20200606135913905.png)]
 
下載檔案步驟:

  1. 設定response響應頭
  2. 使用InputStream讀取檔案
  3. 使用OutputStream寫出檔案
  4. 執行操作
  5. 關閉流,要注意先開啓的後關閉

下載檔案實現:

  1. 編寫Controller

注意:要使用正確的檔案路徑和檔名,記得加後綴名

@RequestMapping(value="/download")
public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
    //要下載的圖片路徑和名稱
    String  path = request.getServletContext().getRealPath("/upload");
    String  fileName = "TIM截圖20190430231748.png";

    //1、設定response 響應頭
    
    //設定頁面不快取,清空buffer
    response.reset(); 
    //設定字元編碼
    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();
    }
    //5、關閉流
    out.close();
    input.close();
    return null;
}
  1. 編寫前端跳轉鏈接
<a href="/download">點選下載</a>
  1. 啓動測試
    在这里插入图片描述