天天看點

Spring和SpringMVC概述IOCAOPSpringMVC

文章目錄

  • 概述
    • 介紹
    • 環境搭建
      • 建立項目
      • 添加依賴
      • 編寫`Bean`
      • 添加配置檔案
      • 執行個體化`Bean`
  • `IOC`
    • 概念
    • 配置檔案`Bean`管理
      • 構造器注入
        • 無參構造
        • 索引注入
        • 類型注入
        • 參數名注入
        • 引用注入
      • `Set`注入
      • 注入特殊值
        • 注入`null`
        • 注入特殊符号
        • 注入級聯屬性
        • 注入數組
        • 注入`List`
        • 注入`Map`
      • `Bean`的作用域
      • `Bean`的生命周期
      • 自動裝配
      • `Bean`的别名
      • 導入外部`Bean`
    • 注解`Bean`管理
      • 元件掃描
      • 示例
      • 注解說明
      • 掃描過濾器
      • 屬性注入
        • `@Value`
        • `@Autowired`
        • `@Qualifier`
        • `@Resource`
      • 完全注解開發
  • `AOP`
    • 概念
    • 切入點表達式
    • 切面操作
      • 添加依賴
      • 開啟掃描器
        • 使用配置檔案
        • 使用注解
      • 啟用代理
        • 使用配置檔案
        • 使用注解
      • 被增強類
      • 增強類
      • 切入點抽取
    • 注解詳解
      • 被增強類
      • `@Pointcut`
      • `@Before`
      • `@After`
      • `@Around`
      • `@AfterReturning`
      • `@AfterThrowing`
    • 連接配接點
      • `JoinPoint`接口
      • `ProceedingJoinPoint`接口
  • `SpringMVC`
    • 項目建立
      • `Maven`多子產品
      • 添加依賴
      • 核心配置
      • 改變資源路徑
      • 建立控制類
      • 視圖解析器
      • 請求處理過程
    • 處理器深入
      • `@RequestMapping`
        • `@RequestMapping`的定義
        • 請求路徑
        • 請求方法
        • 請求參數
        • 請求頭
        • `@RequestMapping`修飾類
      • 處理器參數
        • `Servlet`參數
        • 請求參數
        • 編碼過濾器
        • `@RequestParam`
        • 對象參數
      • 處理器傳回
        • 空傳回
        • 傳回靜态資源
        • 傳回`JSON`
        • 傳回`List`
        • 傳回`Map`
        • 傳回`ModelAndView`
        • `@ResponseBody`修飾類
        • `@RestController`
    • 異常處理
      • 全局異常處理類
      • 完善配置
      • 自定義異常類
      • 控制類抛出異常
    • 攔截器
      • `HandlerInterceptor`接口
      • 定義攔截器
      • 注冊攔截器
      • 攔截器鍊
      • 攔截器與過濾器的差別
    • `RESTful`
      • `REST`風格
      • `RESTful`注解
      • 資源設計規則
      • 動作設計規則
      • 路徑傳參
      • 傳回結果規則
      • `RESTful-API`

概述

介紹

  

Spring

是一個開源的

JavaEE

應用程式架構,主要核心是

IOC(Inversion Of Control,控制反轉)

AOP(Aspect Oriented Programming,面向切面程式設計)

技術。參照官方文檔。

環境搭建

建立項目

  建立

Maven

項目,選擇普通

Java

項目模闆:

1.依次點選File -> New -> Project
2.在彈出的視窗中選擇Maven
3.選擇Project SDK,指定JDK版本
4.勾選Create from archetype(從模闆建立),
  選擇org.apache.maven.archetypes:maven-archetype-quickstart
5.點選Next,輸入項目名和項目位置
6.在Artifact Coordinates中輸入倉庫資訊:
  GroupId指定項目組辨別,一般為"com.公司名";
  ArtifactId指定目前項目在項目組下的辨別,一般為"項目名";
  Version指定工程版本,格式為"X.X.X-裡程碑"
7.配置Maven環境(如果之前IDEA成功配置了Maven環境,此步會自動配置完善)
8.為src/main和src/test中添加resources目錄:
  (1).建立resources目錄
  (2).右擊建立的resources目錄,選擇Mark Directory as,選擇Resources Root
           

添加依賴

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.5.RELEASE</version>
</dependency>
           

編寫

Bean

  建立包并添加

Bean

類:

package com.oner.bean;

public class User {
    private String name;
    private String password;
    
    public User(){super();}
    
    public User(String name,String password) {
        this.name = name;
        this.password = password;
    }
    
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public String getPassword() {return password;}
    public void setPassword(String password) {this.password = password;}
    @Override
    public String toString() {
        return "name is : " + name + " , " + "password is : " + password +" .";
    }
}
           

添加配置檔案

  在

src/main/resources

目錄下添加配置檔案

beans.xml

(官方模闆):

beans.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
                           https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 将Bean加載到容器中管理,id為Bean指定唯一引用辨別,class指定Bean的全類名  -->
    <bean id="user" class="com.oner.beans.User">
        <!-- property标簽用于設定Bean對象的屬性值,name指定屬性名,value指定屬性值 -->
        <property name="name" value="Tony"/>
        <property name="password" value="123456"/>
    </bean>
    
</beans>
           

執行個體化

Bean

public class App {
    public static void main(String[] args) {
        /* 擷取Spring上下文環境(加載配置檔案),ClassPathXmlApplicationContext()
           為可變形參構造器,可傳入多個配置檔案 */
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        /* 擷取Spring容器執行個體化好的Bean對象,getBean()傳入bean的id */
        User user = (User)context.getBean("user");
        /* 調用Bean對象的方法 */
        System.out.println(user);
    }
}
           

IOC

概念

  

IOC(Inversion Of Control,控制反轉)

是一種設計思想,在沒有

IOC

的程式中,對象的建立以及對象之間的調用關系寫死在程式中,對象的建立和對象間的調用由程式控制;使用

IOC

後,對象的建立及對象間的調用由

IOC

容器控制,降低代碼的耦合度。

Spring

就是一種

IOC

容器。

配置檔案

Bean

管理

  

Bean

管理主要分為建立對象和注入屬性,

Bean

管理的方式有使用

XML

配置檔案和使用注解。

構造器注入

  使用

bean

标簽建立對象時,可以使用有參構造器注入屬性。

無參構造

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 通過無參構造器建立Bean對象 -->
    <bean id="user" class="com.oner.beans.User"></bean>
</beans>
           

索引注入

  索引注入适用于定義了有參構造器的

Bean

類。

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 建立對象 -->
    <bean id="user" class="com.oner.beans.User">
        <!-- 通過構造器的參數索引注入屬性 -->
        <constructor-arg index="0" value="Tony"/>
        <constructor-arg index="1" value="123456"/>
    </bean>
</beans>
           

類型注入

  類型注入适用于定義了有參構造器的

Bean

類。

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 建立對象 -->
    <bean id="user" class="com.oner.beans.User">
        <!-- 通過構造器的參數類型注入屬性,多個同類型的參數按參數排列順序注入;
             type必須傳入類的全限定名 -->
        <constructor-arg type="java.lang.String" value="Tony"/>
        <constructor-arg type="java.lang.String" value="123456"/>
    </bean>
</beans>
           

參數名注入

  參數名注入适用于定義了有參構造器的

Bean

類。

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 建立對象 -->
    <bean id="user" class="com.oner.beans.User">
        <!-- 通過構造器的參數名注入屬性,name指定構造器參數名,value指定參數值 -->
        <constructor-arg name="name" value="Tony"/>
        <constructor-arg name="password" value="123456"/>
    </bean>
</beans>
           

引用注入

  引用注入适用于定義了有參構造器且有參構造器的參數類型為其它

Bean

類的

Bean

類。

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 建立對象 -->
    <bean id="bean1" class="com.oner.beans.Bean1">
        <!-- 通過索引、類型或參數名等形式注入其它Bean類型對象,ref指定被引用Bean對象的id或别名 -->
        <constructor-arg index="..." ref="bean2"/>
        <constructor-arg type="..." ref="bean3"/>
        <constructor-arg name="..." ref="bean3"/>
    </bean>
    <bean id="bean2" class="com.oner.beans.Bean2">...</bean>
    <bean id="bean3" class="com.oner.beans.Bean3">...</bean>
</beans>
           

Set

注入

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 建立對象 -->
    <bean id="user" class="com.oner.beans.User">
        <!-- Set注入屬性值 -->
        <property name="name" value="Tony"/>
        <!-- Set注入其它Bean類型對象,ref指定被引用Bean對象的id或别名 -->
        <property name="department" ref="user1"/>
    </bean>
</beans>
           

注入特殊值

注入

null

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 建立對象 -->
    <bean id="user" class="com.oner.beans.User">
        <!-- 注入null -->
        <property name="name">
            <null/>
        </property>
    </bean>
</beans>
           

注入特殊符号

  

XML

的屬性值中不可出現類似于

<,>,&...

等的符号,可以使用轉義字元,也可以使用

CDATA

.

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 建立對象 -->
    <bean id="user" class="com.oner.beans.User">
        <!-- "&lt;"是"<"的轉義,"&gt;"是">"的轉義,"&amp;"是"&"的轉義 -->
        <property name="password" value="&lt;123&amp;456&gt;"/>
        <!-- 使用CDATA包含特殊字元,形式為 : <![CDATA[字元串]]> -->
        <property name="password">
            <value><![CDATA[<123&456>]]></value>
        </property>
    </bean>
</beans>
           

注入級聯屬性

  一個

Bean

類中的其它

Bean

類型屬性稱為級聯屬性,可以使用嵌套

bean

标簽實作級聯屬性的注入。

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 建立對象 -->
    <bean id="user" class="com.oner.beans.User">
        <property name="department">
            <!-- 嵌套bean标簽實作級聯屬性的注入 -->
            <bean id="departmentA" class="com.oner.beans.Department">
                <property name="name" value="sercurity"/>
            </bean>
        </property>
    </bean>
</beans>
           

注入數組

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 建立對象 -->
    <bean id="user" class="com.oner.beans.User">
        <property name="skill">
            <!-- 使用array标簽注入數組 -->
            <array>
                <value>Java</value>
                <value>JavaScript</value>
                <value>C</value>
                
                <!-- 對于成員是非普通類型的數組,需要引用其它Bean對象進行成員注入;
                     bean屬性指定被引用Bean對象的id或别名 -->
                <ref bean="bean1"/>
            </array>
        </property>
    </bean>
</beans>
           

注入

List

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 建立對象 -->
    <bean id="user" class="com.oner.beans.User">
        <property name="hobby">
            <!-- 使用list标簽注入List集合 -->
            <list>
                <value>book</value>
                <value>tv</value>
                <value>game</value>
                
                <!-- 對于成員是非普通類型的集合,需要引用其它Bean對象進行成員注入;
                     bean屬性指定被引用Bean對象的id或别名 -->
                <ref bean="bean1"/>
            </list>
        </property>
    </bean>
</beans>
           

注入

Map

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 建立對象 -->
    <bean id="user" class="com.oner.beans.User">
        <property name="grades">
            <!-- 使用map标簽注入Map -->
            <map>
                <!-- entry标簽指定鍵值對 -->
                <entry key="math" value="149"/>
                <entry key="physics" value="99"/>
                
                <!-- 對于key和value是非普通類型的Map,需要引用其它Bean對象進行成員注入;
                     key-ref屬性指定被引用的鍵的Bean對象的id或别名;
                     value-ref屬性指定被引用的值的Bean對象的id或别名 -->
                <entry key-ref="bean1" value-ref="bean2"/>
            </map>
        </property>
    </bean>
</beans>
           

Bean

的作用域

  在

Spring

中,

Bean

對象預設為單執行個體對象,即在不同位置使用同一個

id

Bean

對象均為同一個對象。可以通過

bean

标簽的

scope

屬性設定

Bean

對象是單執行個體還是多執行個體。

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- "singleton"指定Bean對象為單執行個體,Spring加載配置檔案時即建立對象 -->
    <bean id="..." class="..." scope="singleton">...</bean>
    
    <!-- "prototype"指定Bean對象為多執行個體,調用getBean()方法時建立對象 -->
    <bean id="..." class="..." scope="prototype">...</bean>
    
    <!-- "request"指定Bean對象建立時放置到request域中 -->
    <bean id="..." class="..." scope="request">...</bean>
    
    <!-- "session"指定Bean對象建立時放置到session域中 -->
    <bean id="..." class="..." scope="session">...</bean>
</beans>
           

Bean

的生命周期

  

Bean

對象在

Spring

容器調用構造器時建立,在

Spring

容器關閉時銷毀,可以通過配置檔案監聽

Bean

的建立與銷毀。在

Bean

中添加以下方法:

public class User {
    public void userInit() {
        System.out.println("Hello Spring.");
    }
    public void userDestroy() {
        System.out.println("Bye bye Spring.");
        ClassPathXmlApplicationContext context = 
            new ClassPathXmlApplicationContext("beans.xml");
        /* 手動銷毀Bean對象 */
        context.close();
    }
}
           

  在配置檔案中配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- init-method屬性指定初始化方法名;destroy-method屬性指定銷毀方法名 -->
    <bean id="user" 
          class="com.oner.User"
          init-method="userInit"
          destroy-method="userDestroy">
        ...
    </bean>
</beans>
           

  可在

main

方法中調用方法手動銷毀

bean

:

public static void main(String[] args) {
    /* 擷取Spring上下文環境(加載配置檔案) */
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    /* 使用别名擷取Bean對象 */
    User user = (User)context.getBean("user");
    System.out.println(user);
    /* 手動銷毀bean */
    ((ClassPathXmlApplicationContext)context).close();
}
           

自動裝配

  

Spring

可以根據

Bean

的屬性自動注入其它

Bean

:

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 設定autowire屬性為"byName"指定将id值與指定Bean類屬性名相同
         的其它Bean對象注入到指定Bean對象的指定屬性中 -->
    <bean id="user" class="com.oner.beans.User" autowire="byName"></bean>
    <bean id="department" class="...">...</bean>
    
    <!-- 設定autowire屬性為"byType"指定将class指定的類型與指定Bean類屬性類型相同
         的其它Bean對象注入到指定Bean對象的指定屬性中 -->
    <bean id="user" class="com.oner.beans.User" autowire="byType"></bean>
    <bean id="..." class="com.oner.beans.Department">...</bean>
</beans>
           

Bean

的别名

  可以使用

alias

标簽為

Bean

對象起别名,即使用

alias

标簽配置的别名代替

bean

标簽配置的

id

.

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <bean id="user" class="com.oner.beans.User">
        <!-- name指定構造器參數名,value指定參數值 -->
        <constructor-arg name="name" value="Tony"/>
        <constructor-arg name="password" value="123456"/>
    </bean>
    <!-- name指定Bean對象的id,alias指定别名 -->
    <alias name="user" alias="userInfo"/>
</beans>
           

  使用别名擷取

Bean

對象:

public class App {
    public static void main(String[] args) {
        /* 擷取Spring上下文環境(加載配置檔案) */
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        /* 使用别名擷取Bean對象 */
        User userInfo = (User)context.getBean("userInfo");
        /* 調用Bean對象的方法 */
        System.out.println(userInfo);
    }
}
           

  使用

bean

标簽的

name

屬性也可以為

Bean

對象起别名:

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- name屬性可為Bean對象起多個别名,多個别名之間可以使用",/;/空格"分隔 -->
    <bean id="user" class="com.oner.beans.User" name="a,b;c d">
        <!-- name指定構造器參數名,value指定參數值 -->
        <constructor-arg name="name" value="Tony"/>
        <constructor-arg name="password" value="123456"/>
    </bean>
</beans>
           

  使用别名擷取

Bean

對象:

public class App {
    public static void main(String[] args) {
        /* 擷取Spring上下文環境(加載配置檔案) */
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        /* 使用别名擷取Bean對象 */
        User a = (User)context.getBean("a");
        User b = (User)context.getBean("b");
        User c = (User)context.getBean("c");
        User d = (User)context.getBean("d");
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
        System.out.println(d);
    }
}
           

導入外部

Bean

  使用

import

标簽可以導入外部

Bean

,将多個配置檔案合為一個:

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 使用import标簽導入外部配置檔案,将多個配置檔案合為一個 -->
    <import resource="beans1.xml"/>
    <import resource="beans2.xml"/>
    <import resource="beans3.xml"/>
</beans>
           

  示例:

beans1.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <bean id="user1" class="com.oner.beans.User">
        <property name="name" value="Tony"/>
        <property name="password" value="abcdef"/>
    </bean>
</beans>
           

beans2.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <bean id="user2" class="com.oner.beans.User">
        <property name="name" value="Steven"/>
        <property name="password" value="123456"/>
    </bean>
</beans>
           

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 使用import标簽導入外部配置檔案,将多個配置檔案合為一個 -->
    <import resource="beans1.xml"/>
    <import resource="beans2.xml"/>
</beans>
           

  加載主配置檔案,擷取

Bean

對象:

public class App {
    public static void main(String[] args) {
        /* 擷取Spring上下文環境(加載配置檔案) */
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        /* 使用别名擷取Bean對象 */
        User user1 = (User)context.getBean("user1");
        User user2 = (User)context.getBean("user2");
        System.out.println(user1);
        System.out.println(user2);
    }
}
           

注解

Bean

管理

  

Spring

中用于執行個體化

Bean

的注解有

@Component,@Service,@Controller,@Repository

,它們的功能完全一緻。推薦

@Component

用在任意普通

Bean

類上,

@Service

用在

service

層,

@Controller

用在

controller

層,

@Repository

用在

dao

層。

元件掃描

  使用注解管理

Bean

之前,需要開啟

Spring

元件掃描器。元件掃描器的作用是掃描指定包下的所有類,掃描到使用執行個體化

Bean

的注解的類,即建立該類的對象。開啟方法為:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 步驟1:在beans标簽中添加context命名空間(IDEA可自動導入) -->
<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
                           https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           https://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 步驟2:指定基礎掃描包,多個包之間使用","分隔 -->
    <context:component-scan base-package="com.xxx.package1,com.xxx.package2"/>

</beans>
           

  開啟元件掃描器後,

Spring

将掃描指定的基礎掃描包下的所有類或資源,掃描到使用相關注解的類,即建立該類的對象。

示例

  指定基礎掃描包:

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 指定基礎掃描包 -->
    <context:component-scan base-package="com.oner.beans"/>
</beans>
           

  使用注解:

package com.oner.bean;

/* value屬性指定Bean對象的id,如果省略不寫,預設為類名首字母小寫 */
@Component(value = "user")
public class User {
    private String name;
    private String password;
    
    public User(){super();}
    
    public User(String name,String password) {
        this.name = name;
        this.password = password;
    }
    
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public String getPassword() {return password;}
    public void setPassword(String password) {this.password = password;}
    @Override
    public String toString() {
        return "name is : " + name + " , " + "password is : " + password +" .";
    }
}
           

  擷取

Bean

對象:

public class App {
    public static void main(String[] args) {
        /* 擷取Spring上下文環境(加載配置檔案) */
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        /* 擷取Bean對象 */
        User user = (User)context.getBean("user");
        user.setName("Tony");
        user.setPassword("123456");
        System.out.println(user);
    }
}
           

注解說明

  

@Component,@Service,@Controller,@Repository

都隻有一個

value

屬性,

value

屬性對應配置檔案中

bean

标簽的

id

屬性,通過

getBean()

方法傳入對應的

value

值可擷取

Bean

對象。不傳入

value

值,預設為類名小寫。

public @interface Component {
    String value() default "";
}

@Component
public @interface Service {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

@Component
public @interface Controller {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

@Component
public @interface Repository {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}
           

掃描過濾器

  不指定情況下,

Spring

使用預設掃描過濾器,即掃描指定包下的所有類。可以自定義掃描過濾器:

<?xml version="1.0" encoding="UTF-8"?>
<beans ... >
    <!-- 不指定情況下使用預設掃描過濾器 -->
    <context:component-scan base-package="com.oner.beans"/>
    
    <!-- user-default-filter="false"指定不使用預設掃描過濾器 -->
    <context:component-scan base-package="com.oner.beans" user-default-filter="false">
        <!-- include-filter指定過濾出的類被掃描;
             type="annotation"指定過濾類型為根據注解過濾;
             expression指定掃描表達式,根據注解過濾時,可指定過濾的注解類型 -->
        <context:include-filter type="annotation"
                                expression="org.springframework.stereotype.Service"/>
        
        <!-- exclude-filter指定過濾出的類不被掃描;
             type="annotation"指定過濾類型為根據注解過濾;
             expression指定掃描表達式,根據注解過濾時,可指定過濾的注解類型 -->
        <context:exclude-filter type="annotation"
                                expression="org.springframework.stereotype.Service"/>
    </context:component-scan>
</beans>
           

屬性注入

@Value

  

@Value

用于

Set

注入,修飾屬性并直接指定屬性值。示例:

@Component(value = "bean")
public class Bean {
    @Value(value="Tony")
    private String name;
    @Value(value="123456")
    private String password;
    
    public void test() {
        System.out.println("name is : " + name + " , " + " password is : " + password + " .");
    }
}
           

  擷取

Bean

對象:

public class App {
    public static void main(String[] args) {
        /* 擷取Spring上下文環境(加載配置檔案) */
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        /* 擷取Bean對象 */
        Bean bean = (Bean)context.getBean("bean");
        bean.test();
    }
}
           

@Autowired

  使用

@Autowired

注解可實作根據類型自動注入屬性,

@Autowired

需要加載對應的屬性上。示例:

@Component(value = "bean1")
public class Bean1 {
    /* @Autowired注解是屬性注解 */
    @Autowired
    private Bean2 bean;
    public void test() {
        bean.test();
    }
}
           

  自動裝配

Bean

:

/* 被@Autowired自動注入給指定屬性的類必須使用相應注解執行個體化 */
@Component(value = "bean2")
public class Bean2 {
    public void test() {
        System.out.println("Hello Spring!");
    }
}
           

  擷取

Bean

對象:

public class App {
    public static void main(String[] args) {
        /* 擷取Spring上下文環境(加載配置檔案) */
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        /* 擷取Bean對象 */
        Bean1 bean = (Bean1)context.getBean("bean1");
        bean.test();
    }
}
           

  

@Autowired

注解的工作機制是掃描指定包下使用執行個體化注解的類,掃描到與

@Autowired

注解修飾的屬性同類型的類,将該類的執行個體賦給屬性。

@Autowired

注解的缺點是無法應付指定包下具有多個同類型

Bean

的場景,例如實作同一個接口或繼承同一個類的多個

Bean

@Qualifier

  

@Qualifier

注解可以實作根據執行個體化注解指定的

Bean

value

值注入屬性。

@Qualifier

用于修飾

Bean

的屬性,掃描到的

Bean

類使用執行個體化注解指定的

value

值與

@Qualifier

注解指定的

value

值相同時,使用該

Bean

類的執行個體注入屬性。

@Qualifier

可與

@Autowired

配合使用,提高準确性。例有以下場景:

public interface IBean {
    void test();
}
           

  

IBeanImpl1

實作接口

IBean

:

@Component(value = "bean1")
public class IBeanImpl1 implements IBean {
    @Override
    public void test() {
        System.out.println("Hello Spring,I am IBeanImpl1");
    }
}
           

  

IBeanImpl2

實作接口

IBean

:

@Component(value = "bean2")
public class IBeanImpl2 implements IBean {
    @Override
    public void test() {
        System.out.println("Hello Spring,I am IBeanImpl2");
    }
}
           

  

Bean

使用

IBean

類型注入屬性:

@Component(value = "bean")
public class Bean {
    @Autowired
    @Qualifier(value = "bean1")
    private IBean bean;
    public void test() {
        bean.test();
    }
}
           

  擷取

Bean

對象:

public class App {
    public static void main(String[] args) {
        /* 擷取Spring上下文環境(加載配置檔案) */
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        /* 擷取Bean對象 */
        Bean bean = (Bean)context.getBean("bean");
        bean.test();
    }
}
           

@Resource

  

@Resource

可以看作為

@Autowired

@Qualifier

的合體,即可根據類型注入,也可根據

value

值注入。

@Component(value = "bean")
public class Bean {
    
    /* 根據類型注入 */
    @Resource
    private Bean1 bean1;
    /* 根據value值注入 */
    @Resource(name = "bean3")
    private Bean2 bean2;
    
    public void test() {
        bean1.test();
        bean2.test();
    }
}
           

完全注解開發

  掃描器的配置也可以使用注解實作,進而實作完全注解開發。定義配置類:

/* @Configuration指定類為Spring的配置類;
   @ComponentScan的basePackages屬性指定基礎掃描包,多個基礎掃描包通過數組形式指定  */
@Configuration
@ComponentScan(basePackages = {"com.xxx.package1","com.xxx.package2"})
public class SpringConfiguration {}
           

  加載配置類:

public class App {
    public static void main(String[] args) {
        /* 使用AnnotationConfigApplicationContext類加載配置類 */
        ApplicationContext context = 
            new AnnotationConfigApplicationContext(SpringConfiguration.class);
        /* 擷取Bean對象 */
        Bean bean = (Bean)context.getBean("bean");
        bean.test();
    }
}
           

AOP

概念

  

AOP(Aspect Oriented Programming,面向切面程式設計)

底層使用動态代理技術,用于在不改變原有代碼的情況下增強代碼功能。

Spring

基于

AspectJ

架構實作

AOP

功能。

AOP

中的常見術語:

1.連接配接點(JoinPoint):類中可以被增強的方法稱為連接配接點
2.切入點(PointCut):連接配接點中實際被增強的方法稱為切入點
3.增強(Advice):切入點中增加的邏輯部分稱為增強
    (1).前置增強:在原方法執行之前執行的增強為前置增強
    (2).後置增強:在原方法執行之後執行的增強為後置增強
    (3).環繞增強:在原方法執行之前及之後執行的增強為環繞增強
    (4).異常增強:原方法發生異常之後執行的增強為異常增強(等同于catch(...){...})
    (5).最終增強:原方法執行之後必定執行的增強為最終增強(等同于finally{...})
4.切面(Aspect):将增強應用到切入點的動作稱為切面
           

切入點表達式

  切入點表達式是一個字元串資訊,用于指定切入點的資訊(例如切入點的權限、傳回、所在類的路徑及參數清單等資訊),切入點表達式使得

Spring

能夠精确定位到目标切入點。切入點表達式的基本形式為:

/* 基本形式 */
execution([權限修飾] [傳回類型] [類全路徑.方法名稱](參數清單));
/* 例 */
execution(private int com.xxx.test.add(int,java.lang.String));  

/* 使用通配 */

/* 省略權限修飾表示任意權限 */
execution(int com.xxx.test.add(int,java.lang.String)); 

/* "*"通配傳回類型表示任意傳回類型 */
execution(* com.xxx.test.add(int,java.lang.String)); 

/* "*"通配方法名表示指定包下的所有方法 */
execution(* com.xxx.test.*(int,java.lang.String));

/* "*"通配類名表示指定包下的類的指定方法 */
execution(* com.xxx.*.*(int,java.lang.String)); 

/* "*"通配參數清單表示任意數量及類型的參數,前提是必須有參數 */
execution(* com.xxx.test.add(*)); 

/* ".."通配參數清單表示任意數量及類型的參數,有參無參均可 */
execution(* com.xxx.test.add(..)); 
           

切面操作

添加依賴

  使用

Spring

AOP

操作需要添加以下依賴:

<!-- 添加aspectjweaver依賴 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>
           

開啟掃描器

使用配置檔案

  引入命名空間,指定基礎掃描包:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 步驟1:在beans标簽中添加context命名空間(IDEA可自動導入) -->
<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
                           https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           https://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 步驟2:指定基礎掃描包,多個包之間使用","分隔 -->
    <context:component-scan base-package="com.xxx.package1,com.xxx.package2"/>

</beans>
           

  加載配置檔案:

public class App {
    public static void main(String[] args) {
        /* 加載配置檔案 */
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        /* 擷取Bean對象 */
        Bean bean = (Bean)context.getBean("bean");
    }
}
           

使用注解

  定義配置類:

/* @Configuration指定類為Spring的配置類;
   @ComponentScan的basePackages屬性指定基礎掃描包,多個基礎掃描包通過數組形式指定  */
@Configuration
@ComponentScan(basePackages = {"com.xxx.package1","com.xxx.package2"})
public class SpringConfiguration {
    // do nothing
}
           

  加載配置類:

public class App {
    public static void main(String[] args) {
        /* 使用AnnotationConfigApplicationContext類加載配置類 */
        ApplicationContext context = 
            new AnnotationConfigApplicationContext(SpringConfiguration.class);
        /* 擷取Bean對象 */
        Bean bean = (Bean)context.getBean("bean");
    }
}
           

啟用代理

使用配置檔案

  引入命名空間,啟用生成代理對象:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 步驟1:在beans标簽中添加aop命名空間(IDEA可自動導入) -->
<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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           https://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/aop
                           https://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- 開啟AspectJ自動代理 -->
    <aop:aspectj-autoproxy/>

</beans>
           

使用注解

  定義配置類:

/* @Configuration指定類為Spring的配置類 */
@Configuration
/* @ComponentScan的basePackages屬性指定基礎掃描包,多個基礎掃描包通過數組形式指定 */
@ComponentScan(basePackages = {"com.xxx.package1","com.xxx.package2"})
/* @EnableAspectJAutoProxy的proxyTargetClass屬性用于開啟自動代理 */
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfiguration {
    // do nothing
}
           

被增強類

@Component(value = "bean")
public class Bean {
    /* 切入點 */
    public void test() {
        System.out.println("Hello Spring!");
    }
}
           

增強類

/* @Component注解用于生成執行個體 */
@Component(value = "beanProxy")
/* @Aspect注解用于生成代理對象 */
@Aspect
public class BeanProxy {
    /* 前置增強 */
    @Before(value = "execution(* com.oner.beans.Bean.test(..))")
    public void before() {
        System.out.println("before...");
    }
    
    /* 後置增強 */
    @After(value = "execution(* com.oner.beans.Bean.test(..))")
    public void after() {
        System.out.println("after...");
    }
    
    /* 後置增強:傳回後執行 */
    @AfterReturning(value = "execution(* com.oner.beans.Bean.test(..))")
    public void afterReturning() {
        System.out.println("after returning...");
    }
    
    /* 後置增強:異常後執行 */
    @AfterThrowing(value = "execution(* com.oner.beans.Bean.test(..))")
    public void afterThrowing() {
        System.out.println("after throwing...");
    }
    
    /* 環繞增強 */
    @Around(value = "execution(* com.oner.beans.Bean.test(..))")
    public void around(ProceedingJoinPoint pjp) {
        System.out.println("before proceeding...");
        try{
            /* proceed()方法用于執行被增強方法 */
            pjp.proceed();
        } catch(Throwable t) {
            t.printStackTrace();
        }
        System.out.println("after proceeding...");
    }
}
           

  各種增強的執行順序:

無異常:
1.前置環繞增強(@Around,proceed()之前)
2.前置增強(@Before)
3.切入點
4.後置傳回增強(@AfterReturning)
5.後置增強(@After)
6.後置環繞增強(@Around,proceed()之後)

有異常:
1.前置環繞增強(@Around,proceed()之前)
2.前置增強(@Before)
3.切入點
4.後置異常增強(@AfterThrowing)
5.後置增強(@After)
6.後置環繞增強(@Around,proceed()之後)
           

切入點抽取

  可以将切入點表達式抽取為一個對象,簡化代碼:

/* @Component注解用于生成執行個體 */
@Component(value = "beanProxy")
/* @Aspect注解用于生成代理對象 */
@Aspect
public class BeanProxy {
    
    /* 定義空方法,使用@Pointcut注解抽取切入點 */
    @Pointcut(value = "execution(* com.oner.beans.Bean.test(..))")
    public void pointcut() {}
    
    /* 前置增強 */
    /* 傳入抽取方法名引用切入點 */
    @Before(value = "pointcut()")
    public void before() {
        System.out.println("before...");
    }
    
    /* 後置增強 */
    @After(value = "pointcut()")
    public void after() {
        System.out.println("after...");
    }
    
    /* 後置增強:傳回後執行 */
    @AfterReturning(value = "pointcut()")
    public void afterReturning() {
        System.out.println("after returning...");
    }
    
    /* 後置增強:異常後執行 */
    @AfterThrowing(value = "pointcut()")
    public void afterThrowing() {
        System.out.println("after throwing...");
    }
    
    /* 環繞增強 */
    @Around(value = "pointcut()")
    public void around(ProceedingJoinPoint pjp) {
        System.out.println("before proceeding...");
        try{
            /* proceed()方法用于執行被增強方法 */
            pjp.proceed();
        } catch(Throwable t) {
            t.printStackTrace();
        }
        System.out.println("after proceeding...");
    }
}
           

注解詳解

被增強類

@Component(value = "bean")
/* 修改被增強類 */
public class Bean {
    public String test(int a,int b) {
        return a+b+"";
    }
}
           

@Pointcut

  

@Pointcut

注解的定義為:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Pointcut {
    /* 指定切入點表達式 */
    String value() default "";
    /* 指定抽取方法的參數名 */
    String argNames() default "";
}
           

  

@Pointcut

注解進階用法:

/* 切入點表達式中的切入訓示"&&args(...)"用于為切入點參數指定别名;
   argNames屬性用于指定抽取方法的參數名;
   切入點參數别名 && argNames指定的抽取方法參數名 && 抽取方法參數名 三者必須相同;
   通過這種方式可以将 抽取方法參數 映射到 切入點參數 提供增強方法使用 */
@Pointcut(value = "execution(* com.oner.beans.Bean.test(..)) && args(arg1,arg2)",
          argNames = "arg1,arg2")
public void pointcut(int arg1,int arg2) {}
           

  增強方法通過

@Pointcut

注解擷取切入點參數:

/* 傳入抽取方法名引用切入點;
   抽取方法傳入增強方法參數名;
   通過這種方式可以将 增強方法參數 映射到 切入點參數 */
@Before(value = "pointcut(var1,var2)")
public void before(int var1,int var2) {
    /* 通過增強方法參數擷取切入點參數 */
    System.out.println("argument 1 is " + var1 + " , argument 2 is " + var2);
}
           

@Before

  

@Before

注解的定義為:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Before {
    /* value用于指定切入點表達式 */
    String value();
    /* argNames用于擷取切入點參數清單 */
    String argNames() default "";
}
           

  擷取切入點參數:

/* @Component注解用于生成執行個體 */
@Component(value = "beanProxy")
/* @Aspect注解用于生成代理對象 */
@Aspect
public class BeanProxy {
    
    /* 切入訓示"&&args(...)"用于為切入點的參數指定别名;
       argNames屬性用于指定抽取方法的參數名;
       通過這種方式可以将 切入點參數 映射到 抽取方法參數 */
    @Before(value = "execution(* com.oner.beans.Bean.test(..)) && args(arg1,arg2)",
            argNames = "arg1,arg2")
    public void before(int arg1,int arg2) {
        System.out.println("before...");
        /* 通過增強方法參數擷取切入點參數 */
        System.out.println("argument 1 is " + arg1 + " , argument 2 is " + arg2);
    }
}
           

  使用切入點抽取方法擷取切入點參數:

/* @Component注解用于生成執行個體 */
@Component(value = "beanProxy")
/* @Aspect注解用于生成代理對象 */
@Aspect
public class BeanProxy {
    
    /* 定義空方法,使用@Pointcut注解抽取切入點;
       切入訓示"&&args(...)"用于為切入點的參數指定别名;
       @Pointcut注解的argNames屬性用于指定抽取方法的參數名;
       通過這種方式可以将 切入點參數 映射到 抽取方法參數 */
    @Pointcut(value = "execution(* com.oner.beans.Bean.test(..)) && args(arg1,arg2)",
              argNames = "arg1,arg2")
    public void pointcut(int arg1,int arg2) {}
    
    /* 前置增強 */
    /* 傳入抽取方法名引用切入點;
       抽取方法傳入增強方法參數名;
       通過這種方式可以将 增強方法參數 映射到 切入點參數 */
    @Before(value = "pointcut(var1,var2)")
    public void before(int var1,int var2) {
        System.out.println("before...");
        /* 通過增強方法參數擷取切入點參數 */
        System.out.println("argument 1 is " + var1 + " , argument 2 is " + var2);
    }
}
           

@After

  

@After

注解的定義為:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface After {
    /* 指定切入點表達式 */
    String value();
    /* 指定增強方法參數名 */
    String argNames() default "";
}
           

  

@After

注解的進階用法與

@Before

注解相同。

@Around

  

@Around

注解的定義為:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Around {
    /* 指定切入點表達式 */
    String value();
    /* 指定增強方法參數名 */
    String argNames() default "";
}
           

  

@Around

注解的進階用法與

@Before

注解相同。

@AfterReturning

  

@AfterReturning

注解的定義為:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AfterReturning {
    /* 指定切入點表達式 */
    String value() default "";
    String pointcut() default "";
    /* 用于擷取切入點傳回值 */
    String returning() default "";
    /* 用于擷取切入點參數 */
    String argNames() default "";
}
           

  

@AfterReturning

注解的進階用法:

/* @Component注解用于生成執行個體 */
@Component(value = "beanProxy")
/* @Aspect注解用于生成代理對象 */
@Aspect
public class BeanProxy {
    
    /* 定義空方法,使用@Pointcut注解抽取切入點;
       切入訓示"&&args(...)"用于為切入點的參數指定别名;
       @Pointcut注解的argNames屬性用于指定抽取方法的參數名;
       通過這種方式可以将 切入點參數 映射到 抽取方法參數 */
    @Pointcut(value = "execution(* com.oner.beans.Bean.test(..)) && args(arg1,arg2)",
              argNames = "arg1,arg2")
    public void pointcut(int arg1,int arg2) {}
    
    /* returning屬性用于指定用來擷取切入點傳回值的增強方法參數名 */
    @AfterReturning(value = "pointcut(var1,var2)",returning = "rtn")
    public void afterReturning(int var1,int var2,String rtn) {
        System.out.println("after returning...");
        /* 通過增強方法參數擷取切入點參數 */
        System.out.println("argument 1 is " + var1 + " , argument 2 is " + var2);
        /* 通過增強方法參數擷取切入點傳回值 */
        System.out.println("returning is " + rtn);
    }
}
           

@AfterThrowing

  

@AfterThrowing

注解的定義為:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AfterThrowing {
    /* 指定切入點表達式 */
    String value() default "";
    String pointcut() default "";
    /* 用于擷取切入點抛出的異常對象 */
    String throwing() default "";
    /* 用于擷取切入點參數 */
    String argNames() default "";
}
           

  

@AfterThrowing

注解的進階用法:

/* @Component注解用于生成執行個體 */
@Component(value = "beanProxy")
/* @Aspect注解用于生成代理對象 */
@Aspect
public class BeanProxy {
    
    /* 定義空方法,使用@Pointcut注解抽取切入點;
       切入訓示"&&args(...)"用于為切入點的參數指定别名;
       @Pointcut注解的argNames屬性用于指定抽取方法的參數名;
       通過這種方式可以将 切入點參數 映射到 抽取方法參數 */
    @Pointcut(value = "execution(* com.oner.beans.Bean.test(..)) && args(arg1,arg2)",
              argNames = "arg1,arg2")
    public void pointcut(int arg1,int arg2) {}
    
    /* throwing屬性用于指定用來擷取切入點傳回值的增強方法參數名 */
    @AfterThrowing(value = "pointcut(var1,var2)",throwing = "e")
    public void afterThrowing(int var1,int var2,Throwable e) {
        System.out.println("after throwing...");
        /* 通過增強方法參數擷取切入點參數 */
        System.out.println("argument 1 is " + var1 + " , argument 2 is " + var2);
        /* 通過增強方法參數擷取切入點抛出的異常 */
        e.printStackTrace();
    }
}
           

連接配接點

JoinPoint

接口

  

JoinPoint

接口用于表示連接配接點,通過

JoinPoint

對象可以擷取連接配接點的參數清單、傳回值等資訊。

JoinPoint

接口的常用方法有:

/* 擷取切入點所在的對象 */
Object getTarget();
/* 擷取切入點的參數清單 */
Object[] getArgs();
           

  應用示例,被增強類:

@Component(value = "bean")
public class Bean {
    public String test(int a,int b) {
        return a+b+"";
    }
    public void hello() {
        System.out.println("Hello world!");
    } 
}
           

  增強類:

@Component(value = "beanProxy")
@Aspect
public class BeanProxy {
    /* 前置增強 */
    @Before(value = "execution(* com.oner.beans.Bean.test(..))")
    public void before(JoinPoint point) {
        System.out.println("before...");
        
        /* 擷取切入點的參數清單 */
        Object[] args = point.getArgs();
        for(Object obj : args) {
            System.out.println(obj);
        }
        
        /* 擷取切入點所在對象 */
        Bean bean = (Bean)point.getTarget();
        /* 調用切入點所在對象的成員 */
        bean.hello();
    }
}
           

ProceedingJoinPoint

接口

  

ProceedingJoinPoint

接口是

JoinPoint

接口的子接口,用于環繞增強,常用方法有:

/* ProceedingJoinPoint接口是JoinPoint接口的子接口 */
public interface ProceedingJoinPoint extends JoinPoint {
    /* 執行切入點,擷取切入點的傳回值 */
    Object proceed() throws Throwable;
    /* 改變參數執行切入點,擷取切入點的傳回值,參數args傳入要改變的參數 */
    Object proceed(Object[] args) throws Throwable;
}
           

  應用示例,被增強類:

@Component(value = "bean")
public class Bean {
    public String test(int a,int b) {
        return a+b+"";
    }
    public void hello() {
        System.out.println("Hello world!");
    } 
}
           

  增強類:

@Component(value = "beanProxy")
@Aspect
public class BeanProxy {
    /* 環繞增強 */
    @Around(value = "execution(* com.oner.beans.Bean.test(..))")
    public void around(ProceedingJoinPoint pjp) {
        System.out.println("before proceeding...");
        try{
            /* 改變切入點的參數執行切入點,擷取切入點的傳回值 */
            String rtn = (String)pjp.proceed(new Integer[]{50,50});
            System.out.println("returning of pointcut is " + rtn);
        } catch(Throwable t) {
            t.printStackTrace();
        }
        System.out.println("after proceeding...");
    }
}
           

SpringMVC

  

SpringMVC

是一個基于

Spring

Web

開發架構。可以了解為

Servlet

的更新。

SpringMVC

的工作原理為:

1.使用Servlet對象DispatcherServlet接收使用者請求
2.賦予@Controller注解處理請求的新功能
3.DispatcherServlet對象将請求轉發至@Controller注解生成的執行個體(控制對象)處理
[email protected]注解生成的執行個體(控制對象)響應資料
           

項目建立

Maven

多子產品

1.建立parent子產品
    (1).File -> New -> Project -> Maven,不選擇模闆(不勾選Create from archetype)
    (2).設定項目名稱、工作空間等
    (3).設定項目坐标和版本
2.在parent子產品下建立dao子產品
    (1).右擊parent -> New -> Module -> Maven
    (2).選擇普通Java項目模闆(勾選Create from archetype,
        并選擇org.apache.maven.archetypes:maven-archetype-quickstart)
    (3).設定子產品名稱、工作空間
    (4).設定子產品坐标和版本
    (5).配置Maven
3.在parent子產品下建立service子產品
    (1).右擊parent -> New -> Module -> Maven
    (2).選擇普通Java項目模闆(勾選Create from archetype,
        并選擇org.apache.maven.archetypes:maven-archetype-quickstart)
    (3).設定子產品名稱、工作空間
    (4).設定子產品坐标和版本
    (5).配置Maven
4.在parent子產品下建立controller子產品
    (1).右擊parent -> New -> Module -> Maven
    (2).選擇JavaWeb項目模闆(勾選Create from archetype,
        并選擇org.apache.maven.archetypes:maven-archetype-webapp)
    (3).設定子產品名稱、工作空間
    (4).設定子產品坐标和版本
    (5).配置Maven
5.修改每個pom.xml(修改JDK版本,去除不必要的部分,添加插件等)
6.添加必要檔案夾(src/main和src/test中的java檔案夾和resources檔案夾)
           

添加依賴

<dependecies>
    <!-- Servlet依賴 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <!-- Spring WebMVC依賴 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    ...
</dependecies>
    
<build>
    <plugins>
        <!-- Tomcat伺服器插件 -->
        <plugin>
            <!-- 插件參數 -->
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <!-- Tomcat伺服器參數 -->
            <configuration>
                <!-- 端口号 -->
                <port>8080</port>
                <!-- 站點對外通路路徑,必須是以"/"開頭的字元串 -->
                <path>/test</path>
                <!-- 字元集編碼,預設為ISO-8859-1 -->
                <uriEncoding>UTF-8</uriEncoding>
                <!-- 伺服器名 -->
                <server>tomcat7</server>
            </configuration>
        </plugin>
        ...
    </plugins>
    ...
</build>
           

核心配置

  核心對象

DispatcherServlet

被建立時調用

init()

方法實作的功能:

1.加載SpringMVC配置檔案,建立容器對象:
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-mvc-config.xml");
2.将容器對象存入ServletContext域中,項目中可以直接擷取該對象:
    obj.setAttribute(key,context);
           

  

SpringMVC

的核心配置需要修改

web.xml

檔案并添加

SpringMVC

配置檔案:

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">
    
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 指定SpringMVC配置檔案路徑 -->
        <init-param>
            <!-- 固定的初始化參數名 -->
            <param-name>contextConfigLocation</param-name>
            <!-- 指定SpringMVC配置檔案的類路徑 -->
            <param-value>classpath:spring-mvc-config.xml</param-value>
        </init-param>
        <!-- 預設為-1,即伺服器啟動後不建立該Servlet,此标簽必須置于最後 -->
        <load-on-startup>0</load-on-startup>
    </servlet>
    
    <!-- 路徑映射 -->
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <!-- 使用SpringMVC架構時,url-pattern可以是兩種類型:
             1.自定義擴充名,例如"*.do,*.action...",指定通路路徑為指定擴充名時,
               交由DispatcherServlet處理
             2."/"指定所有請求均交由DispatcherServlet處理 -->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    
</web-app>
           

  在

Controller

子產品的

resources

目錄中添加

SpringMVC

配置檔案(與

Spring

的配置檔案相同):

spring-mvc-config.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
                           https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           https://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 開啟元件掃描器并指定基礎掃描包 -->
    <context:component-scan base-package="com.oner.controllers"/>

</beans>
           

改變資源路徑

  

Web

工程的目錄結構為:

Controller
    |---src
        |---main
            |---java
            |---resources
            |---webapp
                |---WEB-INF
                    |---web.xml
                |---index.jsp
           

  

webapp

根目錄下的資源(例如

index.html

)對使用者公開,使用者可使用站點名+資源名的形式直接通路,但這樣會破壞伺服器端的請求邏輯,導緻使用者無法得到正确的響應。

webapp/WEB-INF

目錄下的資源對使用者不可見,使用者無法直接通路,是以可将非公開資源置于

webapp/WEB-INF

目錄下。

Controller
    |---src
        |---main
            |---java
            |---resources
            |---webapp
                |---WEB-INF
                    |---view
                        |---hello.html
                    |---web.xml
                |---index.html
           

建立控制類

  建立控制類,在控制類中建立控制方法,使用控制方法處理請求。

@Controller(value = "myController")
public class MyController {
    
    /* 建立控制方法,@RequestMapping指定映射路徑,請求路徑可以不以"/"開頭 */
    @RequestMapping(value = "test.do")
    public ModelAndView test() {
        System.out.println("Hello world!");
        ModelAndView mv = new ModelAndView();
        /* 指定視圖,即請求轉發,相當于request.getRequestDispatcher(...).forward() */
        mv.setViewName("/WEB-INF/view/hello.html");
        return mv;
    }
}
           

  通路

http://localhost:8080/oner/test.do

即可通路到指定資源。

視圖解析器

  通路視圖時,每次加上

"/WEB-INF/view/xxx.html"

會導緻代碼備援,可以配置視圖解析器簡化資源的通路路徑。在

SpringMVC

配置檔案下添加一下内容:

spring-mvc-config.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
                           https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           https://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 開啟元件掃描器并指定基礎掃描包 -->
    <context:component-scan base-package="com.oner.controllers"/>
    
    <!-- 配置内部資源視圖解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 内部資源通路路徑的字首 -->
        <property name="prefix" value="/WEB-INF/view/"/>
        <!-- 内部資源通路路徑的字尾 -->
        <property name="suffix" value=".html"/>
    </bean>

</beans>
           

  視圖解析器配置的是視圖的通路路徑,并非請求的通路路徑。配置之後的内部資源通路:

@Controller(value = "myController")
public class MyController {
    
    @RequestMapping(value = "test.do")
    public ModelAndView test() {
        System.out.println("Hello world!");
        ModelAndView mv = new ModelAndView();
        /* 省略前字尾 */
        mv.setViewName("hello");
        return mv;
    }
}
           

請求處理過程

1.請求發起
2.Tomcat伺服器根據web.xml将請求交由DispatcherServlet處理
3.DispatcherServlet掃描指定的基礎包,根據注解将請求交由控制類的控制方法處理
4.控制方法處理請求,傳回ModelAndView對象
5.SpringMVC架構擷取控制方法傳回的ModelAndView對象,将請求轉發至指定路徑
           

處理器深入

  控制類中用于處理請求的方法稱為處理器(

Handler

)。

@RequestMapping

@RequestMapping

的定義

/* @RequestMapping可以修飾方法和類 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    /* RequestMapping的名稱 */
    String name() default "";
    
    /* 映射路徑,一個處理器可映射多個路徑,與path屬性等價 */
    @AliasFor("path")
    String[] value() default {};
    
    /* 映射路徑,一個處理器可映射多個路徑,與value屬性等價 */
    @AliasFor("value")
    String[] path() default {};
    
    /* 指定可接受的請求方法,enum類型,可取值為:
       RequestMethod{GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS,TRACE} */
    RequestMethod[] method() default {};
    
    /* 指定可接受的請求參數格式 */
    String[] params() default {};
    
    /* 指定可接受的請求頭的格式 */
    String[] headers() default {};
    
    String[] consumes() default {};
    String[] produces() default {};
}
           

請求路徑

  

@RequestMapping

注解的

value(path)

屬性為數組類型,可指定一個處理器映射多個路徑。

@Controller(value = "myController")
public class MyController {
    
    /* 一個處理器映射多個路徑 */
    @RequestMapping(value = {"test1.do","test2.do","test3.do"})
    public ModelAndView test() {...}
}
           

請求方法

  

@RequestMapping

注解的

method

屬性為數組類型,指定處理器可接受哪些請求方法,如果使用者發送的請求方法不符合

method

屬性,将無法通路指定資源。

@Controller(value = "myController")
public class MyController {
    
    @RequestMapping(value = "test.do",
                    method = {RequestMethod.GET,RequestMethod.POST})
    public ModelAndView test() {...}
}
           

請求參數

  

@RequestMapping

注解的

params

屬性為數組類型,指定處理器可接受的請求參數格式,如果使用者發送的請求參數格式不符合

params

屬性,将無法通路指定資源。

params

屬性具有簡單的表達式文法:

1.多個參數表達式之間為邏輯與關系
@RequestMapping(params = {"...","...","..."})
    
2.params的值或params成員的值為"參數名"的形式,指定請求必須攜帶該參數
@RequestMapping(params = {"param1","param2"})    
    
3.params的值或params成員的值為"!參數名"的形式,指定請求必須不攜帶該參數
@RequestMapping(params = {"param1","!param2"})   
    
4.params的值或params成員的值為"參數名=值"的形式,指定請求必須攜帶該參數且等于指定值 
@RequestMapping(params = {"name=Tony","password=123456"})
    
5.params的值或params成員的值為"參數名!=值"的形式,指定請求必須攜帶該參數且不等于指定值  
@RequestMapping(params = {"name=Tony","password=123456","sex!=m"})    
           

  示例:

@Controller(value = "myController")
public class MyController {
    
    @RequestMapping(value = "test.do",
                    params = {"id","name=Tony","password=123456","sex!=m"})
    public ModelAndView test() {...}
}
           

請求頭

  

@RequestMapping

注解的

heasers

屬性為數組類型,指定處理器可接受的請求頭格式,如果使用者發送的請求頭格式不符合

headers

屬性,将無法通路指定資源。

headers

屬性的用法與

params

屬性一緻:

@Controller(value = "myController")
public class MyController {
    
    @RequestMapping(value = "test.do",
                    headers = {"Accept-Language=en-US,zh"})
    public ModelAndView test() {...}
}
           

@RequestMapping

修飾類

  

@RequestMapping

修飾類可實作公共路徑抽取。

/* 抽取公共路徑 */
@RequestMapping(value="test")
@Controller(value = "myController")
public class MyController {
    
    /* 實際路徑為"/test/test1.do" */
    @RequestMapping(value = "test1.do")
    public ModelAndView test1() {...}
    
    /* 實際路徑為"/test/test2.do" */
    @RequestMapping(value = "test2.do")
    public ModelAndView test2() {...}
    
    /* 實際路徑為"/test/test3.do" */
    @RequestMapping(value = "test3.do")
    public ModelAndView test3() {...}
}
           

處理器參數

  處理器可定義一些形參,

SpringMVC

調用處理器時可自動傳入參數。

Servlet

參數

@Controller(value = "myController")
public class MyController {
    
    /* 參數清單可以是Servlet相關的參數:HttpServletRequest,HttpServletResponse,HttpSession */
    @RequestMapping(value = "test.do")
    public void test(HttpServletRequest request,
                             HttpServletResponse response,
                             HttpSession session) throws IOException,ServletException {
        /* 設定請求編碼 */
        request.setCharacterEncoding("UTF-8");
        /* 擷取請求參數 */
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        /* 設定響應内容及編碼 */
        response.setContentType("text/html;charset=UTF-8");
        String message = "user name : " + username + " , " + "password : " + password + " .";
        System.out.println(message);
        /* 向頁面輸出内容 */
        response.getOutputStream().write(message.getBytes("UTF-8"));
    }
}
           

  通路

url

:

http://localhost:8080/oner/test.do?username=Tony&password=123456
           

請求參數

  處理器的形參清單可以是與請求參數同名的參數,這種情況下

SpringMVC

将根據請求中的參數名查詢處理器中的形參名,如果有同名形參,則将參數值傳遞給形參。

@Controller(value = "myController")
public class MyController {
    
    /* 使用同名形參擷取請求參數 */
    @RequestMapping(value = "test.do")
    public void test(HttpServletResponse response,
                     String username,String password) throws IOException,ServletException {
        /* 設定響應内容及編碼 */
        response.setContentType("text/html;charset=UTF-8");
        String message = "user name : " + username + " , " + "password : " + password + " .";
        System.out.println(message);
        /* 向頁面輸出内容 */
        response.getOutputStream().write(message.getBytes("UTF-8"));
    }
}
           

  通路

url

:

http://localhost:8080/oner/test.do?username=Tony&password=123456
           

編碼過濾器

  在

web.xml

中添加以下内容,可解決所有請求及響應的編碼問題:

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">
    
    ...
    
    <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>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <!-- 強制響應使用設定的編碼格式 -->
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <!-- 過濾器路徑映射 -->
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <!-- 對所有路徑有效 -->
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
</web-app>
           

@RequestParam

  

@RequestParam

注解為處理器參數起别名,用于解決請求參數與處理器參數名稱不一緻的問題。定義為:

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
    
    /* 參數别名,與name等價 */
    @AliasFor("name")
    String value() default "";
    
    /* 參數别名,與value等價 */
    @AliasFor("value")
    String name() default "";
    
    /* 指定請求中是否必須攜帶對應的請求參數,
       為true時,如果請求中未攜帶對應的請求參數,将無法通路指定資源 */
    boolean required() default true;
    
    String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
           

  示例:

@Controller(value = "myController")
public class MyController {
    
    /* @RequestParam的value屬性為處理器參數起别名;
       required屬性指定請求中是否必須攜帶對應的請求參數 */
    @RequestMapping(value = "test.do")
    public void test(HttpServletResponse response,
                     @RequestParam(value="username",required=false)String user,
                     @RequestParam(value="password",required=false)String pswd) 
        throws IOException,ServletException {
        /* 設定響應内容及編碼 */
        response.setContentType("text/html;charset=UTF-8");
        String message = "user name : " + user + " , " + "password : " + pswd + " .";
        System.out.println(message);
        /* 向頁面輸出内容 */
        response.getOutputStream().write(message.getBytes("UTF-8"));
    }
}
           

  通路

url

:

http://localhost:8080/oner/test.do?username=Tony&password=123456
           

對象參數

  處理器的參數為

pojo

對象時,

SpringMVC

将根據請求參數名查詢

pojo

對象屬性名,如果查詢到同名屬性,則将參數值賦給屬性,

SpringMVC

還支援

pojo

對象中的級聯屬性。示例

pojo

:

public class Employee {
    private int id;
    private String name;
    private Department department;
    
    public Employee() {super();}
    
    public Employee(int id,String name,Department department) {
        this.id = id;
        this.name = name;
        this.department = department;
    }
    
    public int getId() {return id;}
    public void setId(int id) {this.id = id;}
    
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    
    public Department getDepartment() {return department;}
    public void setDepartment(Department department) {this.department = department;}
    
    @Override
    public String toString() {
        return "employee id : " + id + " , " 
            + "employee name : " + name + " , " 
            + department.toString();
    }
}
           

  級聯屬性:

public class Department {
    private int id;
    private String name;
    
    public Department() {super();}
    
    public Department(int id,String name) {
        this.id = id;
        this.name = name;
    }
    
    public int getId() {return id;}
    public void setId(int id) {this.id = id;}
    
    public String getName() {return name;}
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "department id : " + id + " , " 
            + "department name : " + name + " .";
    }
}
           

  示例:

@Controller(value = "myController")
public class MyController {

    @RequestMapping(value = "test.do")
    public void test(HttpServletResponse response,Employee employee) 
        throws IOException,ServletException {
        /* 設定響應内容及編碼 */
        response.setContentType("text/html;charset=UTF-8");
        System.out.println(employee);
        /* 向頁面輸出内容 */
        response.getOutputStream().write(employee.toString().getBytes("UTF-8"));
    }
}
           

  通路

url

:

# department.id和department.name均為級聯屬性
http://localhost:8080/oner/test.do?id=1&name=Tony&department.id=1&department.name=development
           

處理器傳回

  處理器的傳回結果為視圖,視圖即與頁面展示相關的資料,如靜态資源(傳回靜态資源的路徑),

JSON

資料(傳回

JSON

)等。類型可以是

void

String

、對象和

ModelAndView

.

空傳回

  空傳回處理器适用不傳回視圖的場景。

@Controller(value = "myController")
public class MyController {
    
    /* 空傳回處理器适用不反回視圖場景 */
    @RequestMapping(value = "test.do")
    public void test(HttpServletRequest request,
                             HttpServletResponse response,
                             HttpSession session) throws IOException,ServletException {
        ...
    }
}
           

傳回靜态資源

  處理器的傳回類型為

String

時,代表傳回靜态資源的路徑,處理器傳回後頁面将呈現該靜态資源。

@Controller(value = "myController")
public class MyController {
    
    /* 傳回的字元串代表請求轉發的路徑 */
    @RequestMapping(value = "test.do")
    public String test() {
        System.out.println("test success.");
        /* 請求轉發至路徑(配置了視圖處理器)/WEB-INF/view/hello.html */
        return "hello";
    }
}
           

  處理器傳回

String

類型且使用

@ResponseBody

注解(下述介紹)時,傳回結果是

HTML

文本,即傳回的字元串内容将在頁面原樣展示。

@Controller(value = "myController")
public class MyController {
    
    /* @ResponseBody注解會将處理器的傳回結果處理為響應體,
       是以傳回字元串時,字元串将在頁面上原樣展示 */
    @ResponseBody
    @RequestMapping(value = "test.do")
    public Employee test() {
        System.out.println("test success.");
        return "Hello SpringMVC!"
    }
}
           

傳回

JSON

  處理器傳回

JSON

适用于需要響應頁面發送的

Ajax

請求的場合。通過配置,

SpringMVC

可将處理器傳回的對象處理為

JSON

響應給頁面。

SpringMVC

使用

Jackson

工具,是以需要引入依賴:

<!-- Jackson依賴 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.8</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>
           

  在

SpringMVC

的配置檔案中添加注解驅動:

spring-mvc-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入mvc命名空間(IDEA可自動導入) -->
<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
                           https://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
                           http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 開啟元件掃描器并指定基礎掃描包 -->
    <context:component-scan base-package="com.oner.controllers"/>

    <!-- 配置内部資源視圖解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 内部資源通路路徑的字首 -->
        <property name="prefix" value="/WEB-INF/view/"/>
        <!-- 内部資源通路路徑的字尾 -->
        <property name="suffix" value=".html"/>
    </bean>
    
    <!-- 添加注解驅動 -->
    <mvc:annotation-driven/>

</beans>
           

  使用

@ResponseBody

注解修飾處理器時,

@ResponseBody

注解會根據處理器的傳回資料類型,将傳回結果處理為不同類型的響應體追加到

HTTP

封包中。

@Controller(value = "myController")
public class MyController {
    
    /* 通過通路".../hello.do"轉至"...WEB-INF/view/hello.html" */
    @RequestMapping("hello.do")
    public String hello() {
        /* 轉發至"...WEB-INF/view/hello.html" */
        return "hello";
    }
    
    /* @ResponseBody的作用:
       1.将處理器傳回的對象轉換為JSON對象
       2.将JSON對象響應到頁面 */
    @ResponseBody
    @RequestMapping(value = "test.do")
    public Employee test() {
        System.out.println("test success.");
        Employee employee = new Employee(1,"Tony",new Department(1,"development"));
        /* SpringMVC将傳回的對象轉換為JSON發送至頁面 */
        return employee;
    }
}
           

  頁面使用

Ajax

接收

JSON

:

hello.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>hello</title>
        <!-- 遠端jQuery -->
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
        <style type="text/css">
            *{
                font-family: "Youyuan"
            }
        </style>
        <script type="text/javascript">
            $(function(){
                var employeeId = $("input[name='employee-id']");
                var employeeName = $("input[name='employee-name']");
                var departmentId = $("input[name='department-id']");
                var departmentName = $("input[name='department-name']");
                /* 點選按鈕後擷取JSON資料 */
                $("#get").on("click",function(){
                    $.ajax({
                        type:"POST",
                        url:"http://localhost:8080/oner/test.do",
                        async:true,
                        /* 要求響應格式為JSON,如果響應格式不正确,将跳至error回調 */
                        dataType:"json",
                        success:function(data){
                            /* 擷取JSON資料 */
                            employeeId.val(data.id);
                            employeeName.val(data.name);
                            /* 支援級聯屬性(原理是JSON嵌套) */
                            departmentId.val(data.department.id);
                            departmentName.val(data.department.name);
                        },
                        error:function(){
                            alert("error");
                        }
                    })
                });
            });
        </script>
    </head>
    <body>
        <h1>你的資訊</h1><br/>
        員工編号<input name="employee-id" type="text"/><br/>
        員工姓名<input name="employee-name" type="text"/><br/>
        部門編号<input name="department-id" type="text"/><br/>
        部門名稱<input name="department-name" type="text"/><br/>
        <button id="get">擷取資訊</button>
    </body>
</html>
           

  級聯屬性的轉換原理:

/* 級聯屬性轉換為嵌套JSON */
Employee -> {id:1,name:Tony,department:{id:1,name:development}}
           

傳回

List

  處理器傳回數組或

List

時,

SpringMVC

架構會将數組或

List

轉換為

JSON

數組響應給頁面。

/* List轉換為JSON數組 */
List<Employee> -> {{id:1,name:Tony,  department:{id:1,name:development}},
                   {id:2,name:Steven,department:{id:2,name:management},
                   {id:3,name:Ellen, department:{id:3,name:marketing}}
           

  處理器傳回

List

:

@Controller(value = "myController")
public class MyController {
    
    /* 通過通路".../hello.do"轉至"...WEB-INF/view/hello.html" */
    @RequestMapping("hello.do")
    public String hello() {
        /* 轉發至"...WEB-INF/view/hello.html" */
        return "hello";
    }
    
    /* @ResponseBody的作用:
       1.将處理器傳回的對象轉換為JSON對象
       2.将JSON對象響應到頁面 */
    @ResponseBody
    @RequestMapping(value = "test.do")
    public List<Employee> test() {
        System.out.println("test success.");
        Employee e1 = new Employee(1,"Tony",new Department(1,"development"));
        Employee e2 = new Employee(2,"Steven",new Department(2,"management"));
        Employee e3 = new Employee(3,"Ellen",new Department(3,"marketing"));
        ArrayList<Employee> list = new ArrayList<Employee>();
        list.add(e1);
        list.add(e2);
        list.add(e3);
        return list;
    }
}
           

  頁面使用

Ajax

接收

JSON

:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>hello</title>
        <!-- 遠端jQuery -->
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
        <style type="text/css">
            *{
                font-family: "Youyuan"
            }
            table,tr,td{
                border: 1px solid lightseagreen;
                padding: 10px;
            }
        </style>
        <script type="text/javascript">
            $(function(){
                
                /* 點選按鈕後擷取JSON資料 */
                $("#get").on("click",function(){
                    $.ajax({
                        type:"POST",
                        url:"http://localhost:8080/oner/test.do",
                        async:true,
                        /* 要求響應格式為JSON,如果響應格式不正确,将跳至error回調 */
                        dataType:"json",
                        success:function(data){
                            var $row;
                            for(var i=0;i<data.length;i++) {
                                $row = $("#inf").find("tr").eq(i+1);
                                /* 為表格的單元指派 */
                                $row.find("td").eq(0).html(data[i].id);
                                $row.find("td").eq(1).html(data[i].name);
                                /* 級聯屬性 */
                                $row.find("td").eq(2).html(data[i].department.id);
                                $row.find("td").eq(3).html(data[i].department.name);
                            }
                        },
                        error:function(){
                            alert("error");
                        }
                    })
                });
            });
        </script>
    </head>
    <body>
        <h1 align="center">員工資訊</h1><br/>
        <table id="inf" align="center">
            <tr>
                <td align="center">員工編号</td>
                <td align="center">員工姓名</td>
                <td align="center">部門編号</td>
                <td align="center">部門名稱</td>
            </tr>
            <tr>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
            </tr>
            <tr>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
            </tr>
            <tr>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
            </tr>
        </table>
        <br/>
        <center><button id="get">擷取資訊</button></center>
    </body>
</html>
           

傳回

Map

  處理器傳回

Map

時,

SpringMVC

架構會将

Map

轉換為

key

Map

中的

key

value

Map

中的

value

JSON

對象響應給頁面。

Map<String,Employee> -> {Tony:  {id:1,name:Tony,  department:{id:1,name:development}},
                         Steven:{id:2,name:Steven,department:{id:2,name:management}},
                         Ellen: {id:3,name:Ellen, department:{id:3,name:marketing}}}
           

  處理器傳回

Map

:

@Controller(value = "myController")
public class MyController {
    
    /* 通過通路".../hello.do"轉至"...WEB-INF/view/hello.html" */
    @RequestMapping("hello.do")
    public String hello() {
        /* 轉發至"...WEB-INF/view/hello.html" */
        return "hello";
    }
    
    /* @ResponseBody的作用:
       1.将處理器傳回的對象轉換為JSON對象
       2.将JSON對象響應到頁面 */
    @ResponseBody
    @RequestMapping(value = "test.do")
    public Map<String,Employee> test() {
        System.out.println("test success.");
        Employee e1 = new Employee(1,"Tony",new Department(1,"development"));
        Employee e2 = new Employee(2,"Steven",new Department(2,"management"));
        Employee e3 = new Employee(3,"Ellen",new Department(3,"marketing"));
        HashMap<String,Employee> map = new HashMap<String,Employee>();
        map.put(e1.getName(),e1);
        map.put(e2.getName(),e2);
        map.put(e3.getName(),e3);
        return map;
    }
}
           

  頁面使用

Ajax

接收

JSON

:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>hello</title>
        <!-- 遠端jQuery -->
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
        <style type="text/css">
            *{
                font-family: "Youyuan"
            }
            table,tr,td{
                border: 1px solid lightseagreen;
                padding: 10px;
            }
        </style>
        <script type="text/javascript">
            $(function(){
                
                /* 點選按鈕後擷取JSON資料 */
                $("#get").on("click",function(){
                    $.ajax({
                        type:"POST",
                        url:"http://localhost:8080/oner/test.do",
                        async:true,
                        /* 要求響應格式為JSON,如果響應格式不正确,将跳至error回調 */
                        dataType:"json",
                        success:function(data){
                            var $row;
                            $row = $("#inf").find("tr").eq(1);
                            $row.find("td").eq(0).html(data.Tony.id);
                            $row.find("td").eq(1).html(data.Tony.name);
                            $row.find("td").eq(2).html(data.Tony.department.id);
                            $row.find("td").eq(3).html(data.Tony.department.name);
                            
                            $row = $("#inf").find("tr").eq(2);
                            $row.find("td").eq(0).html(data.Steven.id);
                            $row.find("td").eq(1).html(data.Steven.name);
                            $row.find("td").eq(2).html(data.Steven.department.id);
                            $row.find("td").eq(3).html(data.Steven.department.name);
                            
                            $row = $("#inf").find("tr").eq(3);
                            $row.find("td").eq(0).html(data.Ellen.id);
                            $row.find("td").eq(1).html(data.Ellen.name);
                            $row.find("td").eq(2).html(data.Ellen.department.id);
                            $row.find("td").eq(3).html(data.Ellen.department.name);
                        },
                        error:function(){
                            alert("error");
                        }
                    })
                });
            });
        </script>
    </head>
    <body>
        <h1 align="center">員工資訊</h1><br/>
        <table id="inf" align="center">
            <tr>
                <td align="center">員工編号</td>
                <td align="center">員工姓名</td>
                <td align="center">部門編号</td>
                <td align="center">部門名稱</td>
            </tr>
            <tr>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
            </tr>
            <tr>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
            </tr>
            <tr>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
                <td align="center"></td>
            </tr>
        </table>
        <br/>
        <center><button id="get">擷取資訊</button></center>
    </body>
</html>
           

傳回

ModelAndView

  

ModelAndView

中的常用方法:

/* 向request域中添加屬性 */
addObject(String attributeName,Object attributeValue);
/* 設定轉發路徑 */
setViewName(String viewName);
           

@ResponseBody

修飾類

  

@ResponseBody

修飾控制器類時,指定控制器類中的所有處理器的傳回結果均為

JSON

資料或

HTML

文本。

@Controller
/* @ResponseBody修飾控制器類時,指定所有處理器的傳回結果均為JSON資料或HTML文本 */
@ResponseBody
public class MyController {
    @RequestMapping("test1.do")
    public String test1() {
        ...
        return "Hello SpringMVC.";
    }
    @RequestMapping("test2.do")
    public Employee test2() {
        ...
        return employee;
    }
}
           

@RestController

  

@RestController

注解是

@Controller

注解與

@ResponseBody

注解的結合,用于

@Controller

注解與

@ResponseBody

注解同時修飾類的場景。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
/* @RestController是@Controller與@ResponseBody的結合 */
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    /* 指定Bean類的名稱,與@Controller中的value屬性作用相同 */
    String value() default "";
}
           

  

@RestController

修飾控制器類時,控制器類中的所有處理器的傳回結果均為

JSON

HTML

文本。

/* @RestController(value = "myController")是
   @Controller(value = "myController")注解與@ResponseBody注解的結合 */
@RestController(value = "myController")
public class MyController {
    @RequestMapping("test1.do")
    public String test1() {
        ...
        /* 傳回HTML文本 */
        return "Hello SpringMVC.";
    }
    @RequestMapping("test2.do")
    public Employee test2() {
        ...
        /* 傳回JSON */
        return employee;
    }
}
           

異常處理

  

SpringMVC

采用

AOP

思想,使用全局統一的異常處理方式,将所有異常放在同一個位置處理,将業務邏輯與異常處理分開,實作代碼解耦。

全局異常處理類

  

SpringMVC

将業務邏輯中的所有異常統一交給一個類處理,這個類需要使用

@ControllerAdvice

注解修飾,稱為全局異常處理類。具體的異常類型由全局異常處理類中的方法處理,方法使用

@ExceptionHandler

注解修飾并指定此方法可處理的異常類型。

1.使用@ControllerAdvice注解指定全局異常處理類
2.使用@ExceptionHandler注解指定異常處理方法
[email protected]注解指定異常方法處理的異常類型
           

  

@ExceptionHandler

注解的定義為:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
    /* 指定異常處理方法可處理的異常類型,可為方法指定多個異常類型 */
    Class<? extends Throwable>[] value() default {};
}
           

  在

JavaWeb

項目下建立包并建立全局異常處理類:

package com.oner.handler;

/* @ControllerAdvice注解用于指定類為全局異常處理類 */
@ControllerAdvice
public class GlobalExceptionHandler {
    
    /* @ExceptionHandler注解用于指定方法為目标異常處理方法,
       僅有value一個屬性,用于指定目标異常類 */
    @ExceptionHandler(value = NameException.class)
    /* 異常處理方法可以像處理器方法一樣響應内容到頁面上 */
    @ResponseBody
    /* 異常處理方法中的異常參數可以用來擷取異常資訊 */
    public String handleNameException(Exception e) {
        e.printStackTrace();
        return "name wrong.";
    }
    
    @ExceptionHandler(value = PasswordException.class)
    @ResponseBody
    public String handlePasswordException(Exception e) {
        e.printStackTrace();
        return "password wrong.";
    }
    
    /* 未指定value屬性的@ExceptionHandler注解用于處理未知的其它異常 */
    @ExceptionHandler
    public String handleOthers(Exception e) {
        e.printStackTrace();
        /* 響應頁面:"WEB-INF/view/unknown-exception.html" */
        return "unknown-exception";
    }
}
           

完善配置

  添加掃描路徑(用于掃描全局異常處理類的注解),添加注解驅動。在

SpringMVC

的配置檔案中添加以下内容:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入mvc命名空間(IDEA可自動導入) -->
<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
                           https://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
                           http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 開啟元件掃描器并指定基礎掃描包,多個包之間使用","隔開 -->
    <context:component-scan base-package="com.oner.controllers,com.oner.handler"/>

    <!-- 配置内部資源視圖解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 内部資源通路路徑的字首 -->
        <property name="prefix" value="/WEB-INF/view/"/>
        <!-- 内部資源通路路徑的字尾 -->
        <property name="suffix" value=".html"/>
    </bean>
    
    <!-- 添加注解驅動 -->
    <mvc:annotation-driven/>

</beans>
           

自定義異常類

  定義

name

錯誤異常類:

package com.oner.handler.exceptions;

public class NameException extends Exception {
    public NameException() {super();}
    public NameException(String message) {super(message);}
}
           

  定義

password

錯誤異常類:

package com.oner.handler.exceptions;

public class PasswordException extends Exception {
    public PasswordException() {super();}
    public PasswordException(String message) {super(message);}
}
           

控制類抛出異常

@Controller(value = "myController")
public class MyController {
    
    @RequestMapping(value = "test.do")
    public String test(String username,String password) throws Exception {
        if(!"Tony".equals(username)) {
            throw new NameException("name wrong.");
        }
        
        if(!"123456".equals(password)) {
            throw new PasswordException("password wrong.");
        }
        
        if("Tony".equals(username) && "123456".equals(password)) {
            System.out.println("login success.");
            /* 請求轉發至"WEB-INF/view/hello.html" */
            return "hello";
        }
        return null;
    }
}
           

  通路

url

:

# 抛出NameException
http://localhost:8080/oner?username=Tom&password=123456
# 抛出PasswordException
http://localhost:8080/oner?username=Tony&password=12345
# 登陸成功
http://localhost:8080/oner?username=Tony&password=123456
           

攔截器

  攔截器與

JavaWeb

中的

Filter(過濾器)

類似,在處理請求之前先進行預處理,用于權限檢查、身份驗證等資料通路權限操作。

SpringMVC

中定義攔截器需要實作

HandlerInterceptor

接口。

HandlerInterceptor

接口

public interface HandlerInterceptor {
    
    /* preHandle()在處理器處理請求之前(SpringMVC調用處理器之前)被SpringMVC調用;
       參數request用于擷取請求資訊,可用于請求轉發
       參數response用于響應資訊;
       參數handler為處理器所在的控制器對象;
       preHandle()傳回false,請求被攔截,請求不到達處理器;傳回true,請求被放行,由處理器處理 */
    default boolean preHandle(HttpServletRequest request, 
                              HttpServletResponse response, 
                              Object handler) throws Exception {
        return true;
    }
    
    /* postHandle()在處理器處理請求之後響應之前(處理器傳回之前)被SpringMVC調用,
       用于對處理器的處理結果作二次修改;
       參數request用于擷取請求資訊;
       參數response用于響應資訊;
       參數handler為處理器所在的控制器對象;
       參數modelAndView用于修改處理器的處理結果,即修改modelAndView對象可影響最終的響應結果 */
    default void postHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler, 
                            @Nullable ModelAndView modelAndView) throws Exception {
    }
    
    /* afterCompletion()在處理器響應之後(處理器傳回之後)被SpringMVC調用,用于資源回收及異常處理
       參數request用于擷取請求資訊;
       參數response用于響應資訊;
       參數handler為處理器所在的控制器對象;
       參數exception用于擷取處理器抛出的異常資訊(如果有) */
    default void afterCompletion(HttpServletRequest request, 
                                 HttpServletResponse response, 
                                 Object handler, 
                                 @Nullable Exception exception) throws Exception {
    }
}
           

定義攔截器

  在

JavaWeb

工程下建立包并自定義攔截器類實作

HandlerInterceptor

接口:

package com.oner.handler.intercepters;

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
                              HttpServletResponse response, 
                              Object handler) throws Exception {
        System.out.println("preHandle() is executed.");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler, 
                            @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle() is executed.");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, 
                                 HttpServletResponse response, 
                                 Object handler, 
                                 @Nullable Exception exception) throws Exception {
        System.out.println("afterCompletion() is executed.");
    }
}
           

注冊攔截器

  在

SpringMVC

的配置檔案中添加以下内容:

spring-mvc-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入mvc命名空間(IDEA可自動導入) -->
<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
                           https://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
                           http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 開啟元件掃描器并指定基礎掃描包,多個包之間使用","隔開 -->
    <context:component-scan base-package="com.oner.controllers,com.oner.handler"/>

    <!-- 配置内部資源視圖解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 内部資源通路路徑的字首 -->
        <property name="prefix" value="/WEB-INF/view/"/>
        <!-- 内部資源通路路徑的字尾 -->
        <property name="suffix" value=".html"/>
    </bean>
    
    <!-- 添加注解驅動 -->
    <mvc:annotation-driven/>
    
    <!-- 可注冊多個攔截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 目标攔截器攔截的路徑,"xxx/**"可指定攔截"xxx"下的所有路徑,
                 例如"user/**"可指定攔截"user"下的所有路徑 -->
            <mvc:mapping path="test.do"/>
            <!-- 攔截器所在類路徑 -->
            <bean class="com.oner.handler.interceptors.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

</beans>
           

攔截器鍊

  當一個資源的請求路徑的不同層次的路徑設有攔截器時,這些攔截器組成攔截器鍊。攔截器鍊中的攔截器按照路徑層次從外層向内層依次攔截請求。攔截器中方法的執行順序為:

1.preHandle()按照路徑層次從外層向内層依次執行:
    interceptor1.preHandle() -> 
        interceptor2.preHandle() -> 
            interceptor3.preHandle()
2.處理器執行
3.postHandle()按照路徑層次從内層向外層依次執行:
            interceptor3.postHandle() -> 
        interceptor2.postHandle() ->
    interceptor1.postHandle()
4.視圖解析器執行
5.afterCompletion()按照路徑層次從内層向外層依次執行:
            interceptor3.afterCompletion() -> 
        interceptor2.afterCompletion() ->
    interceptor1.afterCompletion()
           

  攔截器的

preHandle()

方法傳回

false

将導緻攔截器鍊斷開,攔截器鍊斷開後,内層的請求無法到達,因而内層的處理器和攔截器不執行,攔截器鍊将從斷開位置的

postHandle()

方法開始迂回執行。

攔截器與過濾器的差別

1.過濾器用于過濾請求,攔截器用于攔截處理器的執行
2.過濾器先于攔截器執行
3.如果DipatcherServlet的請求被攔截,對應的攔截器将不執行
           

RESTful

REST

風格

  

REST(Representational State Transfer)

意為表述性狀态轉移,

RESTful

即為

REST

風格,是一種通過

URL

表示資源,使用請求方法表示對資源的操作的

Web

程式設計風格,一種人為規範,并非實際的代碼架構。傳統的

Web

程式設計中,對資源的每個操作都必須使用特的

URL

實作:

@RestController
@RequestMapping(value = "users")
public class UserController {
    
    /* 通路http://.../users/get.do查詢資訊 */
    @RequestMapping(value = "get.do")
    public User get(int id) {
        // ...
    }
    
    /* 通路http://.../users/add.do增添資訊 */
    @RequestMapping(value = "add.do")
    public String add(User user) {
        // ...
    }
    
    /* 通路http://.../users/update.do更新資訊 */
    @RequestMapping(value = "update.do")
    public String update(User user) {
        // ...
    }
    
    /* 通路http://.../users/delete.do删除資訊 */
    @RequestMapping(value = "delete.do")
    public String delete() {
        // ...
    }
}
           

  使用

RESTful

程式設計時,對同一個資源的所有操作都通路同一個

URL

,使用請求方法規定具體操作:

@RestController
/* 對user資源的所有操作都通路"http://.../users" */
@RequestMapping(value = "users")
public class UserController {
    
    /* 規定使用GET方法進行查詢操作 */
    @RequestMapping(method = RequestMethod.GET)
    public User get(int id) {
        // ...
    }
    
    /* 規定使用POST方法進行增添操作 */
    @RequestMapping(method = RequestMethod.POST)
    public String add(User user) {
        // ...
    }
    
    /* 規定使用PUT方法進行更新操作 */
    @RequestMapping(method = RequestMethod.PUT)
    public String update(User user) {
        // ...
    }
    
    /* 規定使用DELETE方法進行删除操作 */
    @RequestMapping(method = RequestMethod.DELETE)
    public String delete() {
        // ...
    }
}
           

RESTful

注解

  

SpringMVC

提供了專用的

RESTful

注解代替

@RequestMapping(method = ...)

注解。常用有:

/* @GetMapping與@RequestMapping(method = RequestMethod.GET)等價 */
@GetMapping

/* @PostMapping與@RequestMapping(method = RequestMethod.POST)等價 */
@PostMapping

/* @PutMapping與@RequestMapping(method = RequestMethod.PUT)等價 */
@PutMapping

/* @DeleteMapping與@RequestMapping(method = RequestMethod.DELETE)等價 */
@DeleteMapping

/* @PatchMapping與@RequestMapping(method = RequestMethod.PATCH)等價 */
@PatchMapping
           

  這些注解與

@RequestMapping

的差別在于

method

屬性的取值,其餘特性相同。這些注解與

@RequestMapping

的定義基本相同,以

@GetMapping

為例:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
/* 指定請求方法 */
@RequestMapping(
    method = {RequestMethod.GET}
)
public @interface GetMapping {
    
    /* 與@RequestMapping注解中的name屬性作用一緻,為路徑映射起名 */
    String name() default "";
    
    /* 與@RequestMapping注解中的value屬性作用一緻,指定通路路徑 */
    String[] value() default {};
    
    /* 與@RequestMapping注解中的path屬性作用一緻,指定通路路徑 */
    String[] path() default {};
    
    /* 與@RequestMapping注解中的param屬性作用一緻,規定請求參數的格式 */
    String[] params() default {};
    
    /* 以下為不常用屬性,與@RequestMapping注解中作用一緻 */
    String[] headers() default {};
    String[] consumes() default {};
    String[] produces() default {};
}
           

  

RESTful

注解使用示例:

@RestController(value = "users")
public class UserController {
    
    @GetMapping
    public User get(int id) {
        // ...
    }
    
    @PostMapping
    public String add(User user) {
        // ...
    }
    
    @PutMapping
    public String update(User user) {
        // ...
    }
    
    @DeleteMapping
    public String delete(int id) {
        // ...
    }
}
           

資源設計規則

  

RESTful

的核心原則在于,對同一個資源的所有操作均通路同一個

URL

,使用不同的請求方法進行不同的操作。是以,對于資源路徑的設計應當遵循以下規則:

1.每個URL都代表一個資源,例:
    http://myoner.com/users
    http://myoner.com/employees
    http://myoner.com/departments
2.URL中不能出現動詞,隻能是名詞,且所用的名詞應當與資料庫表名對應一緻
           

動作設計規則

  

RESTful

為每個請求方法都賦予了新的含義:

GET : 從伺服器查詢一條或多條資料
POST : 在伺服器上添加一條資料
PUT : 在伺服器上更新一條資料(PUT更新整個對象)
PATCH : 在伺服器上更新一條資料(PATCH更新對象的個别屬性)
DELETE : 從伺服器删除資料
HEAD : 擷取一個資源的中繼資料,例如一個資源的最後修改日期
OPTIONS : 擷取資源的API(即用戶端可對該資源進行操作的描述)
           

路徑傳參

  

RESTful

中,請求方法需要的參數通過請求路徑獲得,這種方式稱為路徑傳參。路徑傳參是

RESTful

特有的方式,比傳統的請求參數方式(

?name=value&name=value...

)更加簡潔:

@RestController
@RequestMapping(value = "users")
public class UserController {
    /* 在URL中使用路徑占位符,用于接收請求URL中的路徑參數并傳遞給處理器,
       例如http://.../users/10/Tom */
    @GetMapping(value = "{id}/{name}")
    /* 使用@PathVariable注解辨別參數,
       SpringMVC将URL中占位符的實際内容預設傳遞給處理器的同名參數 */
    public User get(@PathVariable int id,@PathVariable String name) {
        // ...
    }
}
           

  

RESTful

路徑傳參時,必須使用

@PathVariable

注解辨別處理器參數,否則

SpringMVC

會将處理器參數了解為請求參數(

URL

中的

?name=value&name=value...

部分)。

@PathVariable

注解的定義為:

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
    /* 處理器參數的别名(與name作用相同),預設為"",此時SpringMVC将讀取實際參數名 */
    @AliasFor("name")
    String value() default "";
    
    /* 處理器參數的别名(與value作用相同),預設為"",此時SpringMVC将讀取實際參數名 */
    @AliasFor("value")
    String name() default "";
    
    /* 是否要求路徑中必須帶有該參數 */
    boolean required() default true;
}
           

  為提高代碼的可讀性,建議使用

@PathVariable

注解時為處理器參數起别名:

@RestController
@RequestMapping(value = "users")
public class UserController {
    
    /* 建議明确指定處理器參數的路徑傳參名,使得路徑占位符與處理器參數之間的對應關系更加清晰 */
    @GetMapping(value = "{id}/{name}")
    public User get(@PathVariable(value = "id") int id,
                    @PathVariable(value = "name") String name) {
        // ...
    }
}
           

傳回結果規則

  

RESTful

也為處理器的傳回結果制定了規範,實際開發中可作為參考,不一定必須遵守這些規範:

GET/users : 傳回資源清單(資源對象的數組/集合)
GET/users/Tom : 傳回單個資源對象
POST : 傳回新增的資源對象
PUT : 傳回完整的被修改的資源對象
PATCH : 傳回完整的被修改的資源對象
DELETE : 傳回空文檔
           

RESTful-API

  

RESTful-API

是前後端分離項目的溝通方式。

RESTful-API

并非實際的項目代碼,而是一種描述性質的說明文檔,文檔中羅列了所有

URL

的功能,所需要的參數,傳回結果等詳細資訊。示例:

URL : http://myoner.com/users
method : GET
required : true
params : name(使用者姓名)
request headers : 
{
    "Accept" : "*/*"
}
response body : 
{
    id,
    name,
    gender,
    age
}
description : 根據姓名擷取使用者資訊