SpringMVC

疏影橫斜水清淺發表於2024-04-19

SpringMVC

1、回顧MVC

1.1什麼是mvc

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

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

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

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

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

img

1.2、Model1時代

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

img

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

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

1.3、Model2時代

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

img

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

職責分析:

Controller:控制器

  1. 取得表單資料
  2. 呼叫業務邏輯
  3. 轉向指定的頁面

Model:模型

  1. 業務邏輯
  2. 儲存資料的狀態

View:檢視

  1. 顯示頁面

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

1.4、回顧Servlet

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

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>
    
  2. 建立一個Moudle:springmvc-01-servlet , 新增Web app的支援!

  3. 匯入servlet 和 jsp 的 jar 依賴

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>
    
  4. 編寫一個Servlet類,用來處理使用者的請求

    package com.kuang.servlet;
    //實現Servlet介面
    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/hello.jsp").forward(req,resp);
        }
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req,resp);
        }
    }
    
  5. 編寫Hello.jsp,在WEB-INF目錄下新建一個jsp的資料夾,新建hello.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Kuangshen</title>
    </head>
    <body>
        ${msg}
    </body>
    </html>
    
  6. 在web.xml中註冊Servlet

    <?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>HelloServlet</servlet-name>
            <servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>HelloServlet</servlet-name>
            <url-pattern>/user</url-pattern>
        </servlet-mapping>
    </web-app>
    
  7. 配置Tomcat,並啟動測試

    • localhost:8080/user?method=add
    • localhost:8080/user?method=delete

MVC框架要做哪些事情

  1. 將url對映到java類或java類的方法 .
  2. 封裝使用者提交的資料 .
  3. 處理請求—呼叫相關的業務處理—封裝響應資料 .
  4. 將響應的資料進行渲染 . jsp / html 等表示層資料 .

說明:

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

2、什麼是SpringMVC

2.1、概述

img

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

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

我們為什麼要學習SpringMVC呢?

Spring MVC的特點:

  1. 輕量級,簡單易學
  2. 高效 , 基於請求響應的MVC框架
  3. 與Spring相容性好,無縫結合
  4. 約定優於配置
  5. 功能強大:RESTful、資料驗證、格式化、本地化、主題等
  6. 簡潔靈活

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

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

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

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

2.2、中心控制器

Spring的web框架圍繞DispatcherServlet設計。 DispatcherServlet的作用是將請求分發到不同的處理器。從Spring 2.5開始,使用Java 5或者以上版本的使用者可以採用基於註解的controller宣告方式。

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

img
SpringMVC的原理如下圖所示:

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

img

2.3、SpringMVC執行原理

img

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

簡要分析執行流程

  1. DispatcherServlet表示前置控制器,是整個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查詢控制器,如上url被查詢控制器為:hello。
  3. HandlerExecution將解析後的資訊傳遞給DispatcherServlet,如解析控制器對映等。
  4. HandlerAdapter表示處理器介面卡,其按照特定的規則去執行Handler。
  5. Handler讓具體的Controller執行。
  6. Controller將具體的執行資訊返回給HandlerAdapter,如ModelAndView。
  7. HandlerAdapter將檢視邏輯名或模型傳遞給DispatcherServlet。
  8. DispatcherServlet呼叫檢視解析器(ViewResolver)來解析HandlerAdapter傳遞的邏輯檢視名。
  9. 檢視解析器將解析的邏輯檢視名傳給DispatcherServlet。
  10. DispatcherServlet根據檢視解析器解析的檢視結果,呼叫具體的檢視。
  11. 最終檢視呈現給使用者。

3、HelloSpring

3.1、配置版

  1. 新建一個Moudle , springmvc-02-hello , 新增web的支援!

  2. 確定匯入了SpringMVC 的依賴!

  3. 配置web.xml , 註冊DispatcherServlet

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <!--1.註冊DispatcherServlet-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--關聯一個springmvc的配置檔案:【servlet-name】-servlet.xml-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!--啟動級別-1-->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--/ 匹配所有的請求;(不包括.jsp)-->
        <!--/* 匹配所有的請求;(包括.jsp)-->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
  4. 編寫SpringMVC 的 配置檔案!名稱:springmvc-servlet.xml : [servletname]-servlet.xml

    說明,這裡的名稱要求是按照官方來的

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    </beans>
    
  5. 新增 處理對映器

    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    
  6. 新增 處理器介面卡

    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    
  7. 新增 檢視解析器

    <!--檢視解析器:DispatcherServlet給他的ModelAndView-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
        <!--字首-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--字尾-->
        <property name="suffix" value=".jsp"/>
    </bean>
    
  8. 編寫我們要操作業務Controller ,要麼實現Controller介面,要麼增加註解;需要返回一個ModelAndView,裝資料,封檢視;

    package com.kuang.controller;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    //注意:這裡我們先匯入Controller介面
    public class HelloController implements Controller {
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            //ModelAndView 模型和檢視
            ModelAndView mv = new ModelAndView();
            //封裝物件,放在ModelAndView中。Model
            mv.addObject("msg","HelloSpringMVC!");
            //封裝要跳轉的檢視,放在ModelAndView中
            mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
            return mv;
        }
    }
    
  9. 將自己的類交給SpringIOC容器,註冊bean

    <!--Handler--><bean id="/hello" class="com.kuang.controller.HelloController"/>
    
  10. 寫要跳轉的jsp頁面,顯示ModelandView存放的資料,以及我們的正常頁面;

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Kuangshen</title>
    </head>
    <body>
        ${msg}
    </body>
    </html>
    
  11. 配置Tomcat 啟動測試!

img

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

  1. 檢視控制檯輸出,看一下是不是缺少了什麼jar包。
  2. 如果jar包存在,顯示無法輸出,就在IDEA的專案釋出中,新增lib依賴!
  3. 重啟Tomcat 即可解決!

小結:看這個估計大部分人都能理解其中的原理了,但是我們實際開發才不會這麼寫,不然就瘋了,還學這個玩意幹嘛!我們來看個註解版實現,這才是SpringMVC的精髓,到底有多麼簡單,看這個圖就知道了。

img

3.2、註解版

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

    建立包結構 com.kuang.controller

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

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

  4. 配置web.xml

    注意點:

    • 注意web.xml版本問題,要最新版!
    • 註冊DispatcherServlet
    • 關聯SpringMVC的配置檔案
    • 啟動級別為1
    • 對映路徑為 / 【不要用/*,會404】
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <!--1.註冊servlet-->
        <servlet>
            <servlet-name>SpringMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--透過初始化引數指定SpringMVC配置檔案的位置,進行關聯-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!-- 啟動順序,數字越小,啟動越早 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--所有請求都會被springmvc攔截 -->
        <servlet-mapping>
            <servlet-name>SpringMVC</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    

    / 和 /* 的區別:
    < url-pattern > / </ url-pattern > 不會匹配到.jsp, 只針對我們編寫的請求;
    即:.jsp 不會進入spring的 DispatcherServlet類 。
    < url-pattern > / </ url-pattern > 會匹配 .jsp,
    會出現返回 jsp檢視 時再次進入spring的DispatcherServlet 類,導致找不到對應的controller所以報404錯。

  5. 新增Spring MVC配置檔案

    • 讓IOC的註解生效
    • 靜態資源過濾 :HTML . JS . CSS . 圖片 , 影片 …..
    • MVC的註解驅動
    • 配置檢視解析器

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

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:mvc="http://www.springframework.org/schema/mvc"       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context        https://www.springframework.org/schema/context/spring-context.xsd        http://www.springframework.org/schema/mvc        https://www.springframework.org/schema/mvc/spring-mvc.xsd">    <!-- 自動掃描包,讓指定包下的註解生效,由IOC容器統一管理 -->    <context:component-scan base-package="com.kuang.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>
    

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

  6. 建立Controller

    編寫一個Java控制類: com.kuang.controller.HelloController , 注意編碼規範

    package com.kuang.controller;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    @Controller
    @RequestMapping("/HelloController")
    public class HelloController {
        //真實訪問地址 : 專案名/HelloController/hello
        @RequestMapping("/hello")
        public String sayHello(Model model){
            //向模型中新增屬性msg與值,可以在JSP頁面中取出並渲染
            model.addAttribute("msg","hello,SpringMVC");
            //web-inf/jsp/hello.jsp
            return "hello";
        }
    }
    
    • @Controller是為了讓Spring IOC容器初始化時自動掃描到;
    • @RequestMapping是為了對映請求路徑,這裡因為類與方法上都有對映所以訪問時應該是/HelloController/hello;
    • 方法中宣告Model型別的引數是為了把Action中的資料帶到檢視中;
    • 方法返回的結果是檢視的名稱hello,加上配置檔案中的前字尾變成WEB-INF/jsp/hello.jsp。
  7. 建立檢視層

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

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

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

    配置Tomcat , 開啟伺服器 , 訪問 對應的請求路徑!

img

OK,執行成功!

3.3、小結

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

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

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

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

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

4、Controller 及 RestFul

4.1、控制器Controller

  • 控制器複雜提供訪問應用程式的行為,通常透過介面定義或註解定義兩種方法實現。
  • 控制器負責解析使用者的請求並將其轉換為一個模型。
  • 在Spring MVC中一個控制器類可以包含多個方法
  • 在Spring MVC中,對於Controller的配置方式有很多種

我們來看看有哪些方式可以實現:

4.2、實現Controller介面

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

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

測試

  1. 新建一個Moudle,springmvc-04-controller 。 將剛才的03 複製一份, 我們進行操作!

    • 刪掉HelloController
    • mvc的配置檔案只留下 檢視解析器!
  2. 編寫一個Controller類,ControllerTest1

    //定義控制器
    //注意點:不要導錯包,實現Controller介面,重寫方法;
    public class ControllerTest1 implements Controller {
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            //返回一個模型檢視物件
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg","Test1Controller");
            mv.setViewName("test");
            return mv;
        }
    }
    
  3. 編寫完畢後,去Spring配置檔案中註冊請求的bean;name對應請求路徑,class對應處理請求的類

    <bean name="/t1" class="com.kuang.controller.ControllerTest1"/>
    
  4. 編寫前端test.jsp,注意在WEB-INF/jsp目錄下編寫,對應我們的檢視解析器

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Kuangshen</title>
    </head>
    <body>
        ${msg}
    </body>
    </html>
    
  5. 配置Tomcat執行測試,我這裡沒有專案釋出名配置的就是一個 / ,所以請求不用加專案名,OK!

img

說明:

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

4.3、使用註解@Controller

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

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

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

    //@Controller註解的類會自動新增到Spring上下文中
    @Controller
    public class ControllerTest2{
        //對映訪問路徑
        @RequestMapping("/t2")
        public String index(Model model){
            //Spring MVC會自動例項化一個Model物件用於向檢視中傳值
            model.addAttribute("msg", "ControllerTest2");
            //返回檢視位置
            return "test";
        }
    }
    
  • 執行tomcat測試

img

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

註解方式是平時使用的最多的方式!除了這兩種之外還有其他的方式,大家想要自己研究的話,可以參考我的部落格:https://www.cnblogs.com/hellokuangshen/p/11270742.html

4.4、RequestMapping

@RequestMapping

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

  • 為了測試結論更加準確,我們可以加上一個專案名測試 myweb

  • 只註解在方法上面

    @Controller
    public class TestController {
        @RequestMapping("/h1")
        public String test(){
            return "test";
        }
    }
    

    訪問路徑:http://localhost:8080 / 專案名 / h1

  • 同時註解類與方法

    @Controller
    @RequestMapping("/admin")
    public class TestController {
        @RequestMapping("/h1")
        public String test(){
            return "test";
        }
    }
    

    訪問路徑:http://localhost:8080 / 專案名/ admin /h1 , 需要先指定類的路徑再指定方法的路徑;

4.5、RestFul 風格

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

功能
資源:網際網路所有的事物都可以被抽象為資源
資源操作:使用POST、DELETE、PUT、GET,使用不同方法對資源進行操作。
分別對應 新增、 刪除、修改、查詢。

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

http://127.0.0.1/item/queryItem.action?id=1 查詢,GET
http://127.0.0.1/item/saveItem.action 新增,POST
http://127.0.0.1/item/updateItem.action 更新,POST
http://127.0.0.1/item/deleteItem.action?id=1 刪除,GET或POST

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

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

學習測試

  1. 在新建一個類 RestFulController

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

    @Controller
    public class RestFulController {
        //對映訪問路徑
        @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";
        }
    }
    
  3. 我們來測試請求檢視下

img

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

  • 使路徑變得更加簡潔;
  • 獲得引數更加方便,框架會自動進行型別轉換。
  • 透過路徑變數的型別可以約束訪問引數,如果型別不一樣,則訪問不到對應的請求方法,如這裡訪問是的路徑是/commit/1/a,則路徑與方法不匹配,而不會是引數轉換失敗。

img

  1. 我們來修改下對應的引數型別,再次測試

    //對映訪問路徑@RequestMapping("/commit/{p1}/{p2}")public String index(@PathVariable int p1, @PathVariable String p2, Model model){    String result = p1+p2;    //Spring MVC會自動例項化一個Model物件用於向檢視中傳值    model.addAttribute("msg", "結果:"+result);    //返回檢視位置    return "test";}
    

img

使用method屬性指定請求型別

用於約束請求的型別,可以收窄請求範圍。指定請求謂詞的型別如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等

我們來測試一下:

  • 增加一個方法

    //對映訪問路徑
    @RequestMapping("/commit/{p1}/{p2}")
    public String index(@PathVariable int p1, @PathVariable String p2, Model model){
        String result = p1+p2;
        //Spring MVC會自動例項化一個Model物件用於向檢視中傳值
        model.addAttribute("msg", "結果:"+result);
        //返回檢視位置
        return "test";
    }
    
  • 我們使用瀏覽器位址列進行訪問預設是Get請求,會報錯405:

img

  • 如果將POST修改為GET則正常了;

    //對映訪問路徑,必須是POST請求
    @RequestMapping(value = "/hello",method = {RequestMethod.POST})
    public String index2(Model model){
        model.addAttribute("msg", "hello!");
        return "test";
    }
    

img

小結:

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

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

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

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

@GetMapping 是一個組合註解

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

平時使用的會比較多!

4.6、小黃鴨除錯法

場景一:我們都有過向別人(甚至可能向完全不會程式設計的人)提問及解釋程式設計問題的經歷,但是很多時候就在我們解釋的過程中自己卻想到了問題的解決方案,然後對方卻一臉茫然。

場景二:你的同行跑來問你一個問題,但是當他自己把問題說完,或說到一半的時候就想出答案走了,留下一臉茫然的你。

其實上面兩種場景現象就是所謂的小黃鴨除錯法(Rubber Duck Debuging),又稱橡皮鴨除錯法,它是我們軟體工程中最常使用除錯方法之一。

img

此概念據說來自《程式設計師修煉之道》書中的一個故事,傳說程式大師隨身攜帶一隻小黃鴨,在除錯程式碼的時候會在桌上放上這隻小黃鴨,然後詳細地向鴨子解釋每行程式碼,然後很快就將問題定位修復了。

5、結果跳轉方式

5.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","ControllerTest1");
        mv.setViewName("test");
        return mv;
    }
}

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

5.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";
        //return "redirect:hello.do"; //hello.do為另一個請求/
    }
}

6、資料處理

6.1、處理提交資料

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

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

處理方法 :

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

後臺輸出 : kuangshen

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

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

處理方法 :

//@RequestParam("username") : username提交的域的名稱 .
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
    System.out.println(name);
    return "hello";
}

後臺輸出 : kuangshen

3、提交的是一個物件

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

  1. 實體類

    public class User {
        private int id;
        private String name;
        private int age;
        //構造
        //get/set
        //tostring()
    }
    
  2. 提交資料 : http://localhost:8080/mvc04/user?name=kuangshen&id=1&age=15

  3. 處理方法 :

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

    後臺輸出 : User

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

6.2、資料顯示到前端

第一種 : 透過ModelAndView

我們前面一直都是如此 . 就不過多解釋

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

第二種 : 透過ModelMap

ModelMap

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

第三種 : 透過Model

Model

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

6.3、對比

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

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

當然更多的以後開發考慮的更多的是效能和最佳化,就不能單單僅限於此的瞭解。

請使用80%的時間打好紮實的基礎,剩下18%的時間研究框架,2%的時間去學點英文,框架的官方文件永遠是最好的教程。

6.4、亂碼問題

測試步驟:

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

    <form action="/e/t" method="post">
      <input type="text" name="name">
      <input type="submit">
    </form>
    
  2. 後臺編寫對應的處理類

    @Controller
    public class Encoding {
        @RequestMapping("/e/t")
        public String test(Model model,String name){
            model.addAttribute("msg",name); //獲取表單提交的值
            return "test"; //跳轉到test頁面顯示輸入的值
        }
    }
    
  3. 輸入中文測試,發現亂碼

img

不得不說,亂碼問題是在我們開發中十分常見的問題,也是讓我們程式猿比較頭大的問題!

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

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

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

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

處理方法 :

  1. 修改tomcat配置檔案 : 設定編碼!

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

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

    這個也是我在網上找的一些大神寫的,一般情況下,SpringMVC預設的亂碼處理就已經能夠很好的解決了!

    然後在web.xml中配置這個過濾器即可!

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

7、整合SSM

7.1、環境要求

環境:

  • IDEA

  • MySQL 5.7.19

  • Tomcat 9

  • Maven 3.6

    要求:

  • 需要熟練掌握MySQL資料庫,Spring,JavaWeb及MyBatis知識,簡單的前端知識;

7.2、資料庫環境

建立一個存放書籍資料的資料庫表

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,'從進門到進牢');

7.3、基本環境搭建

  1. 新建一Maven專案! ssmbuild , 新增web的支援

  2. 匯入相關的pom依賴!

    <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>
    </dependencies>
    
  3. Maven資源過濾設定

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
    
  4. 建立基本結構和配置框架!

    • com.stone.pojo

    • com.stone.dao

    • com.stone.service

    • com.stone.controller

    • 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>
      </configuration>
      
    • 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">
      </beans>
      

7.4、Mybatis層編寫

  1. 資料庫配置檔案 database.properties

    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
    
  2. IDEA關聯資料庫

  3. 編寫MyBatis的核心配置檔案

    <?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>
        <typeAliases>
            <package name="com.stone.pojo"/>
        </typeAliases>
        <mappers>
            <mapper resource="com/stone/dao/BookMapper.xml"/>
        </mappers>
    </configuration>
    
  4. 編寫資料庫對應的實體類 com.stone.pojo.Books

    使用lombok外掛!

    package com.stone.pojo;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Books {
        private int bookID;
        private String bookName;
        private int bookCounts;
        private String detail;
    }
    
  5. 編寫Dao層的 Mapper介面!

    package com.stone.dao;
    import com.stone.pojo.Books;
    import java.util.List;
    public interface BookMapper {
        //增加一個Book
        int addBook(Books book);
        //根據id刪除一個Book
        int deleteBookById(int id);
        //更新Book
        int updateBook(Books books);
        //根據id查詢,返回一個Book
        Books queryBookById(int id);
        //查詢全部Book,返回list集合
        List<Books> queryAllBook();
    }
    
  6. 編寫介面對應的 Mapper.xml 檔案。需要匯入MyBatis的包;

    <?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.stone.dao.BookMapper">
        <!--增加一個Book-->
        <insert id="addBook" parameterType="Books">
            insert into ssmbuild.books(bookName,bookCounts,detail)
            values (#{bookName}, #{bookCounts}, #{detail})
        </insert>
        <!--根據id刪除一個Book-->
        <delete id="deleteBookById" parameterType="int">
            delete from ssmbuild.books where bookID=#{bookID}
        </delete>
        <!--更新Book-->
        <update id="updateBook" parameterType="Books">
            update ssmbuild.books
            set bookName = #{bookName},bookCounts = #{bookCounts},detail = #{detail}
            where bookID = #{bookID}
        </update>
        <!--根據id查詢,返回一個Book-->
        <select id="queryBookById" resultType="Books">
            select * from ssmbuild.books
            where bookID = #{bookID}
        </select>
        <!--查詢全部Book-->
        <select id="queryAllBook" resultType="Books">
            SELECT * from ssmbuild.books
        </select>
    </mapper>
    
  7. 編寫Service層的介面和實現類

    介面:

    package com.stone.service;
    import com.stone.pojo.Books;
    import java.util.List;
    //BookService:底下需要去實現,呼叫dao層
    public interface BookService {
        //增加一個Book
        int addBook(Books book);
        //根據id刪除一個Book
        int deleteBookById(int id);
        //更新Book
        int updateBook(Books books);
        //根據id查詢,返回一個Book
        Books queryBookById(int id);
        //查詢全部Book,返回list集合
        List<Books> queryAllBook();
    }
    

    實現類:

    package com.stone.service;
    import com.stone.dao.BookMapper;
    import com.stone.pojo.Books;
    import java.util.List;
    public class BookServiceImpl implements BookService {
        //呼叫dao層的操作,設定一個set介面,方便Spring管理
        private 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 books) {
            return bookMapper.updateBook(books);
        }
        public Books queryBookById(int id) {
            return bookMapper.queryBookById(id);
        }
        public List<Books> queryAllBook() {
            return bookMapper.queryAllBook();
        }
    }
    

OK,到此,底層需求操作編寫完畢!

7.5、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.stone.dao"/>
        </bean>
    </beans>
    
  3. Spring整合service層

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

Spring層搞定!再次理解一下,Spring就是一個大雜燴,一個容器!對吧!

7.6、SpringMVC層

  1. web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <!--DispatcherServlet-->
        <servlet>
            <servlet-name>DispatcherServlet</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>DispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
        <!--encodingFilter-->
        <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>
    </web-app>
    
  2. spring-mvc.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
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">
        <!-- 配置SpringMVC -->
        <!-- 1.開啟SpringMVC註解驅動 -->
        <mvc:annotation-driven />
        <!-- 2.靜態資源預設servlet配置-->
        <mvc:default-servlet-handler/>
        <!-- 3.配置jsp 顯示ViewResolver檢視解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
            <property name="prefix" value="/WEB-INF/jsp/" />
            <property name="suffix" value=".jsp" />
        </bean>
        <!-- 4.掃描web相關的bean -->
        <context:component-scan base-package="com.stone.controller" />
    </beans>
    
  3. Spring配置整合檔案,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="spring-mvc.xml"/>
    </beans>
    

配置檔案,暫時結束!Controller 和 檢視層編寫

  1. BookController 類編寫 , 方法一:查詢全部書籍

    @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";
        }
    }
    
  2. 編寫首頁 index.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
    <!DOCTYPE HTML>
    <html>
    <head>
        <title>首頁</title>
        <style type="text/css">
            a {
                text-decoration: none;
                color: black;
                font-size: 18px;
            }
            h3 {
                width: 180px;
                height: 38px;
                margin: 100px auto;
                text-align: center;
                line-height: 38px;
                background: deepskyblue;
                border-radius: 4px;
            }
        </style>
    </head>
    <body>
    <h3>
        <a href="${pageContext.request.contextPath}/book/allBook">點選進入列表頁</a>
    </h3>
    </body>
    </html>
    
  3. 書籍列表頁面 allbook.jsp

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>書籍列表</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <!-- 引入 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 column">
                <div class="page-header">
                    <h1>
                        <small>書籍列表 —— 顯示所有書籍</small>
                    </h1>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-md-4 column">
                <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增</a>
            </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="${requestScope.get('list')}">
                        <tr>
                            <td>${book.getBookID()}</td>
                            <td>${book.getBookName()}</td>
                            <td>${book.getBookCounts()}</td>
                            <td>${book.getDetail()}</td>
                            <td>
                                <a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.getBookID()}">更改</a> |
                                <a href="${pageContext.request.contextPath}/book/del/${book.getBookID()}">刪除</a>
                            </td>
                        </tr>
                    </c:forEach>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    
  4. BookController 類編寫 , 方法二:新增書籍

    @RequestMapping("/toAddBook")
    public String toAddPaper() {
        return "addBook";
    }
    @RequestMapping("/addBook")
    public String addPaper(Books books) {
        System.out.println(books);
        bookService.addBook(books);
        return "redirect:/book/allBook";
    }
    
  5. 新增書籍頁面:addBook.jsp

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>新增書籍</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <!-- 引入 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 column">
                <div class="page-header">
                    <h1>
                        <small>新增書籍</small>
                    </h1>
                </div>
            </div>
        </div>
        <form action="${pageContext.request.contextPath}/book/addBook" method="post">
            書籍名稱:<input type="text" name="bookName"><br><br><br>
            書籍數量:<input type="text" name="bookCounts"><br><br><br>
            書籍詳情:<input type="text" name="detail"><br><br><br>
            <input type="submit" value="新增">
        </form>
    </div>
    
  6. BookController 類編寫 , 方法三:修改書籍

    @RequestMapping("/toUpdateBook")
    public String toUpdateBook(Model model, int id) {
        Books books = bookService.queryBookById(id);
        System.out.println(books);
        model.addAttribute("book",books );
        return "updateBook";
    }
    @RequestMapping("/updateBook")
    public String updateBook(Model model, Books book) {
        System.out.println(book);
        bookService.updateBook(book);
        Books books = bookService.queryBookById(book.getBookID());
        model.addAttribute("books", books);
        return "redirect:/book/allBook";
    }
    
  7. 修改書籍頁面 updateBook.jsp

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>修改資訊</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <!-- 引入 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 column">
                <div class="page-header">
                    <h1>
                        <small>修改資訊</small>
                    </h1>
                </div>
            </div>
        </div>
        <form action="${pageContext.request.contextPath}/book/updateBook" method="post">
            <input type="hidden" name="bookID" value="${book.getBookID()}"/>
            書籍名稱:<input type="text" name="bookName" value="${book.getBookName()}"/>
            書籍數量:<input type="text" name="bookCounts" value="${book.getBookCounts()}"/>
            書籍詳情:<input type="text" name="detail" value="${book.getDetail() }"/>
            <input type="submit" value="提交"/>
        </form>
    </div>
    
  8. BookController 類編寫 , 方法四:刪除書籍

    @RequestMapping("/del/{bookId}")
    public String deleteBook(@PathVariable("bookId") int id) {
        bookService.deleteBookById(id);
        return "redirect:/book/allBook";
    }
    

配置Tomcat,進行執行!

到目前為止,這個SSM專案整合已經完全的OK了,可以直接執行進行測試!這個練習十分的重要,大家需要保證,不看任何東西,自己也可以完整的實現出來!

專案結構圖

img

7.7、小結及展望

SSM框架的重要程度是不言而喻的,學到這裡,大家已經可以進行基本網站的單獨開發。但是這只是增刪改查的基本操作。可以說學到這裡,大家才算是真正的步入了後臺開發的門。也就是能找一個後臺相關工作的底線。

或許很多人,工作就做這些事情,但是對於個人的提高來說,還遠遠不夠!

我們後面還要學習一些 SpringMVC 的知識!

  • Ajax 和 Json
  • 檔案上傳和下載
  • 攔截器

SpringBoot、SpringCloud開發!

7.8、實現查詢書籍功能

  1. 前端頁面增加一個輸入框和查詢按鈕

    <div class="col-md-4 column"></div>
    <div class="col-md-4 column">
        <form class="form-inline" action="/book/queryBook" method="post" style="float: right">
            <input type="text" name="queryBookName" class="form-control" placeholder="輸入查詢書名" required>
            <input type="submit" value="查詢" class="btn btn-primary">
        </form>
    </div>
    
  2. 編寫查詢的Controller

    @RequestMapping("/queryBook")
    public String queryBook(String queryBookName,Model model){
        System.out.println("要查詢的書籍:"+queryBookName);
        //業務邏輯還沒有寫
        return "allBook";
    }
    
  3. 由於底層沒有實現,所以我們要將底層程式碼先搞定

  4. Mapper介面

    //根據id查詢,返回一個Book
    Books queryBookByName(String bookName);
    
  5. Mapper.xml

    <!--根據書名查詢,返回一個Book-->
    <select id="queryBookByName" resultType="Books">
        select * from ssmbuild.books
        where bookName = #{bookName}
    </select>
    
  6. Service介面

    //根據id查詢,返回一個Book
    Books queryBookByName(String bookName);
    
  7. Service實現類

    public Books queryBookByName(String bookName) {
        return bookMapper.queryBookByName(bookName);
    }
    
  8. 完善Controller

    @RequestMapping("/queryBook")
    public String queryBook(String queryBookName,Model model){
        System.out.println("要查詢的書籍:"+queryBookName);
        Books books = bookService.queryBookByName(queryBookName);
        List<Books> list = new ArrayList<Books>();
        list.add(books);
        model.addAttribute("list", list);
        return "allBook";
    }
    
  9. 測試,查詢功能OK!

  10. 無聊最佳化!我們發現查詢的東西不存在的時候,查出來的頁面是空的,我們可以提高一下使用者的體驗性!

    1. 在前端新增一個展示全部書籍的按鈕

img

2. 並在後臺增加一些判斷性的程式碼!
   ```java
   @RequestMapping("/queryBook")
   public String queryBook(String queryBookName,Model model){
       System.out.println("要查詢的書籍:"+queryBookName);
       //如果查詢的資料存在空格,則最佳化
       Books books = bookService.queryBookByName(queryBookName.trim()); 
       List<Books> list = new ArrayList<Books>();
       list.add(books);
       //如果沒有查出來書籍,則返回全部書籍列表
       if (books==null){
           list = bookService.queryAllBook();
           model.addAttribute("error", "沒有找到本書!");
       }
       model.addAttribute("list", list);
       return "allBook";
   }
   ```
3. 將錯誤資訊展示在前臺頁面!完整的查詢欄程式碼
   ```jsp
   <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="/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="輸入查詢書名" required>
               <input type="submit" value="查詢" class="btn btn-primary">
           </form>
       </div>
   </div>

相關文章