01-SpringMVC、三層架構、組件、請求參數綁定、常用注解


一、SpringMVC的基本概念
1.1、關於三層架構和MVC
1.1.1、三層架構
開發架構一般都是基於兩種形式,一種是 C/S 架構,也就是客戶端/服務器,另一種是 B/S 架構,也就 是瀏覽器服務器。在 JavaEE 開發中,幾乎全都是基於 B/S架構的開發。那么在 B/S架構中,系統標准的三層架構 包括:表現層、業務層、持久層。三層架構在實際開發中使用的非常多。

三層架構中:

表現層: 也就是我們常說的web層。它負責接收客戶端請求,向客戶端響應結果,通常客戶端使用http協議請求 web 層,web 需要接收 http 請求,完成 http 響應。表現層包括展示層和控制層:控制層負責接收請求,展示層負責結果的展示。 表現層依賴業務層,接收到客戶端請求一般會調用業務層進行業務處理,並將處理結果響應給客戶端。 表現層的設計一般都使用 MVC 模型。(MVC 是表現層的設計模型,和其他層沒有關系) 

業務層: 也就是我們常說的 service 層。它負責業務邏輯處理,和我們開發項目的需求息息相關。web 層依賴業務層,但是業務層不依賴 web 層。 業務層在業務處理時可能會依賴持久層,如果要對數據持久化需要保證事務一致性。(也就是我們說的, 事務應該放到業務層來控制)。

持久層: 也就是我們是常說的 dao 層。負責數據持久化,包括數據層即數據庫和數據訪問層,數據庫是對數據進行持久化的載體,數據訪問層是業務層和持久層交互的接口,業務層需要通過數據訪問層將數據持久化到數據庫中。通俗的講,持久層就是和數據庫交互,對數據庫表進行增刪改查的。 

1.1.2、MVC模型
MVC 全名是 Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫, 是一種用於設計創建 Web 應用程序表現層的模式。MVC 中每個部分各司其職: 
Model(模型):   
  通常指的就是我們的數據模型。作用一般情況下用於封裝數據。 
View(視圖):   
  通常指的就是我們的 jsp 或者 html。作用一般就是展示數據的。通常視圖是依據模型數據創建的。 
Controller(控制器):   
  是應用程序中處理用戶交互的部分。作用一般就是處理程序邏輯的。
  它相對於前兩個不是很好理解,這里舉個例子:
  例如:   
    我們要保存一個用戶的信息,該用戶信息中包含了姓名,性別,年齡等等。   
    這時候表單輸入要求年齡必須是 1~100 之間的整數。姓名和性別不能為空。並且把數據填充到模型之中。   
    此時除了 js 的校驗之外,服務器端也應該有數據准確性的校驗,那么校驗就是控制器的該做的。   
    當校驗失敗后,由控制器負責把錯誤頁面展示給使用者。
    如果校驗成功,也是控制器負責把數據填充到模型,並且調用業務層實現完整的業務需求。

1.2、SpringMVC概述
1.2.1、SpringMVC

SpringMVC 是一種基於 Java 的實現 MVC 設計模型的請求驅動類型的輕量級 Web 框架,屬於 Spring FrameWork 的后續產品,已經融合在 Spring Web Flow 里面。Spring 框架提供了構建 Web 應用程序的全功能 MVC 模塊。使用 Spring 可插入的 MVC 架構,從而在使用 Spring 進行 WEB 開發時,可以選擇使用 Spring 的 Spring MVC 框架或集成其他 MVC 開發框架,如 Struts1(現在一般不用),Struts2 等。
SpringMVC 已經成為目前最主流的 MVC 框架之一,並且隨着 Spring3.0 的發布,全面超越 Struts2,成 為最優秀的 MVC 框架。 
它通過一套注解,讓一個簡單的 Java 類成為處理請求的控制器,而無須實現任何接口。同時它還支持 RESTful 編程風格的請求。



1.2.2、SpringMVC在三層架構的位置

1.2.3、SpringMVC優勢
1、清晰的角色划分: 

  前端控制器(DispatcherServlet)  
  請求到處理器映射(HandlerMapping)  
  處理器適配器(HandlerAdapter)  
  視圖解析器(ViewResolver)  
  處理器或頁面控制器(Controller)  
  驗證器( Validator)  
  命令對象(Command  請求參數綁定到的對象就叫命令對象)  
  表單對象(Form Object 提供給表單展示和提交到的對象就叫表單對象)

2、分工明確,而且擴展點相當靈活,可以很容易擴展,雖然幾乎不需要。
3、由於命令對象就是一個 POJO,無需繼承框架特定 API,可以使用命令對象直接作為業務對象。
4、和 Spring 其他框架無縫集成,是其它 Web 框架所不具備的。
5、可適配,通過 HandlerAdapter 可以支持任意的類作為處理器。
6、可定制性,HandlerMapping、ViewResolver 等能夠非常簡單的定制。
7、功能強大的數據驗證、格式化、綁定機制。
8、利用 Spring 提供的 Mock 對象能夠非常簡單的進行 Web 層單元測試。
9、本地化、主題的解析的支持,使我們更容易進行國際化和主題的切換。
10、強大的 JSP 標簽庫,使 JSP 編寫更容易。
…還有比如RESTful風格的支持、簡單的文件上傳、約定大於配置的契約式編程支持、基於注解的零配置支持等等。

1.2.4、SpringMVC和Struct2的優劣分析
共同點: 
  它們都是表現層框架,都是基於 MVC 模型編寫的。 
  它們的底層都離不開原始 ServletAPI。 
  它們處理請求的機制都是一個核心控制器。 
區別: 
  Spring MVC 的入口是 Servlet。而 Struts2 是 Filter 。
  Spring MVC 是基於方法設計的(是單例的),而 Struts2 是基於類,Struts2 每次執行都會創建一個動作類(是多例的,每次請求創建一個實例對象)。所 以 Spring MVC 會稍微比 Struts2 快些。 
  Spring MVC 使用更加簡潔,同時還支持 JSR303, 處理 ajax 的請求更方便 
  --->(JSR303 是一套 JavaBean 參數校驗的標准,它定義了很多常用的校驗注解,
  我們可以直接將這些注解加在我們 JavaBean 的屬性上面,就可以在需要校驗的時候進行校驗了。) 
  Struts2 的 OGNL 表達式使頁面的開發效率相比 Spring MVC 更高些,但執行效率並沒有比 JSTL 提升,尤其是 struts2 的表單標簽,遠沒有 html 執行效率高。 

二、SpringMVC
2.1、SpringMVC案例

2.1.1、前期准備
解決maven項目創建過慢問題:
創建maven項目時,添加如下鍵值:

  archetypeCatalog
  internal

2.1.2、引入jar包

<?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>com.itheima</groupId>
  <artifactId>day01_01start</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>day01_01start Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <!--版本鎖定-->
    <spring.version>5.0.2.RELEASE</spring.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>day01_01start</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>
pom.xml

2.1.3、配置核心控制器--一個Servlet

<!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>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <!--加載springmvc.xml配置文件。啟動服務器加載-->
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.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>
  
  <!--配置中文過濾器-->
  <filter>
    <filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

</web-app>
web.xml

2.1.4、創建springmvc的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

  <!--開啟注解掃描-->
  <context:component-scan base-package="cn.itcast"></context:component-scan>
  
  <!--視圖解析器對象-->
  <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages"></property>
    <property name="suffix" value=".jsp"></property>
  </bean>
  
  <!--開啟SpringMVC框架注解的支持-->
  <mvc:annotation-driven/>
</beans>
springmvc.xml

2.1.5、編寫控制器並使用注解配置

/**
 * 控制器類
 */
@Controller
//@RequestMapping(path="/user")
public class HelloController {
    
    //入門案例
    @RequestMapping(path="/hello")
    public String sayHello(){
        System.out.println("Hello StringMVC");
        return "success";
    }
}
HelloController


2.2、執行過程及原理分析
2.2.1、案例的執行過程

1、啟動服務器,加載應用的web.xml。
   在web.xml中配置了前端控制器:

<!--配置前端控制器-->
<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <!--加載springmvc.xml配置文件。啟動服務器加載-->
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.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>
web.xml

附注:  
  DispatcherServlet前端控制器負責接受請求,轉發請求,不對業務進行處理。
  DispatcherServlet是一個servlet,負責接受http;可以在web.xml中配置spring-mvc.xml,用於加載配置信息。
  如果沒有上下文會創建一個新的上下文。
2、實例化並初始化servlet

<load-on-startup>1</load-on-startup>

  原本該servlet在第一請求才會被創建為對象。配置上述標簽,則在服務器啟動時創建DispatcherServlet對象

3、加載springmvc配置文件創建spring容器,根據配置初始化容器中的對象。

<!--開啟注解掃描-->
<context:component-scan base-package="cn.itcast"/>

<!-- 視圖解析器對象 -->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".jsp"/>
</bean>
  
<!--開啟SpringMVC框架注解的支持-->
<mvc:annotation-driven/>
springmvc.xml

<context:component-scan base-package="cn.payn"/>  可以在該標簽中設置掃描的范圍,如下:

<!--開啟注解掃描,只掃描Controller注解-->
<context:component-scan base-package="cn.payn">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

開啟注解掃描,掃描包“cn.payn”下的組件。HelloController類變成Bean對象,加載進Spring的IoC容器中。InternalResourceViewResolver視圖解析器的Bean對象。

4、客戶端發送請求:hello
5、請求到達前端控制器
6、到達前端控制器。截取請求的動名稱hello。並從RequestMappiing中找。
7、找到后,執行控制器中的方法。
8、方法有返回值,根據返回值通過InternalResourceViewResolver(視圖解析器)找到響應結果視圖。
9、響應瀏覽器,展示結果。

更加詳細的流程:


2.3、涉及到的組件
2.3.1、DispatcherServlet:前端控制器
用戶請求到達前端控制器,它就相當於 mvc 模式中的 c,dispatcherServlet 是整個流程控制的中心,由 它調用其它組件處理用戶的請求,dispatcherServlet 的存在降低了組件之間的耦合性。 

2.3.2、HandlerMapping:處理映射器
HandlerMapping 負責根據用戶請求找到 Handler 即處理器,SpringMVC 提供了不同的映射器實現不同的 映射方式,例如:配置文件方式,實現接口方式,注解方式等。 

2.3.3、Handler:處理器
它就是我們開發中要編寫的具體業務控制器。由 DispatcherServlet 把用戶請求轉發到 Handler。由 Handler 對具體的用戶請求進行處理。 

2.3.4、處理器適配器
通過 HandlerAdapter 對處理器進行執行,這是適配器模式的應用,通過擴展適配器可以對更多類型的處理 器進行執行。

2.3.5、ViewResolver:視圖解析器
View Resolver 負責將處理結果生成 View 視圖,View Resolver 首先根據邏輯視圖名解析成物理視圖名 即具體的頁面地址,再生成 View 視圖對象,最后對 View 進行渲染將處理結果通過頁面展示給用戶。 

2.3.6、View視圖
SpringMVC 框架提供了很多的 View 視圖類型的支持,包括:jstlView、freemarkerView、pdfView 等。我們最常用的視圖就是 jsp。
一般情況下需要通過頁面標簽或頁面模版技術將模型數據通過頁面展示給用戶,需要由程序員根據業務需求開發具體的頁面。

2.3.7、<mvc:annotation-driven/>說明
在 SpringMVC 的各個組件中,處理器映射器HandlerMapping、處理器適配器HandlerAdapter、視圖解析器ViewResolver稱為 SpringMVC 的三大組件。 
使用<mvc:annotation-driven> 自動加載 RequestMappingHandlerMapping (處理映射器)和 RequestMappingHandlerAdapter ( 處理適配器 ),可用在SpringMVC.xml 配置文件中使用 <mvc:annotation-driven>替代注解處理器和適配器的配置。 
它就相當於在 xml 中配置了:

<!-- 上面的標簽相當於如下配置--> 
<!-- Begin --> 
<!-- HandlerMapping --> 
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean> 
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean> 

<!-- HandlerAdapter --> 
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerA dapter"></bean> 
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean> 
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean> 

<!-- HadnlerExceptionResolvers --> 
<bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExcept ionResolver"></bean> 
<bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolv er"></bean> 
<bean class="org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver" ></bean> 
<!-- End --> 
View Code


2.4、@RequestMapping

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {

  用於建立請求 URL 和處理請求方法之間的對應關系。 
類上:   
  請求 URL 的第一級訪問目錄。此處不寫的話,就相當於應用的根目錄。寫的話需要以/開頭。   
  它出現的目的是為了使我們的 URL 可以按照模塊化管理: 
  例如:
    賬戶模塊:
      /account/add
      /account/update 
      /account/delete ... 
    訂單模塊:
      /order/add
      /order/update
      /order/delete
屬性:
  value:用於指定請求的 URL。它和 path 屬性的作用是一樣的。 
  method:用於指定請求的方式。 
  params:用於指定限制請求參數的條件。它支持簡單的表達式。要求請求參數的 key 和 value 必須和配置的一模一樣。 
    例如:     
      params = {"accountName"},表示請求參數必須有 accountName     
      params = {"moeny!100"},表示請求參數中 money 不能是 100。 
  headers:用於指定限制請求消息頭的條件。 --->headers = {"Accept"}  即請求頭必須帶有 Accept
注意:以上四個屬性只要出現 2 個或以上時,他們的關系是與的關系。 

2.4.2、使用示例
2.4.2.1、出現位置的示例

/**
 * 控制器類
 */
@Controller
@RequestMapping(path="/user")
public class HelloController {
    //入門案例
    @RequestMapping(path="/hello")
    public String sayHello(){
        System.out.println("Hello StringMVC");
        return "success";
    }

    /**
     * RequestMapping注解
     * @return
     */
    @RequestMapping(value="/testRequestMapping")
    public String testRequestMapping(){
        System.out.println("測試RequestMapping注解...");
        return "success";
    }

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

    <h3>入門程序</h3>

    <%--相對路徑的寫法--%>
    <%--<a href="hello">入門程序</a>--%>

    <%--第一種訪問方式  ${pageContext.request.contextPath}即/day01_01start --%>
    <a href="${pageContext.request.contextPath}/user/testRequestMapping">RequestMapping注解  訪問一</a>
    
    <%--注意:加上/表示絕對路徑--%>
    <a href="user/testRequestMapping">RequestMapping注解  訪問二</a>

</body>
</html>
index.jsp

注意:當我們使用此種方式配置時,在 jsp中第二種寫法時,不要在訪問 URL 前面加 /,否則無法找到資源。 

2.4.2.2、mothod屬性
方法上設置:@RequestMapping(value="/saveAccount",method=RequestMethod.POST
注意:當使用 get 請求時,提示錯誤信息是 405,信息是方法不支持 get 方式請求 。


2.4.2.3、params屬性的示例
@RequestMapping(value="/removeAccount",params= {"accountName","money>100"}) 
"money>100"-->是一個字符串、鍵。
<!-- 請求參數的示例 -->
<a href="account/removeAccount?accountName=aaa&money>100">刪除賬戶,金額 100</a>
<br/>
<a href="account/removeAccount?accountName=aaa&money>150">刪除賬戶,金額 150</a>
注意:  當我們點擊第一個超鏈接時,可以訪問成功。  當我們點擊第二個超鏈接時,無法訪問。如下圖: 


三、請求參數的綁定
3.1、綁定說明
3.1.1、綁定的機制
表單中請求參數都是基於 key=value 的。
SpringMVC 綁定請求參數的過程是通過把表單提交請求參數,作為控制器中方法參數進行綁定的。
例如:
  <a href="param/testParam?username=payn&password=123">請求參數綁定</a>
  中的請求參數:
    username=payn&password =123

@Controller
@RequestMapping("/param")
public class ParamController {

    /**請求參數綁定入門
     * @param username
     * @param password
     * @return
     */
    @RequestMapping("/testParam")
    public String testParam(String username,String password){
        System.out.println("執行了。。。");
        System.out.println("用戶名:"+username);
        System.out.println("密碼:"+password);
        return "success";
    }
    
    
}
ParamController

3.1.2、支持的數據類型
基本類型參數: 
  包括基本類型和 String 類型
POJO (Plain Ordinary Java Object)類型參數:   
  包括實體類,以及關聯的實體類
數組和集合類型參數:   
  包括 List 結構和 Map 結構的集合(包括數組) 
SpringMVC 綁定請求參數是自動實現的,但是要想使用,必須遵循使用要求。

3.1.3、使用要求
如果是基本類型或者 String類型: 
  要求參數名稱必須和控制器中方法的形參名稱保持一致。(嚴格區分大小寫)
如果是 POJO類型,或者它的關聯對象: 
  要求表單中參數名稱和 POJO 類的屬性名稱保持一致。並且控制器方法的參數類型是 POJO 類型。
如果是集合類型,有兩種方式:
第一種:
  要求集合類型的請求參數必須在 POJO 中。在表單中請求參數名稱要和 POJO 中集合屬性名稱相同。
  給 List 集合中的元素賦值,使用下標。
   給 Map 集合中的元素賦值,使用鍵值對。
第二種:
   接收的請求參數是 json 格式數據。需要借助一個注解實現。

3.1.4、使用示例
3.1.4.1、基本類型和String類型作為參數
3.1.4.2、POJO類型作為參數

public class Account implements Serializable {
    private String username;
    private String password;
    private Double money;
    
    private User user;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Account{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", money=" + money +
                ", user=" + user +
                '}';
    }
}
Account
public class User implements Serializable{
    private String uname;
    private Integer age;

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "uname='" + uname + '\'' +
                ", age=" + age +
                '}';
    }
}
User
<%--把數據封裝到Account類中  user  指的是Account的user屬性--%>
<form action="param/saveAccount" method="post">
      賬號:<input type="text" name="username"><br/>
      密碼:<input type="password" name="password"><br/>
      金額:<input type="text" name="money"><br/>
      姓名:<input type="text" name="user.uname"><br/>
      年齡:<input type="text" name="user.age"><br/>
      <input type="submit" value="提交">
</form>
param.jsp


3.1.4.3、POJO類中包含集合類型參數
注意:框架實現一些數據類型自動轉換。內置轉換器全都在:org.springframework.core.convert.support 包下。
如遇特殊類型轉換要求,需要自己編寫自定義類型轉換器。

java.lang.Boolean -> java.lang.String : ObjectToStringConverter 
java.lang.Character -> java.lang.Number : CharacterToNumberFactory java.lang.Character -> java.lang.String : ObjectToStringConverter java.lang.Enum -> java.lang.String : EnumToStringConverter java.lang.Number -> java.lang.Character : NumberToCharacterConverter 
java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory java.lang.Number -> java.lang.String : ObjectToStringConverter java.lang.String -> java.lang.Boolean : StringToBooleanConverter java.lang.String -> java.lang.Character : StringToCharacterConverter java.lang.String -> java.lang.Enum : StringToEnumConverterFactory 
java.lang.String -> java.lang.Number : StringToNumberConverterFactory java.lang.String -> java.util.Locale : StringToLocaleConverter 
java.lang.String -> java.util.Properties : StringToPropertiesConverter java.lang.String -> java.util.UUID : StringToUUIDConverter 
java.util.Locale -> java.lang.String : ObjectToStringConverter java.util.Properties -> java.lang.String : PropertiesToStringConverter java.util.UUID -> java.lang.String : ObjectToStringConverter 
......
內置類型轉換器


3.1.4.4、請求參數亂碼問題
post 請求方式:
在 web.xml 中配置一個過濾器 

<!--配置SpringMVC編碼過濾器-->
  <filter>
    <filter-name>characterEncodingFilter</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>
    <!-- 啟動過濾器 -->    
    <init-param>     
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>    
    </init-param>
  </filter>
  <!-- 過濾所有請求 -->
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

在 springmvc.xml 的配置文件中可以配置,靜態資源不過濾: 

<!-- location 表示路徑,mapping 表示文件,**表示該目錄下的文件以及子目錄的文件 -->   
<mvc:resources location="/css/" mapping="/css/**"/>   
<mvc:resources location="/images/" mapping="/images/**"/>   
<mvc:resources location="/scripts/" mapping="/javascript/**"/>

get 請求方式:  tomacat 對 GET和 POST 請求處理方式是不同的,GET請求的編碼問題,要改 tomcat 的 server.xml 配置文件,如下: 

<Connector  connectionTimeout="20000"  port="8080" protocol="HTTP/1.1"  redirectPort="8443"/> 
改為: 
<Connector  connectionTimeout="20000"  port="8080" protocol="HTTP/1.1"  redirectPort="8443" useBodyEncodingForURI="true"/>

如果遇到 ajax 請求仍然亂碼,請把:
  useBodyEncodingForURI="true"改為 URIEncoding="UTF-8"  即可。

3.2、特殊情況
3.2.1、自定義類型轉換器
3.2.1.1、使用場景
2011/11/11  ---  可以進行自動轉換(User{uname='root', age=23, date=Fri Nov 11 00:00:00 CST 2011})
2011-11-11  ---  無法進行自動轉換   無法進行數據的封裝

Failed to bind request element: org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcept ion:  Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is          
    org.springframework.core.convert.ConversionFailedException:  
    Failed to convert from type [java.lang.String] to type [java.util.Date] for value '2018-01-01'; nested exception is java.lang.IllegalArgumentException 


3.2.1.2、實現
第一步:定義一個類,實現Convert接口,該接口由兩個泛型

@FunctionalInterface
public interface Converter<S, T> {   ///S:表示接受的類型,T:表示目標類型 

    /**
     * Convert the source object of type {@code S} to target type {@code T}.
     * @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
     * @return the converted object, which must be an instance of {@code T} (potentially {@code null})
     * @throws IllegalArgumentException if the source cannot be converted to the desired target type
     */
    @Nullable
    T convert(S source);

}
Converter源碼

自定義類型轉換器:

/**
 * 將字符串轉換日期
 */
public class StringToDateConverter implements Converter<String,Date> {
    
    @Override
    public Date convert(String source) {
        if(source==null){
            throw new RuntimeException("請傳入數據");
        }
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        try {
            Date date = df.parse(source);
            return date;
        } catch (ParseException e) {
            throw new RuntimeException("輸入日期有誤。");
        }
    }
}
StringToDateConverter

 第二步:在spring配置文件中配置類型轉換器
spring 配置類型轉換器的機制是,將自定義的轉換器注冊到類型轉換服務中去。

public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {

    @Nullable
    private Set<?> converters;
ConversionServiceFactoryBean源碼
 <!--配置類型轉換器工廠-->
  <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <!--給工廠注入一個新的類型轉換器-->
    <property name="converters">
      <set>
        <!--配置自定義類型轉換器-->
        <bean class="cn.itcast.utils.StringToDateConverter"></bean>
      </set>
    </property>
  </bean>
  
  <!--開啟SpringMVC框架注解的支持-->
  <mvc:annotation-driven conversion-service="conversionService"/>

第三步:在annotation-driven標簽中引用匹配值的類型轉換服務

<mvc:annotation-driven conversion-service="conversionService"/>

運行結果:
  User{uname='root', age=23, date=Fri Nov 11 00:00:00 CST 2011}

3.2.2、使用(原生)ServletAPI對象作為方法參數
SpringMVC 還支持使用原始 ServletAPI 對象作為控制器方法的參數。支持原始 ServletAPI 對象有: 
  HttpServletRequest 
  HttpServletResponse
  HttpSession
  java.security.Principal
  Locale
  InputStream 
  OutputStream 
  Reader 
  Writer 
可以把上述對象,直接寫在控制的方法參數中使用。 
  <a href="param/testServlet">Servlet原生的API</a>

/**原生的API
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/testServlet")
    public String testServlet(HttpServletRequest request, HttpServletResponse response){
        System.out.println("執行了。。。");
        System.out.println(request);

        HttpSession session = request.getSession();
        System.out.println(session);

        ServletContext servletContext = session.getServletContext();
        System.out.println(servletContext);

        System.out.println(response);
        
        return "success";
    }
ParamController


四、常用注解
4.1、@RequestParam
  把請求中指定名稱的參數給控制器中的形參賦值。
屬性: 
  value:請求參數中的名稱。 
  required:請求參數中是否必須提供此參數。默認值:true。表示必須提供,如果不提供將報錯。 
示例:
  <a href="anno/testRequestParam?name=哈哈">RequestParam</a>

/**
 * 常用注解
 */
@Controller
@RequestMapping("/anno")
public class annoController {
    
    @RequestMapping("/testRequestParam")
    public String testRequestParam(@RequestParam(name = "name") String username){
        System.out.println("執行了。。。");
        System.out.println(username);
        return "success";
    }
}
AnnoController
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {

    /**
     * Alias for {@link #name}.
     */
    @AliasFor("name")
    String value() default "";

    /**
     * The name of the request parameter to bind to.
     * @since 4.2
     */
    @AliasFor("value")
    String name() default "";

    /**
     * Whether the parameter is required.
     * <p>Defaults to {@code true}, leading to an exception being thrown
     * if the parameter is missing in the request. Switch this to
     * {@code false} if you prefer a {@code null} value if the parameter is
     * not present in the request.
     * <p>Alternatively, provide a {@link #defaultValue}, which implicitly
     * sets this flag to {@code false}.
     */
    boolean required() default true;

    /**
     * The default value to use as a fallback when the request parameter is
     * not provided or has an empty value.
     * <p>Supplying a default value implicitly sets {@link #required} to
     * {@code false}.
     */
    String defaultValue() default ValueConstants.DEFAULT_NONE;

}
注解RequestParam 源碼


4.2、@RequestBody
  用於獲取請求體內容。直接使用得到是 key=value&key=value...結構的數據。 
  get 請求方式不適用。
屬性: 
  required:是否必須有請求體。默認值是:true。當取值為 true 時,get 請求方式會報錯。如果取值 為 false,get 請求得到是 null。 
示例:

<form action="anno/testRequestBody" method="post">
      用戶姓名:<input type="text" name="username" /><br/>
      用戶年齡:<input type="text" name="age" /><br/>
      <input type="submit" value="提交" />
</form>
anno.jsp
@RequestMapping("/testRequestBody")
    public String testRequestBody(@RequestBody String body){
        System.out.println("執行了。。。");
        System.out.println(body);
        return "success";
    }
AnnoController


4.3、@PathVariable
  用於綁定 url 中的占位符。例如:請求 url 中 /delete/{id},這個{id}就是 url 占位符。 
  url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 風格 URL 的一個重要標志。
屬性: 
  value:用於指定 url 中占位符名稱。 
  required:是否必須提供占位符
示例:
  <a href="anno/testPathVariable/10">testPathVariable</a>

/**PathVariable注解
     * @param id
     * @return
     */
    @RequestMapping("/testPathVariable/{sid}")
    public String testPathVariable(@PathVariable(name="sid") String id){
        System.out.println("執行了。。。");
        System.out.println(id);
        return "success";
    }
AnnoController

4.3.3、關於REST風格URL

什么是 rest:  
REST(英文:Representational State Transfer,簡稱 REST)描述了一個架構樣式的網絡系統, 比如 web 應用程序。它首次出現在 2000 年 Roy Fielding 的博士論文中,他是 HTTP 規范的主要編寫者之 一。在目前主流的三種 Web 服務交互方案中,REST 相比於 SOAP(Simple Object Access protocol,簡單 對象訪問協議)以及 XML-RPC 更加簡單明了,無論是對 URL 的處理還是對 Payload 的編碼,REST 都傾向於用更 加簡單輕量的方法設計和實現。值得注意的是 REST 並沒有一個明確的標准,而更像是一種設計的風格。 
它本身並沒有什么實用性,其核心價值在於如何設計出符合 REST 風格的網絡接口。 
restful 的優點  
它結構清晰、符合標准、易於理解、擴展方便,所以正得到越來越多網站的采用。 
restful 的特性: 
資源(Resources):網絡上的一個實體,或者說是網絡上的一個具體信息。 
它可以是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的存在。可以用一個 URI(統一 資源定位符)指向它,每種資源對應一個特定的 URI 。
要獲取這個資源,訪問它的 URI 就可以,因此 URI 即為每一個資源的獨一無二的識別符。 

表現層(Representation):把資源具體呈現出來的形式,叫做它的表現層(Representation)。 
比如,文本可以用 txt 格式表現,也可以用 HTML 格式、XML 格式、JSON 格式表現,甚至可以采用二進制格式。 

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

restful 的示例:  
/account/1   HTTP GET : 得到 id = 1 的 account   
/account/1   HTTP DELETE: 刪除 id = 1 的 account  
/account/1   HTTP PUT: 更新 id = 1 的 account
/account    HTTP POST: 新增 account 
REST風格URL


4.3.4、基於HiddentHttpMethodFilter的示例
作用: 
  由於瀏覽器 form 表單只支持 GET 與 POST 請求,而 DELETE、PUT 等 method 並不支持,Spring3.0 添加了一個過濾器,可以將瀏覽器請求改為指定的請求方式,發送給我們的控制器方法,使得支持 GET、POST、PUT 與 DELETE 請求。 
使用方法: 
  第一步:在 web.xml 中配置該過濾器。 
  第二步:請求方式必須使用 post 請求。 
  第三步:按照要求提供_method 請求參數,該參數的取值就是我們需要的請求方式。

4.4、@RequestHeader
  用於獲取請求消息頭。
屬性: 
  value:提供消息頭名稱 
  required:是否必須有此消息頭
注:在實際開發中一般不怎么用。 
示例:
  <a href="anno/testRequestHeader">testRequestHeader</a>

/**獲取請求頭的值
     * @param header
     * @return
     */
    @RequestMapping("/testRequestHeader")
    public String testRequestHeader(@RequestHeader(value = "Accept") String header){
        System.out.println("執行了。。。");
        System.out.println(header);
        return "success";
    }
CnnoController



4.5、@CookieValue

  用於把指定 cookie 名稱的值傳入控制器方法參數。
屬性: 
  value:指定 cookie 的名稱。 
  required:是否必須有此 cookie
示例:
  <a href="anno/testCookieValue">testCookieValue</a>

/**獲取cookie的值
     * @param cookieValue
     * @return
     */
    @RequestMapping("/testCookieValue")
    public String testCookieValue(@CookieValue(value = "JSESSIONID") String cookieValue){
        System.out.println("執行了。。。");
        System.out.println(cookieValue);
        return "success";
    }
AnnoController

  850F52E2652FB4B79DABDBC8341B1BA8

4.6、@ModelAttribute
  該注解是 SpringMVC4.3 版本以后新加入的。它可以用於修飾方法和參數。
  出現在方法上,表示當前方法會在控制器的方法執行之前,先執行。它可以修飾沒有返回值的方法,也可以修飾有具體返回值的方法。
  出現在參數上,獲取指定的數據給參數賦值。
屬性:
  value:用於獲取數據的 key。key 可以是 POJO 的屬性名稱,也可以是 map 結構的 key。
場景:
  當表單提交數據不是完整的實體類數據時,保證沒有提交數據的字段使用數據庫對象原來的數據。
  例如:
    我們在編輯一個用戶時,用戶有一個創建信息字段,該字段的值是不允許被修改的。在提交表單數 據是肯定沒有此字段的內容,一旦更新會把該字段內容置為 null,此時就可以使用此注解解決問題。

示例:
4.6.2.1、基於POJO屬性的基本使用

<form action="anno/testModelAttribute" method="post">
    用戶姓名:<input type="text" name="uname" /><br/>
    用戶年齡:<input type="text" name="age" /><br/>
    <input type="submit" value="提交" />
</form>
/**ModelAttribute注解
     * @param user
     * @return
     */
    @RequestMapping("/testModelAttribute")
    public String testModelAttribute(User user){
        System.out.println("執行了。。。");
        System.out.println(user);
        return "success";
    }
    
    @ModelAttribute
    public void showUser(){
        System.out.println("showUser執行了。。。");
    }
AnnoController

4.6.2.2、基於Map的應用場景示例1:ModerAttribute修飾方法帶返回值

/**ModelAttribute注解
     * @param user
     * @return
     */
    @RequestMapping("/testModelAttribute")
    public String testModelAttribute(User user){
        System.out.println("執行了。。。");
        System.out.println(user);
        return "success";
    }
    
    @ModelAttribute
    public User showUser(String uname){
        System.out.println("showUser執行了。。。");
        User user = new User();
        user.setUname(uname);
        user.setAge(20);
        user.setDate(new Date());
        return user;
    }
AnnoController

4.6.2.3、基於Map的應用場景示2:ModerAttribute修飾方法不帶返回值

/**ModelAttribute注解
     * @param user
     * @return
     */
    @RequestMapping("/testModelAttribute")
    public String testModelAttribute(@ModelAttribute("abc") User user){
        System.out.println("執行了。。。");
        System.out.println(user);
        return "success";
    }

    @ModelAttribute
    public void showUser(String uname, Map<String ,Object> map){
        System.out.println("showUser執行了。。。");
        User user = new User();
        user.setUname(uname);
        user.setAge(20);
        user.setDate(new Date());
        map.put("abc",user);
    }
View Code


4.7、@SessionAttribute
  用於多次執行控制器方法間的參數共享。
屬性: 
  value:用於指定存入的屬性名稱 
  type:用於指定存入的數據類型。 
示例:

<a href="anno/testSessionAttribute">存入---testSessionAttribute</a><br/>
<a href="anno/getSessionAttribute">獲取---getSessionAttribute</a><br/>
<a href="anno/delSessionAttribute">清除---delSessionAttributes</a><br/>
/**
 * 常用的注解
 */
@Controller
@RequestMapping("/anno")
@SessionAttributes(value={"msg"})   // 把msg=美美存入到session域對中
public class AnnoController {
    /**添加值到request域中
     * SessionAttributes的注解
     * @return
     */
    @RequestMapping(value="/testSessionAttributes")
    public String testSessionAttributes(Model model){
        System.out.println("testSessionAttributes...");
        // 底層會存儲到request域對象中
        model.addAttribute("msg","美美");
        return "success";
    }

    /**
     * 獲取值
     * @param modelMap
     * @return
     */
    @RequestMapping(value="/getSessionAttributes")
    public String getSessionAttributes(ModelMap modelMap){
        System.out.println("getSessionAttributes...");
        String msg = (String) modelMap.get("msg");
        System.out.println(msg);
        return "success";
    }

    /**
     * 清除
     * @param status
     * @return
     */
    @RequestMapping(value="/delSessionAttributes")
    public String delSessionAttributes(SessionStatus status){
        System.out.println("delSessionAttributes執行了...");
        status.setComplete();    //清除session中的內容
        return "success";
    }
}
AnnoController

也可以這么寫:public String testSessionAttribute(HttpServletRequest request...)
但是和Servlet的AIP的耦合過高。
SpringMVC中提供了一個類Model接口
ModerMap extends LinkedHashMap
向model中存儲數據,spring(底層實現)會將數據存儲到request域對象中。

@SessionAttibute 注解只能是作用在類上。
此時存儲在request域對象中的內容,現在session中也有。

存的時候可以使用Model model對象進行存儲。-->spring會自行創建 Model 的實現類進行存儲
取的時候,使用ModelMap modelMap進行取。ModelMap是Model的實現類。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM