【SpringMVC】快速入門

三次框架

在B/S架構中,系統標準的三層架構包括:表現層、業務層、持久層。

表現層

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

業務層

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

持久層

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

MVC模型

MVC全名是Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫。
Model(模型): 通常指的就是我們的數據模型。作用一般情況下用於封裝數據。
View(視圖): 通常指的就是我們的jsp或者html。作用一般就是展示數據的。 通常視圖是依據模型數據創建的。
Controller(控制器): 是應用程序中處理用戶交互的部分。作用一般就是處理程序邏輯的。

SpringMVC是什麼

SpringMVC是一種基於Java的實現MVC設計模型的請求驅動類型的輕量級Web框架,屬於 Spring FrameWork 的後續產品,已經融合在Spring Web Flow裏面。

SpringMVC和Struts2的優勢分析

共同點:

它們都是表現層框架,都是基於MVC模型編寫的。
它們的底層都離不開原始ServletAPI。
它們處理請求的機制都是一個核心控制器。

區別:

Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter
Spring MVC 是基於方法設計的,而Struts2是基於類,Struts2每次執行都會創建一個動作類。所以Spring MVC 會稍微比 Struts2 快些。 Spring MVC 使用更加簡潔,同時還支持 JSR303, 處理 ajax 的請求更方便。
Struts2 的OGNL 表達式使頁面的開發效率相比Spring MVC 更高些,但執行效率並沒有比JSTL提升,尤其是struts2的表單標籤,遠沒有html執行效率高。

SpringMVC中的部分組件

DispatcherServlet:前端控制器

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

HandlerMapping:處理器映射器

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

Handler:處理器

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

HandlAdapter:處理器適配器

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

View Resolver:視圖解析器

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

View:視圖

SpringMVC框架提供了很多的View視圖類型的支持,包括:jstlView、freemarkerView、pdfView等。我們最常用的視圖就是jsp。

入門案例

配置核心控制器web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <!-- 配置spring mvc的核心控制器 -->
  <servlet>
    <!--固定的寫控制器組件DispatcherServlet,起控制作用,相當於servlet的指揮中心-->
    <servlet-name>dispatcherServlet</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>
    
    <!-- 配置servlet的對象的創建時間點:應用加載時創建。 取值只能是非0正整數,表示啓動順序 -->
    <!--配置爲1代表當Tomcat啓動的時候DispatcherServlet就會被創建-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <!--用servlet攔截-->
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <!--填寫"/"代表攔截全部頁面-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

創建SpringMVC的配置文件springmvc.xml

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

    <!--配置視圖解析器,視圖解析器對象,id和class都是固定不變的,它可以幫助我們跳轉到指定頁面-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--prefix指定跳轉到文件的位置-->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <!--suffix指定跳轉到的文件的後綴名-->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--開啓SpringMVC框架註解的支持-->
    <mvc:annotation-driven></mvc:annotation-driven>

</beans>

編寫控制器並使用註解配置

@Controller
public class HelloController {
    //@RequestMapping請求映射標籤,當該類被調用的時候會執行這個方法
    //path屬性指定方法的請求路徑
    @RequestMapping(path="/hello")
    public String sayHello(){
        System.out.println("Hello StringMVC");
        //默認表示爲跳轉到名稱爲success.jsp的頁面
        return "success";
    }
}

SpringMVC的請求響應流程

在這裏插入圖片描述

@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

上面例子中 /account 和 /order 可以使用 @RequestMapping 填寫在類上, 而 /add 等這些作爲二級目錄寫在方法上

方法上: 請求URL的第二級訪問目錄。

註解的屬性:

  • value:用於指定請求的URL。它和path屬性的作用是一樣的。
  • method:用於指定請求的方式(選擇GET,POST,PUT,PATCH等方式)。
  • params:用於指定限制請求參數的條件。它支持簡單的表達式。要求請求參數的key和value必須和配置的一模一樣。
    例如: params = {“accountName”},表示請求參數必須有accountName params = {“moeny!100”},表示請求參數中money不能是100。
  • headers:用於指定限制請求消息頭的條件。

請求參數的綁定

  1. 請求參數的綁定說明
    1. 綁定機制
      1. 表單提交的數據都是key=value格式的 username=haha&password=123
      2. SpringMVC的參數綁定過程是把表單提交的請求參數,作爲控制器中方法的參數進行綁定的
      3. 要求:提交表單的name和參數的名稱是相同的
  2. 支持的數據類型
    1. 基本數據類型和字符串類型
    2. 實體類型(JavaBean)
    3. 集合數據類型(List、map集合等)

零散接收參數-案例

編寫一個簡單的jsp頁面,超鏈接中傳遞兩個參數:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>請求參數綁定</title>
</head>
<body>
    <a href="param/testParam?username=hehe&password=12345">請求參數綁定</a>
</body>
</html>

參數將會進入@RequestMapping指定的控制類的方法中:

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

當訪問該jsp頁面並點擊該鏈接後就會調用testParam方法,在控制檯輸出:
賬戶:hehe,密碼:1234

用類封裝參數-案例

當時如果傳入的參數比較多,就應該把參數封裝到一個類中:
新建一個實體類Account並添加get,set和toString方法:

public class Account implements Serializable {
    private String username;
    private String password;
    private Double money;
    //省去get,set和toString方法
}

創建一個表單,並且地址指向一個方法:

    <!--創建一個表單-->
    <form action="param/saveAccount" method="post">
        姓名:<input type="text" name="username"/><br>
        密碼:<input type="text" name="password"/><br>
        金額:<input type="text" name="money"/><br>
        <input type="submit" value="提交"><br>
    </form>

在測試類中創建這個方法,直接在方法的形參中輸入這個類,就可以直接封裝進去:

@Controller
@RequestMapping("/param")
public class ParamController {
    //請求參數封裝到類中
    @RequestMapping("/saveAccount")
    public String saveAccount(Account account) {
        System.out.println("testParam執行了。。");
        System.out.println(account);
        return "success";
    }
}

輸入:
輸入頁面
控制檯輸出:
在這裏插入圖片描述

如果Account 類中包含另一個類的實例。應該怎麼封裝呢?

User類:

public class User implements Serializable {
    private String uname;
    private Integer age;
}

Account :

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

那麼在創建表單的時候可以這樣實現:

    <!--創建一個表單-->
    <form action="param/saveAccount" method="post">
        姓名:<input type="text" name="username"/><br>
        密碼:<input type="text" 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="提交"><br>
    </form>

解決中文亂碼問題:在過濾器中實現:

web.xml中添加:

  <!--配置解決中文亂碼的過濾器-->
  <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>

如果這個Account類中還包含了List與Map集合應該怎麼封裝?

public class Account implements Serializable {
    private String username;
    private String password;
    private Double money;
    //新增
    private User user;
    //新增
    List<User> users;
}

jsp中的表單:

    <!--創建一個表單,類中存在List、Map集合-->
    <form action="param/saveAccount" method="post">
        姓名:<input type="text" name="username"/><br>
        密碼:<input type="text" name="password"/><br>
        金額:<input type="text" name="money"/><br>

        用戶姓名:<input type="text" name="user.uname"/><br>
        用戶年齡:<input type="text" name="user.age"/><br>

        <%--封裝到list中的一個User對象裏--%>
        路人姓名0<input type="text" name="users[0].uname"/><br>
        路人年齡0<input type="text" name="users[0].age"/><br>
        <%--封裝到list中的一個User對象裏--%>
        路人姓名1<input type="text" name="users[1].uname"/><br>
        路人年齡1<input type="text" name="users[1].age"/><br>
        <%--封裝到map中的一個User對象裏,鍵設置爲‘key2’--%>
        路人姓名2<input type="text" name="map['key2'].uname"/><br>
        路人年齡2<input type="text" name="map['key2'].age"/><br>

        <input type="submit" value="提交"><br>
    </form>

如此就可以實現集合的封裝。
輸入:
在這裏插入圖片描述
控制檯輸出;
testParam執行了。。
Account{username=‘張張’, password=‘123456’, money=200.0, user=User{uname=‘打哈’, age=22}, users=[User{uname=‘甲’, age=21}, User{uname=‘乙’, age=25}], map={key2=User{uname=‘丙’, age=30}}}

自定義類型轉換器

在User中添加一個生日:

public class User implements Serializable {
    private String uname;
    private Integer age;
    private Date date;
}

創建表單:

    <!--創建一個表單-->
    <form action="param/saveUser" method="post">
        姓名:<input type="text" name="uname"/><br>
        年齡:<input type="text" name="age"/><br>
        生日:<input type="text" name="date"/><br>
        <input type="submit" value="提交"><br>
    </form>

處理提交的數據:

    //自定義類型轉換器
    @RequestMapping("/saveUser")
    public String saveUser(User user) {
        System.out.println("saveUser執行了。。");
        System.out.println(user);
        return "success";
    }

輸入:
在這裏插入圖片描述
此時控制檯會打印:
saveUser執行了。。
User{uname=‘張張’, age=123456, date=Sun Feb 02 00:00:00 CST 2020}
但是如果想把Date類型的輸入進行修改:
在這裏插入圖片描述
訪問就會報錯:
在這裏插入圖片描述
此時我們需要自定義類型轉換器:

/**
 * 字符串轉換成日期
 * @author liwenlong
 * @data 2020/5/21
 */
public class StringToDate implements Converter<String, Date> {
    @Override
    public Date convert(String s) {
        if (s == null) {
            throw new RuntimeException("請傳入數據");
        }
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        try {
            //把字符串轉日期
            return df.parse(s);
        } catch (ParseException e) {
            throw new RuntimeException("錯誤");
        }
    }
}

在springmvc.xml文件中註冊類型轉換器:

    <!--配置自定義類型轉換器-->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <!--註冊類型轉化器-->
        <property name="converters">
            <set>
                <bean class="com.lwl.util.StringToDate"></bean>
            </set>
        </property>
    </bean>
    
    <!--開啓SpringMVC框架註解的支持-->
    <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

此時輸入2020-02-02就不會再報錯,但是原來的2020/02/02就不能用了。

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