12.SSM框架集~SpringMVC

12.SSM框架集~SpringMVC

本文是上一篇文章的後續,詳情點擊該連接

SpringMVC

       在學習了Spring之後,基於MVC設計模式的項目,我們可以使用Mybatis將數據庫替換,使用Spring將Controller層和Service層,以及Service層和數據庫層之間進行解耦。但是基於MVC的模式中,在Controller層中的Servlet爲請求的代碼入口。tomcat服務器在接受到請求後,會根據請求地址自定調用對應的servlet的service方法完成請求處理。

       但是此時此刻我們還是會發現一個問題。第一就是每個功能都要聲明對應的Servlet,而且在Servlet中獲取請求數據比較麻煩,響應的方式的代碼其實只想聲明對應的響應數據。

       項目只聲明一個Servlet,該Servlet作爲項目請求的公共入口。並且在該Servlet必須聲明代碼,此代碼根據請求地址調用對應的邏輯代碼處理請求。如果將邏輯方法全部聲明在Servlet中造成代碼的體系結構不清晰,將邏輯方法單獨聲明到邏輯類中(Controller類)。然後Servlet中根據請求動態的調用對應的邏輯類中的邏輯方法處理請求即可。

如何在Servlet中獲取邏輯類對象呢?

       使用Spring容器的子容器,在子容器中存儲所有的Controller的實例化對象,然後Servlet一次性從子容器中獲取所有的對象即可。在init方法中實現即可。

如何在Servlet中根據請求動態調用對象的邏輯方法呢

       反射+註解

在這裏插入圖片描述在這裏插入圖片描述

       其本質就是將Servlet進行了封裝,提供一個公共的Servlet。該Servlet可以根據請求動態的調用對應的邏輯方法完成請求處理。提升開發效率。

[1] 使用Maven創建war類型項目並配置SpringMVC的依賴

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>SpringMVC</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <!--配置版本號-->
  <properties>
    <servlet-version>3.1.0</servlet-version>
    <jsp-version>2.2</jsp-version>
    <jstl-version>1.2</jstl-version>
  </properties>


  <!--配置依賴-->
  <dependencies>
    <!--配置SpringMVC的依賴-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.11.RELEASE</version>
    </dependency>
    <!--配置web的相關依賴-->
    <!--servlet的資源座標-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>${servlet-version}</version>
      <scope>provided</scope>
    </dependency>
    <!--jsp的資源座標-->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>${jsp-version}</version>
      <scope>provided</scope>
    </dependency>
    <!--jstl的資源座標-->
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>${jstl-version}</version>
    </dependency>

  </dependencies>

  <!--配置tomcat插件-->
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <port>8080</port><!--配置tomcat啓動的端口號-->
          <path>/mvc</path><!--配置項目的訪問名稱-->
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

配置SpringMVC容器對象的配置文件加載路徑

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

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--配置SpringMVC容器對象的配置文件加載路徑-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--服務器啓動即完成DispatcherServlet的初始化創建-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvc</servlet-name>
    <url-pattern>/</url-pattern><!--攔截除Jsp以外的所有請求-->
  </servlet-mapping>
</web-app>

在resources下創建並配置springmvc.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
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        ">
    <!--配置註解掃描路徑-->
    <context:component-scan base-package="com.alvin.controller"></context:component-scan>
    <!--配置註解解析器-->
    <mvc:annotation-driven></mvc:annotation-driven>


</beans>

在Controller包下創建控制器類並聲明單元方法

@Controller
public class MyController {
    //聲明邏輯方法:給DispatcherServlet使用,必須按照SpringMVC的方式來聲明邏輯方法
    @RequestMapping("alvin")
    public String demo(){
        System.out.println("我是控制器的使用方法");
        return "hello world";
    }
}

在這裏插入圖片描述在這裏插入圖片描述

       在學習了SpringMVC的基本使用流程後,發現SpringMVC將Servlet進行了封裝,在外部聲明控制器類,並在其中聲明單元方法。DispatcherServlet根據請求動態的調用對應的單元方法處理請求,所以我們需要在單元方法中聲明請求處理的邏輯代碼。而請求的處理需要獲取本次請求的請求數據,那麼在單元方法中如何獲取請求數據呢?

       請求被tomcat接受後會調用DispatcherServlet處理請求,Tomcat會將封裝了此次請求數據的request對象作爲實參傳遞給DispatcherServlet的service方法,而service方法中又會根據請求調用對應的單元方法處理請求,所以只需要在service方法中將請求數據作爲實參傳遞給單元方法使用即可。注意,單元方法必須聲明對應的形參接收數據。

緊耦方式

       DispatcherServlet中的service方法直接將此次請求的request對象傳遞給調用的單元方法即可。同時在單元方法上聲明形參HttpServletRequest來接收request實參即可。

       DispatcherServlet在其service方法中將請求數據根據需求從request對象中獲取出來後,將數據直接傳遞給對應的單元方法使用。同時在單元方法上直接聲明對應的形參接收請求數據即可。

緊耦方式(request)在單元方法中獲取請求數據

       在控制器類中聲明請求處理單元方法,並在單元方法上聲明形參,形參類型爲 HttpServletRequest,接收DispactherServlet傳遞的封裝了此次請求的請求數據的 request對象。

       在單元方法中使用request.getParameter(“鍵名”)獲取請求數據

       在單元方法中聲明請求處理的邏輯代碼

    @RequestMapping("alvinreq")
    public String getDataByRequest(HttpServletRequest request){
        //獲取請求數據
        String uname = request.getParameter("uname");
        int age = Integer.parseInt(request.getParameter("age"));
        //處理請求
        System.out.println("緊耦合方式獲取請求數據(request對象):" + uname + " " + age);
        //響應
        return "hello world";
    }

       單元方法都是由DispatcherServlet根據請求來調用,由DispatcherServlet來傳入實參數據給單元方法使用

解耦方式獲取請求數據

       讓DispatcherServlet將請求數據獲取後傳遞給單元方法,但是請求數據的獲取需要數據的鍵名,而DispatcherServlet不是我們自己聲明的無法修改其底層代碼,怎麼將請求數據的鍵名告訴給DispatcherServlet呢?

       在單元方法上聲明形參來接收請求數據時,形參名必須和請求數據的鍵名一致,DispatcherServlet會將調用單元方法的形參名作爲請求數據的鍵名獲取請求數據,然後傳遞給單元方法。

@RequestMapping("reqData")
    public String getDataByArgName(String name,int age){
        //處理請求
        System.out.println("解耦方式形參名爲鍵名獲取請求數據: " + name + " " + age);
        //響應結果
        return "hello world";
    }

       如果形參名和請求數據的鍵名不一致,不會報錯,傳入null。

       如果請求數據的類型和後臺單元方法的形參的類型不匹配,則會報400異常

       如果形參類型爲基本類型,則如果請求中沒有對應的請求數據,可能會出現數據類型轉換異常,比如:將null轉換爲int,建議將形參都聲明爲包裝類的類型

形參名和請求數據的鍵名不一致怎麼辦?

       在單元方法上的形參聲明中使用註解@RequestParam來實現。

    @RequestMapping("getDataByArg")
    public String getDataByArg(@RequestParam(value = "name") String uname, Integer age){
        //處理請求
        System.out.println("解耦方式獲取請求數據,形參名和請求數據的鍵名不一致: " + uname + " " + age);
        //響應結果
        return "hello world";
    }

       DispatcherServlet默認是使用單元方法的形參名即爲請求數據的鍵名來獲取請求數據的

       那麼如果形參名和請求數據的鍵名不一致, 則在單元方法上的形參前使用註解

       @RequestParam來指明請求數據的鍵名

       value屬性:聲明請求數據的鍵名,如果只有value時,value可以省略不寫

       required:true|false,設置爲true表示請求中必須攜帶鍵名爲指定鍵名的請求數據,否則400

       defaultValue:默認值,如果請求中沒有對應的請求數據,則使用默認值,不可以和required一起使用

使用實體類對象獲取請求數據

       在學習了使用SpringMVC後,我們可以在單元方法上聲明形參直接獲取請求數據只要形參名和請求數據的鍵名一致即可。但是如果我們的請求數據過多,總不能咱們聲明N個形參來接收請求數據吧?而且按照我們以往的開發經驗,請求數據過多我們會將請求封裝到實體類對象中進行使用,保證數據的完整性。那麼,在SpringMVC中一旦請求數據過多,如何在單元方法上獲取請求數據呢?

       我們希望在單元方法中直接獲取一個封裝好請求數據的實體類對象使用,美滋滋。也就說我們希望DispatcherServlet可以將請求數據封裝到實體類對象中,然後將實體類對象作爲實參傳遞給單元方法使用。在單元方法上聲明對應的實體類的形參類型,來接收DispatcherServlet傳遞的封裝了請求數據的實體類對象,以及告訴DispatcherServlet使用哪個實體類來封裝請求數據。而且,要求實體類的屬性名必須和請求數據的鍵名一致,DispatcherServlet會按照該方式將請求數據賦值給實體類的屬性。

public class User {
    private Integer uid;
    private String uname;
    private Integer age;
}//get set不在這裏寫
    @RequestMapping("getDataByObject")
    public String getDataByObject(User user){
        //處理請求
        System.out.println("解耦方式:使用實體類獲取請求數據: " + user);
        //響應結果
        return "hello world";
    }

獲取同鍵不同值的請求數據

       目前我們在單元方法上可以使用形參或者實體類來接收請求數據,美滋滋。但是有某些請求中,請求數據是同鍵不同值的。比如,在頁面中的多項選擇的請求數據,愛好,fav=1&fav=2&fav3.像這樣的請求數據,如何獲取呢?

       我們自己使用Request對象獲取同鍵不同值的數據,使用 req.ParameterValues(“鍵名”),返回值是String[]數組。在單元方法上聲明形 參,類型爲String[]數組類型,要求形參名和請求數據的鍵名一致即可。

    @RequestMapping("getDataBykey")
    public String getDataBykeyValues(String[] fav){
        //處理請求
        System.out.println("解耦方式:獲取同鍵不同值的請求數據 " + fav[0]);
        //響應結果
        return "hello world";
    }

混合使用緊耦和解耦方式獲取請求數據

       目前我們可以在單元方法中使用形參,實體類,request對象方式獲取請求數據,但是如果請求中的數據,一部分要放入到對應的實體類中,一部分要使用形參直接獲取怎麼辦?

       我們可以在單元方法上根據自己的需求來聲明形參獲取請求數據, DispatcherServlet會想盡一切辦法給我們聲明的單元方法的形參賦值。那麼如果沒有賦值,則表明形參聲明有問題。我們的獲取請求數據的方式可以混合使用。

    @RequestMapping("getData")
    public String getDataBy(User user,@RequestParam("name") String uname,int age,String[]fav,HttpServletRequest request){
        //處理請求
        System.out.println("實體類: " + user);
        System.out.println("形參名:" + uname + " " + age);
        System.out.println("同鍵不同值: " + fav);
        System.out.println("request對象的: " + request.getParameter("name"));
        //響應結果
        return "hello world";
    }

SpringMVC對restful請求的支持

restful格式請求的介紹

       目前我們瀏覽器發起請求給服務器的時候,一般我們常用的請求方式有兩個,get 和post方式。不管get還是post方式,請求數據的格式都是鍵值對格式,get 方式請求數據是在請求地址上以問號隔開的形式,拼接在請求地址後,post請求 呢是有專門的請求實體的,例如:

get方式請求:

       localhost:8080/project/aa?uname=zhangsan&age=18

post方式請求:

       地址:localhost:8080/project/aa

       要求我們後臺獲取請求數據的代碼,必須按照指定的鍵名來獲取請求數據。鍵名 就是請求數據的鍵名。這樣造成,一旦請求數據的鍵名發生變更,造成後臺的邏輯 代碼也需要進行變更。前臺的請求數據的鍵名和後臺的邏輯代碼之間的耦合性過 高,造成前臺和後臺的開發過程中相互依賴性過高。怎麼辦?

       讓前臺和後臺代碼之間進行解耦。也就說不再讓請求數據的鍵名造成前後臺代碼之間耦合性高。前臺請求數據的鍵名發生變更,不影響後臺邏輯代碼的正常執行

       請求數據不再以鍵值對的形式發送給後臺使用。直接發送數據本身給後臺即可。既然請求數據不再使用鍵值對,請求數據必須按照指定的格式來進行發送。使用restful格式。

       傳統的get方式請求格式:

              localhost:8080/project/aa?uname=zhangsan&age18

       restful格式

              localhost:8080/project/aa/zhangsan/18

restful格式要求請求數據作爲請求地址的一部分發送給後臺使用

SpringMVC使用佔位{字符}聲明公共單元方法

    @RequestMapping("aa/{bb}/{cc}")
    public String testRestFul(){
        //處理請求
        System.out.println("處理restful格式的單元方法");
        //響應結果
        return "/alvin";
    }

       SpringMVC的單元方法支持模糊匹配的聲明,可以聲明一個單元方法處理N個請求

       因爲不同的請求,有可能使用相同的處理邏輯。

@RequestMapping("aa/{bb}/{cc}")

       以aa開頭,並且單元方法的路徑爲三層路徑的,第二層和第三層路徑爲任意字符的請求地址都會

       調用該單元方法處理請求,比如:aa/asda/asd,aa/asdas/asda

@RequestMapping("{bb}/{cc}")

       單元方法的路徑有兩層,並且每層爲任意字符。

@RequestMapping("{bb}")

       單元方法的路徑有一層,並且爲任意字符。

單元方法中獲取restful請求地址中的請求數據

@RequestMapping("reg/{name}/{age}/{fav}")
public String testGetDataRestFul(@PathVariable("name") String uname, @PathVariable Integer age, @PathVariable String fav,String course){
    //處理請求
    System.out.println(uname+":"+age+":"+fav);
    System.out.println("鍵值對數據:"+course);
    //響應結果
    return "/alvin";
}

        restful只是一種請求數據攜帶的格式,它只是表明將請求數據作爲請求地址的一部分發送給後臺

       和請求方式沒有關係,請求地址可以是get方式也可以是post方式來發送。並且restful格式本身

       仍然可以使用鍵值對攜帶數據.並且仍然按照解耦方式獲取即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章