主頁 > 後端開發 > SpringMVC學習筆記 - 第一章 - 作業流程、Bean加載控制、請求與回應(引數接收與內容回傳)、RESTful

SpringMVC學習筆記 - 第一章 - 作業流程、Bean加載控制、請求與回應(引數接收與內容回傳)、RESTful

2023-01-27 06:45:04 後端開發

【前置內容】Spring 學習筆記全系列傳送門:

  • Spring學習筆記 - 第一章 - IoC(控制反轉)、IoC容器、Bean的實體化與生命周期、DI(依賴注入)

  • Spring學習筆記 - 第二章 - 注解開發、配置管理第三方Bean、注解管理第三方Bean、Spring 整合 MyBatis 和 Junit 案例

  • Spring學習筆記 - 第三章 - AOP與Spring事務

SpingMVC 學習筆記全系列傳送門:

  • 【本章】SpringMVC學習筆記 - 第一章 - 作業流程、Bean加載控制、請求與回應(引數接收與內容回傳)、RESTful

目錄
  • 1、SpringMVC概述
  • 2、SpringMVC入門案例
    • 2.1 注意事項
    • 2.2 案例制作
    • 2.3 相關知識點
    • 2.4 作業流程決議
      • 2.4.1 啟動服務器初始化程序
      • 2.4.2 單次請求程序
    • 2.5 bean 加載控制
      • 2.5.1 問題分析
      • 2.5.2 思路分析
      • 2.5.3 環境準備
      • 2.5.4 設定 bean 加載控制
      • 2.5.5 相關知識點:@ComponentScan
  • 3、請求與相應
    • 3.1 設定請求映射路徑
      • 3.1.1 環境準備
      • 3.1.2 問題分析
      • 3.1.3 設定映射路徑
    • 3.2 請求引數
      • 3.2.1 環境準備
      • 3.2.2 引數傳遞及中文亂碼處理方案
    • 3.3 五種型別引數傳遞
      • 3.3.1 普通引數
      • 3.3.2 POJO 資料型別
      • 3.3.3 嵌套 POJO 型別引數
      • 3.3.4 陣列型別引數
      • 3.3.5 集合型別引數
      • 3.3.6 相關知識點:@RequestParam
    • 3.4 JSON 資料傳輸引數
      • 3.4.1 JSON 資料傳輸引數分類與準備作業
      • 3.4.2 三種引數傳輸格式
        • 3.4.2.1 json普通陣列
        • 3.4.2.2 json物件
        • 3.4.2.3 json物件陣列
      • 3.4.3 相關知識點
    • 3.5 日期型別引數傳遞
    • 3.6 回應
      • 3.6.1 環境準備
      • 3.6.2 回應頁面(了解)
      • 3.6.3 回傳文本資料(了解)
      • 3.6.4 回應 JSON 資料
        • 3.6.4.1 回應 POJO 物件
        • 3.6.4.2 回應 POJO 集合物件
        • 3.6.4.3 相關知識點:@ResponseBody
  • 4、REST風格
    • 4.1 REST 簡介
    • 4.2 RESTful 入門案例
      • 4.2.1 環境準備
      • 4.2.2 思路分析
      • 4.2.3 修改 RESTFUL 風格
        • 4.2.3.1 各項操作
        • 4.2.3.2 相關知識點:@PathVarlable
        • 4.2.3.3 相關知識點:三種接收引數的注解
    • 4.3 RESTFUL 快速開發
      • 4.3.1 開發簡化
      • 4.3.2 相關知識點
    • 4.4 RESTFUL 案例
      • 4.4.1 需求分析
      • 4.4.2 環境準備
      • 4.4.3 后臺介面開發
      • 4.4.4 頁面訪問處理

1、SpringMVC概述

  • SpringMVC是一種基于Java實作MVC模型的輕量級Web框架

  • 優點

    • 使用簡單、開發便捷(相比于Servlet)
    • 靈活性強
  • SpringMVC主要負責的就是

    • controller如何接收請求和資料
    • 如何將請求和資料轉發給業務層
    • 如何將回應資料轉換成json發回到前端
  • 三層架構與MVC模式

    三層架構與MVC模式

    • 瀏覽器發送一個請求給后端服務器,后端服務器現在是使用Servlet來接收請求和資料

      如果所有的處理都交給Servlet來處理的話,所有的東西都耦合在一起,對后期的維護和擴展極為不利

    • 將后端服務器Servlet拆分成三層,分別是webservicedao

      • web層主要由servlet來處理,負責頁面請求和資料的收集以及回應結果給前端
      • service層主要負責業務邏輯的處理
      • dao層主要負責資料的增刪改查操作

      servlet處理請求和資料的時候,存在的問題是一個servlet只能處理一個請求

    • 針對web層進行了優化,采用了MVC設計模式,將其設計為controllerviewModel

      • controller負責請求和資料的接收,接收后將其轉發給service進行業務處理
      • service根據需要會呼叫dao對資料進行增刪改查
      • dao把資料處理完后將結果交給service,service再交給controller
      • controller根據需求組裝成Model和View,Model和View組合起來生成頁面轉發給前端瀏覽器
      • 這樣做的好處就是controller可以處理多個請求,并對請求進行分發,執行不同的業務操作,

    三層架構與MVC模式傳遞

    • 隨著互聯網的發展,上面的模式因為是同步呼叫,性能慢慢的跟不是需求,所以異步呼叫慢慢的走到了前臺,是現在比較流行的一種處理方式

      • 因為是異步呼叫,所以后端不需要回傳view視圖,將其去除
      • 前端如果通過異步呼叫的方式進行互動,后臺就需要將回傳的資料轉換成json格式進行回傳

2、SpringMVC入門案例

2.1 注意事項

  • SpringMVC是基于Spring的,在pom.xml只匯入了spring-webmvcjar包的原因是它會自動依賴spring相關坐標
  • AbstractDispatcherServletInitializer類是SpringMVC提供的快速初始化Web3.0容器的抽象類
  • AbstractDispatcherServletInitializer提供了三個介面方法供用戶實作
    • createServletApplicationContext方法,創建Servlet容器時,加載SpringMVC對應的bean并放入WebApplicationContext物件范圍中,而WebApplicationContext的作用范圍為ServletContext范圍,即整個web容器范圍
    • getServletMappings方法,設定SpringMVC對應的請求映射路徑,即SpringMVC攔截哪些請求
    • createRootApplicationContext方法,如果創建Servlet容器時需要加載非SpringMVC對應的bean,使用當前方法進行,使用方式和createServletApplicationContext相同,
    • createServletApplicationContext用來加載SpringMVC環境
    • createRootApplicationContext用來加載Spring環境

2.2 案例制作

  1. 創建 Maven-webapp 專案,整理包結構

    image-20221228060317972

  2. 匯入依賴

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>priv.dandelion</groupId>
      <artifactId>01_quickstart</artifactId>
      <version>1.0-SNAPSHOT</version>
      <packaging>war</packaging>
    
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
          <scope>provided</scope>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>5.2.10.RELEASE</version>
        </dependency>
      </dependencies>
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.1</version>
            <configuration>
              <port>80</port>
              <path>/</path>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </project>
    
    
  3. 創建 Controller

    // 定義Controller,宣告為Spring的bean
    @Controller
    public class UserController {
    
        // 設定當前操作的訪問路徑
        @RequestMapping("/save")
        // 設定當前操作的回傳值型別
        @ResponseBody
        public String save() {
            System.out.println("user----save");
            // 相應的內容直接回傳
            return "{'hello':'springmvc'}";
        }
    }
    
  4. 創建配置類

    // 創建SpringMVC的組態檔,加載controller對應的bean
    @Configuration
    @ComponentScan("priv.dandelion.controller")
    public class SpringMvcConfig {
    }
    
  5. 定義 Servlet 容器啟動的配置類(代替 web.xml

    // 定義一個servlet容器啟動的配置類,在此處加載spring配置
    public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
    
        // 加載SpringMVC容器配置
        @Override
        protected WebApplicationContext createServletApplicationContext() {
            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            // 注冊配置
            ctx.register(SpringMvcConfig.class);
            // tomcat服務器啟動時就可以加載到SpringMvcConfig.class
            return ctx;
        }
    
        // 設定那些請求歸屬于SpringMVC處理
        @Override
        protected String[] getServletMappings() {
            // 將所有請求交給SpringMVC處理
            return new String[]{"/"};
        }
    
        // 加載Spring容器配置,此處暫時未用到
        @Override
        protected WebApplicationContext createRootApplicationContext() {
            return null;
        }
    }
    

2.3 相關知識點

  • @Controller

    名稱 @Controller
    型別 類注解
    位置 SpringMVC控制器類定義上方
    作用 設定SpringMVC的核心控制器bean
  • @RequestMapping

    名稱 @RequestMapping
    型別 類注解或方法注解
    位置 SpringMVC控制器類或方法定義上方
    作用 設定當前控制器方法請求訪問路徑
    相關屬性 value(默認),請求訪問路徑
  • @ResponseBody

    名稱 @ResponseBody
    型別 類注解或方法注解
    位置 SpringMVC控制器類或方法定義上方
    作用 設定當前控制器方法回應內容為當前回傳值,無需決議

2.4 作業流程決議

包含關系:

  • Web容器
    • ServletContext
      • WebApplicationContext
        • UserController
          • /save -> save() 【SpringMVC 的映射并不是放在 bean 中管理的】

2.4.1 啟動服務器初始化程序

  1. 服務器啟動,執行 web 服務器配置類 ServletContainersInitConfig,初始化web容器

    • 功能類似于以前的web.xml
  2. 執行createServletApplicationContext方法,創建了WebApplicationContext物件(存在于 ServletContext 中)

    • 該方法加載SpringMVC的配置類SpringMvcConfig來初始化SpringMVC的容器

      // 加載SpringMVC容器配置
      @Override
      protected WebApplicationContext createServletApplicationContext() {
          AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
          // 注冊配置
          ctx.register(SpringMvcConfig.class);
          // tomcat服務器啟動時就可以加載到SpringMvcConfig.class
          return ctx;
      }
      
  3. 加載SpringMvcConfig配置類,以在下一步加載所需的bean

    // 創建SpringMVC的組態檔,加載controller對應的bean
    @Configuration
    @ComponentScan("priv.dandelion.controller")
    public class SpringMvcConfig {
    }
    
  4. 執行@ComponentScan加載對應的bean

    掃描指定包及其子包下所有類上的注解,如Controller類上的@Controller注解

  5. 加載UserController,每個@RequestMapping的名稱對應一個具體的方法

    • 此時就建立了 /save 和 save() 方法的對應關系

      // 定義Controller,宣告為Spring的bean
      @Controller
      public class UserController {
                      
          // 設定當前操作的訪問路徑
          @RequestMapping("/save")
          // 設定當前操作的回傳值型別
          @ResponseBody
          public String save() {
              System.out.println("user----save");
              // 相應的內容直接回傳
              return "{'hello':'springmvc'}";
          }
      }
      
  6. 執行getServletMappings方法,設定SpringMVC攔截請求的路徑規則

    • /代表所攔截請求的路徑規則,只有被攔截后才能交給SpringMVC來處理請求/代表所攔截請求的路徑規則,只有被攔截后才能交給SpringMVC來處理請求

      // 設定那些請求歸屬于SpringMVC處理
      @Override
      protected String[] getServletMappings() {
          // 將所有請求交給SpringMVC處理
          return new String[]{"/"};
      }
      

2.4.2 單次請求程序

  1. 發送請求http://localhost/save

  2. web容器發現該請求滿足SpringMVC攔截規則,將請求交給 SpringMVC 處理

  3. 決議請求路徑 /save

  4. 由 /save 匹配執行對應的方法 save()

    • 上面的第五步已經將請求路徑和方法建立了對應關系,通過 /save 就能找到對應的save方法
  5. 執行 save()

  6. 檢測到有 @ResponseBody 直接將 save() 方法的回傳值作為回應體回傳給請求方

2.5 bean 加載控制

2.5.1 問題分析

問題:

  • 哪些 bean 交給 SpringMVC 管理,哪些包交給 Spring 管理
  • 因為功能不同,如何避免 Spring 錯誤加載到 SpringMVC 的 bean
  • 包結構

    • config目錄存入的是配置類,本篇和前面的內容已經寫過的配置類有:

      • ServletContainersInitConfig
      • SpringConfig
      • SpringMvcConfig
      • JdbcConfig
      • MybatisConfig
    • controller 目錄存放的是 SpringMVC 的 controller 類

    • service 目錄存放的是 service 介面和實作類

    • dao 目錄存放的是 dao/Mapper 介面

  • 管理

    • SpringMVC加載其相關bean
      • 表現層 bean(Controller),也就是controller包下的類
    • Spring控制的bean
      • 業務 bean(Service)
      • 功能 bean(DataSource,SqlSessionFactoryBean,MapperScannerConfigurer等)

2.5.2 思路分析

加載Spring控制的bean的時候排除掉 SpringMVC 控制的 bean

  • 方式一:Spring加載的bean設定掃描范圍為精準范圍,例如service包、dao包等
  • 方式二:Spring加載的bean設定掃描范圍為priv.dandelion,排除掉controller包中的bean
  • 方式三:不區分Spring與SpringMVC的環境,加載到同一個環境中[了解即可]

2.5.3 環境準備

  • 創建 Web 的 Maven 專案,洗掉 web.xml 組態檔

  • 依賴

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>priv.dandelion</groupId>
        <artifactId>02_bean_load</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <dependencies>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.2.10.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.16</version>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.6</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.2.10.RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>1.3.0</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.1</version>
                    <configuration>
                        <port>80</port>
                        <path>/</path>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    
  • 創建對應的配置類

    • 替代 web.xml 的 web 服務器配置類 ServletContainersInitConfig

      public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
          protected WebApplicationContext createServletApplicationContext() {
              AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
              ctx.register(SpringMvcConfig.class);
              return ctx;
          }
          protected String[] getServletMappings() {
              return new String[]{"/"};
          }
          protected WebApplicationContext createRootApplicationContext() {
              return null;
          }
      }
      
    • SpringMVC 配置類

      @Configuration
      @ComponentScan("priv.dandelion.controller")
      public class SpringMvcConfig {
      }
      
    • Spring 配置類

      @Configuration
      @ComponentScan("priv.dandelion")
      public class SpringConfig {
      }
      
  • 物體類

    public class User {
        private Integer id;
        private String name;
        private Integer age;
        // Getter
        // Settrt
        // toString
    }
    
  • Dao 介面

    public interface UserDao {
        @Insert("insert into tbl_user(name,age)values(#{name},#{age})")
        public void save(User user);
    }
    
  • Service 實作類 (介面不表)

    @Service
    public class UserServiceImpl implements UserService {
        public void save(User user) {
            System.out.println("user service ...");
        }
    }
    
  • Controller

    @Controller
    public class UserController {
    
        @RequestMapping("/save")
        @ResponseBody
        public String save(){
            System.out.println("user save ...");
            return "{'info':'springmvc'}";
        }
    }
    

2.5.4 設定 bean 加載控制

  • web服務器啟動時加載配置類的相關配置

    • 標準方式

      • 方式
        • 修改 createRootApplicationContext() 方法中的內容
          • 與 createServletApplicationContext() 基本相同但是加載 SpringConfig.class
      • 說明
        • createServletApplicationContext() 加載的是 SpringMVC 環境配置
        • createRootApplicationContext() 加載的是 Spring 環境配置
      public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
          protected WebApplicationContext createServletApplicationContext() {
              AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
              ctx.register(SpringMvcConfig.class);
              return ctx;
          }
          protected String[] getServletMappings() {
              return new String[]{"/"};
          }
          protected WebApplicationContext createRootApplicationContext() {
              AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
              ctx.register(SpringConfig.class);
              return ctx;
          }
      }
      
    • 簡化方式

      說明:

      • AbstractDispatcherServletInitializer 類包含一個子類 AbstractAnnotationConfigDispatcherServletInitializer 可以簡化配置
      • 方法名中包含 RootConfig 的是 Spring 的配置,包含 ServletConfig 的是對 SpringMVC 的配置,與標準方式中的相同
      public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
      
          @Override
          protected Class<?>[] getRootConfigClasses() {
              return new Class[]{SpringConfig.class};
          }
      
          @Override
          protected Class<?>[] getServletConfigClasses() {
              return new Class[]{SpringMvcConfig.class};
          }
      
          @Override
          protected String[] getServletMappings() {
              return new String[]{"/"};
          }
      }public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
      
          @Override
          protected Class<?>[] getRootConfigClasses() {
              return new Class[]{SpringConfig.class};
          }
      
          @Override
          protected Class<?>[] getServletConfigClasses() {
              return new Class[]{SpringMvcConfig.class};
          }
      
          @Override
          protected String[] getServletMappings() {
              return new String[]{"/"};
          }
      }
      
  • bean 加載控制方式

    • 方案一:修改Spring 配置類,精準掃描

      說明:

      • 此處使用了MyBatis技術且是自動代理,所以可以不掃Dao
      • 但是建議按照標準開發規則書寫,通用性強
      @Configuration
      @ComponentScan({"priv.dandelion.service","priv.dandelion.dao"})
      public class SpringConfig {
      }
      
    • 方案二

      說明:

      • 掃描所有的包

      • 但是使用過濾器進行排除

        • 過濾的型別是按注解過濾,過濾Controller注解

      使用到的屬性:

      • excludeFilters屬性:設定掃描加載bean時,排除的過濾規則

      • type屬性:設定排除規則,當前使用按照bean定義時的注解型別進行排除

        • ANNOTATION:按照注解排除
        • ASSIGNABLE_TYPE:按照指定的型別過濾
        • ASPECTJ:按照Aspectj運算式排除,基本上不會用
        • REGEX:按照正則運算式排除
        • CUSTOM:按照自定義規則排除

        大家只需要知道第一種ANNOTATION即可

      • classes屬性:設定排除的具體注解類,當前設定排除@Controller定義的bean

      @Configuration
      @ComponentScan(
              value = "https://www.cnblogs.com/dandelion-000-blog/archive/2023/01/26/priv.dandelion",
              excludeFilters = @ComponentScan.Filter(
                      type = FilterType.ANNOTATION,
                      classes = Controller.class
              )
      )
      public class SpringConfig {
      }
      
    • 方式三(不區分 Spring 與 SpringMVC 的環境)【此處不做詳細說明】

2.5.5 相關知識點:@ComponentScan

名稱 @ComponentScan
型別 類注解
位置 類定義上方
作用 設定spring配置類掃描路徑,用于加載使用注解格式定義的bean
相關屬性 excludeFilters:排除掃描路徑中加載的bean,需要指定類別(type)和具體項(classes)
includeFilters:加載指定的bean,需要指定類別(type)和具體項(classes)

3、請求與相應

3.1 設定請求映射路徑

本小節注意:

  • 當類上和方法上都添加了@RequestMapping注解,前端發送請求的時候,要和兩個注解的value值相加匹配才能訪問到,
  • @RequestMapping注解value屬性前面加不加/都可以

3.1.1 環境準備

  • 依賴

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>priv.dandelion</groupId>
        <artifactId>03_request_mapping</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <dependencies>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.2.10.RELEASE</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.1</version>
                    <configuration>
                        <port>80</port>
                        <path>/</path>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  • 配置類

    • Spring 配置類(此處未使用到)

      @Configuration
      @ComponentScan(value = "https://www.cnblogs.com/dandelion-000-blog/archive/2023/01/26/priv.dandelion",
              excludeFilters = @ComponentScan.Filter(
                      type = FilterType.ANNOTATION,
                      classes = Controller.class
              )
      )
      public class SpringConfig {
      }
      
    • SpringMVC 配置類

      @Configuration
      @ComponentScan("priv.dandelion.controller")
      public class SpringMvcConfig {
      }
      
    • web服務器配置類 (簡化配置)

      public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
      
          protected Class<?>[] getServletConfigClasses() {
              return new Class[]{SpringMvcConfig.class};
          }
          protected String[] getServletMappings() {
              return new String[]{"/"};
          }
          protected Class<?>[] getRootConfigClasses() {
              return new Class[0];
          }
      }
      
  • Controller

    • UserController

      @Controller
      public class UserController {
      
          @RequestMapping("/save")
          @ResponseBody
          public String save(){
              System.out.println("user save ...");
              return "{'module':'user save'}";
          }
      
          @RequestMapping("/delete")
          @ResponseBody
          public String delete(){
              System.out.println("user delete ...");
              return "{'module':'user delete'}";
          }
      }
      
    • BookController

      @Controller
      public class BookController {
      
          @RequestMapping("/save")
          @ResponseBody
          public String save(){
              System.out.println("book save ...");
              return "{'module':'book save'}";
          }
      }
      

3.1.2 問題分析

  • 以上環境準備完成后,啟動服務器時會報錯

    [INFO] Initializing Servlet 'dispatcher'
    [WARNING] Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'userController' method 
    priv.dandelion.controller.UserController#save()
    to { /save}: There is already 'bookController' bean method
    priv.dandelion.controller.BookController#save() mapped.
    [ERROR] Context initialization failed
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'userController' method 
    priv.dandelion.controller.UserController#save()
    to { /save}: There is already 'bookController' bean method
    priv.dandelion.controller.BookController#save() mapped.
        at ...
        at ...
        ...
    
  • 從錯誤資訊可知

    • UserController有一個save方法,訪問路徑為http://localhost/save
    • BookController也有一個save方法,訪問路徑為http://localhost/save
    • 當訪問http://localhost/saved的時候,到底是訪問 UserController 還是 BookController,就會出現沖突
  • 解決方案:為不同模塊設定模塊名作為請求路徑前置

    • 對于Book模塊的save,將其訪問路徑設定http://localhost/book/save
    • 對于User模塊的save,將其訪問路徑設定http://localhost/user/save

3.1.3 設定映射路徑

  • 方案一(耦合度高不推薦):對每一個資源的 RequestMapping 進行修改

    • UserController

      @Controller
      public class UserController {
      
          @RequestMapping("/user/save")
          @ResponseBody
          public String save(){
              System.out.println("user save ...");
              return "{'module':'user save'}";
          }
      
          @RequestMapping("/user/delete")
          @ResponseBody
          public String delete(){
              System.out.println("user delete ...");
              return "{'module':'user delete'}";
          }
      }
      
    • BookController(不表)

  • 方案二:為 Controller 添加一個整體的 RequestMapping(稱為請求路徑前綴),其他不變

    • UserController

      @Controller
      @RequestMapping("/user")
      public class UserController {
      
          @RequestMapping("/save")
          @ResponseBody
          public String save(){
              System.out.println("user save ...");
              return "{'module':'user save'}";
          }
      
          @RequestMapping("/delete")
          @ResponseBody
          public String delete(){
              System.out.println("user delete ...");
              return "{'module':'user delete'}";
          }
      }
      
    • BookController(不表)

3.2 請求引數

3.2.1 環境準備

  • 依賴、配置類見 3.1.1

  • 物體類

    • Address

      public class Address {
          private String province;
          private String city;
          
          // Getter,Setter,toString不表
      }
      
    • User

      public class User {
          private String name;
          private int age;
      
          // Getter,Setter,toString不表
      }
      
  • Controller

    @Controller
    public class UserController {
    
        @RequestMapping("/commonParam")
        @ResponseBody
        public String commonParam(){
            return "{'module':'commonParam'}";
        }
    }
    

3.2.2 引數傳遞及中文亂碼處理方案

Get請求與引數:

http://localhost/commonParam?name=dandelion&age=18

POST請求與引數:

  • 發送Post請求時,引數放在請求體中,若使用PostMan工具,引數需要寫在Body模塊中,發送表單資料時使用 x-www-from-urlencodedform-data除了發送表單之外還可以發送檔案)
  • GET請求

    • 接收引數

      @Controller
      public class UserController {
      
          @RequestMapping("/commonParam")
          @ResponseBody
          public String commonParam(String name, int age){
              System.out.println("普通引數name:"+ name);
              System.out.println("普通引數age:"+ age);
              return "{'module':'commonParam'}";
          }
      }
      
    • GET中文亂碼:配置pom.xml

      Tomcat8.5以后的版本已經處理了中文亂碼的問題,但是IDEA中的Tomcat插件目前只到Tomcat7,所以需要修改pom.xml來解決GET請求中文亂碼問題

      <build>
          <plugins>
              <plugin>
                  <groupId>org.apache.tomcat.maven</groupId>
                  <artifactId>tomcat7-maven-plugin</artifactId>
                  <version>2.1</version>
                  <configuration>
                      <port>80</port><!--tomcat埠號-->
                      <path>/</path> <!--虛擬目錄-->
                      <uriEncoding>UTF-8</uriEncoding><!--訪問路徑編解碼字符集-->
                  </configuration>
              </plugin>
          </plugins>
      </build>
      
  • POST請求

    • 接收引數

      POST請求接收引數代碼與GET請求一致

    • POST中文亂碼問題:設定過濾器

      • 在web服務器配置類 ServletContainersInitConfig 中重寫 getServletFilters() 方法,創建所需的過濾器物件,并設定編碼字符集為UTF-8

      • CharacterEncodingFilter 是在 spring-web 包中,所以用之前需要匯入對應的 jar 包

        import org.springframework.web.filter.CharacterEncodingFilter;
        
      @Override
      protected Filter[] getServletFilters() {
          CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
          characterEncodingFilter.setEncoding("UTF-8");
          return new Filter[]{characterEncodingFilter};
      }
      

3.3 五種型別引數傳遞

3.3.1 普通引數

普通引數的基本使用已經實作過,詳見 3.2.2 引數傳遞及中文亂碼處理方案

  • 解決請求中的引數名稱和 Controller 方法的引數不一致問題

    • 當出現請求中的引數名稱和Controller中方法的引數名不匹配時,無法正常接收到引數
    • 使用 @RequestPaam() 注解修飾不一致的引數,為其指定需要匹配的請求引數
    @Controller
    public class UserController {
    
        // `http://localhost/commonParam?username=dandelion&age=12`
        @RequestMapping("/commonParam")
        @ResponseBody
        public String commonParam(@RequestParam("username") String name, int age){
            System.out.println("普通引數name:"+ name);
            System.out.println("普通引數age:"+ age);
            return "{'module':'commonParam'}";
        }
    }
    

3.3.2 POJO 資料型別

  • 直接使用一個物體類作為形參,框架會使用 setter 自動將資料進行寫入
  • 物體類中的屬性名稱需要和請求引數的名稱保持一致,否則接收不到
  • 若物體類中沒有對應的 setter 可以和請求引數的引數名匹配,則默認零假空(未進行寫入),可以使用該特性在實際開發中減少作業量
  • 物體類

    public class User {
        private String name;
        private int age;
    
        // Getter,Setter,toString不表
    }
    
  • Controller

    @Controller
    public class UserController {
    
        @RequestMapping("/pojoParam")
        @ResponseBody
        public String pojoParam(User user){
            System.out.println("POJO引數:"+ user);
            return "{'module':'pojoParam'}";
        }
    }
    

3.3.3 嵌套 POJO 型別引數

對于嵌套的POJO型別,在進行引數傳遞時,也要使用嵌套的形式來書寫請求引數名稱

http://localhost/pojoParam?name=dandelion&age=12&address.province=hubei&address.city=wuhan

  • 物體類

    • User

      public class User {
          private String name;
          private int age;
          
          private Address address;
      
          // Getter,Setter,toString不表
      }
      
    • Address

      public class Address {
          private String province;
          private String city;
          
          // Getter,Setter,toString不表
      }
      
  • Controller

    Controller 部分代碼與 3.3.3 嵌套 POJO 型別引數一致

3.3.4 陣列型別引數

  • 請求引數為陣列時,不同的陣列元素使用相同的請求引數名稱
  • 接收請求引數時,使用陣列作為形參
  • 請求

    http://localhost/arrayParam?person=zhangsan&person=lisi&person=wangwu
    
  • 接收

    @Controller
    public class UserController {
    
        @RequestMapping("/arrayParam")
        @ResponseBody
        public String arrayParam(String[] person){
            System.out.println("陣列引數:"+ Arrays.toString(person));
            return "{'module':'arrayParam'}";
        }
    }
    

3.3.5 集合型別引數

  • 請求

    與陣列引數的請求方式相同

  • 接收

    • 錯誤案例

      以下代碼段運行時會報錯:NoSuchMethodException: java.util.List.<init>(),缺少構造,(內心:廢話 List 介面哪來的構造)

      嚴重: Servlet.service() for servlet [dispatcher] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: No primary or default constructor found for interface java.util.List] with root cause
      java.lang.NoSuchMethodException: java.util.List.<init>()
      
      @Controller
      public class UserController {
      
          @RequestMapping("/listParam")
          @ResponseBody
          public String listParam(List<String> person){
              System.out.println("集合引數:"+ person);
              return "{'module':'listParam'}";
          }
      }
      
    • 原因及解決方案

      • 問題原因

        SpringMVC 將 List 看做是一個 POJO 物件來處理,將其創建一個物件并準備把前端的資料封裝到物件中,但是 List 是一個介面無法創建物件,所以報錯,

      • 解決方案:使用@RequestParam注解

        • 集合保存普通引數:請求引數名與形參集合物件名相同且請求引數為多個,@RequestParam 系結引數關系
        • 顯而易見,對于簡單資料型別使用陣列會比集合更簡單些,
        @Controller
        public class UserController {
        
            @RequestMapping("/listParam")
            @ResponseBody
            public String listParam(@RequestParam List<String> person){
                System.out.println("集合引數:"+ person);
                return "{'module':'listParam'}";
            }
        }
        

3.3.6 相關知識點:@RequestParam

名稱 @RequestParam
型別 形參注解
位置 SpringMVC控制器方法形參定義前面
作用 系結請求引數與處理器方法形參間的關系
相關引數 required:是否為必傳引數
defaultValue:引數默認值

3.4 JSON 資料傳輸引數

  • 引數放在請求體中,若使用 PostMan 工具,引數需要寫在Body模塊中,發送表單資料時使用 raw,并將資料格式修改為 JSON

3.4.1 JSON 資料傳輸引數分類與準備作業

  • 引數分類

    // json普通陣列
    ["value1","value2","value3",...]
    // json物件
     {"key1":"value1","key2":"value2",...}
    // json物件陣列
    [{"key11":"value11",...},{"key21":"value21",...}]
    
  • 準備作業

    • 添加依賴

      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.9.0</version>
      </dependency>
      
    • 開啟SpringMVC注解驅動,用于開啟 json 資料型別自動轉換:@EnableWebMvc

      @Configuration
      @ComponentScan("priv.dandelion.controller")
      @EnableWebMvc
      public class SpringMvcConfig {
      }
      
    • 引數前添加@RequestBody

      • 使用@RequestBody注解將外部傳遞的json陣列資料映射到形參的集合物件中作為資料
      • 區別于 @RequestParam,本小節總結部分會進行說明
      • 區別于 @ResponseBody,書寫要正確
      • 下文中展示

3.4.2 三種引數傳輸格式

3.4.2.1 json普通陣列
  • JSON

    ["zhangsan","lisi","wangwu"]
    
  • 接收

    @RequestMapping("/listParamForJson")
    @ResponseBody
    // //使用@RequestBody注解將外部傳遞的json陣列資料映射到形參的集合物件中作為資料
    public String listParamForJson(@RequestBody List<String> person){
        System.out.println("list common(json)引數傳遞 list:" + person);
        return "{'module':'list common for json param'}";
    }
    
3.4.2.2 json物件
  • 若 JSON 物件中的 key 與物體類中的 setter 名稱(標準書寫)不能匹配時,不執行 setter ,物體類中的資料不變(不進行其他操作時默認為零假空)
  • 同理,若不傳遞某一屬性的值,物體類中的資料不變(不進行其他操作時默認為零假空)
  • JSON

    // 單個POJO
    {
    	"name":"dandelion",
    	"age":12
    }
    
    // 嵌套POJO
    {
    	"name1":"dandelion",
    	"age":12,
        "address":{
            "province":"provinceName",
            "city":"cityName"
        }
    }
    
  • 物體類

    見 3.3.3

  • 接收

    @RequestMapping("/pojoParamForJson")
    @ResponseBody
    public String pojoParamForJson(@RequestBody User user){
        System.out.println("pojo(json)引數傳遞 user:"+user);
        return "{'module':'pojo for json param'}";
    }
    
3.4.2.3 json物件陣列
  • JSON

    [
        {"name":"dandelion","age":15,"address":{"province":"provinceName","city":"cityName"}},
        {"name":"dandelion000","age":12}
    ]
    
  • 接收

    @RequestMapping("/listPojoParamForJson")
    @ResponseBody
    public String listPojoParamForJson(@RequestBody List<User> list){
        System.out.println("list pojo(json)引數傳遞 list:"+list);
        return "{'module':'list pojo for json param'}";
    }
    

3.4.3 相關知識點

  • 知識點1:@EnableWebMvc

    名稱 @EnableWebMvc
    型別 配置類注解
    位置 SpringMVC配置類定義上方
    作用 開啟SpringMVC多項輔助功能
  • 知識點2:@RequestBody

    • 整理

      名稱 @RequestBody
      型別 形參注解
      位置 SpringMVC控制器方法形參定義前面
      作用 將請求中請求體所包含的資料傳遞給請求引數,此注解一個處理器方法只能使用一次
    • @RequestBody與@RequestParam區別

      • 區別

        • @RequestParam用于接收url地址傳參,表單傳參【application/x-www-form-urlencoded】
        • @RequestBody用于接收json資料【application/json】
      • 應用

        • 后期開發中,發送json格式資料為主,@RequestBody應用較廣
        • 如果發送非json格式資料,選用@RequestParam接收請求引數

3.5 日期類型引數傳遞

  • 接收案例

    • 請求

      http://localhost/dataParam?date=2022/02/22
      
    • 接收

      @RequestMapping("/dataParam")
      @ResponseBody
      public String dataParam(Date date, Date date1){
          System.out.println("引數傳遞 date:"+date);
          System.out.println("引數傳遞 date1:"+date1);
          return"{'module':'data param'}";
      }
      
    • 結果

      • 發送請求后會發現該部分代碼會報錯,但若請求引數中只有date而沒有date1時則正常接收

      • 報錯資訊:

        方法引數型別不匹配,在將 String 轉換為 Date 時出現問題,轉換失敗

        [WARNING] Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value '2022-02-22'; nested exception is java.lang.IllegalArgumentException]
        
        
  • 解決方案(接收任意日期格式的方法):使用 @DateTimeFormat 指定日期格式

    • 請求

      http://localhost/dataParam?date=2022/02/22&date1=22-02-2022&date2=2022-02-22 22:22:22
      
    • 接收

      @RequestMapping("/dataParam")
      @ResponseBody
      public String dataParam(Date date,
                              @DateTimeFormat(pattern = "dd-MM-yyyy") Date date1,
                              @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date2){
          System.out.println("引數傳遞 date:"+ date);
          System.out.println("引數傳遞 date1(dd-MM-yyyy):"+ date1);
          System.out.println("引數傳遞 date2(yyyy-MM-dd HH:mm:ss):"+ date2);
          return"{'module':'data param'}";
      }
      
  • 相關知識點

    • @DateTimeFormat

      名稱 @DateTimeFormat
      型別 形參注解
      位置 SpringMVC控制器方法形參前面
      作用 設定日期時間型資料格式
      相關屬性 pattern:指定日期時間格式字串
    • 內部實作原理

      SpringMVC中提供了很多型別轉換介面和實作類,其中有 Converter 介面

      • Converter 介面

        • Converter所屬的包為org.springframework.core.convert.converter

        • 框架中有提供很多對應Converter介面的實作類,用來實作不同資料型別之間的轉換,如:

        • 請求引數年齡資料(String→Integer)

        • 日期格式轉換(String → Date)

        /**
        *	S: the source type
        *	T: the target type
        */
        public interface Converter<S, T> {
            @Nullable
            //該方法就是將從頁面上接收的資料(S)轉換成我們想要的資料型別(T)回傳
            T convert(S source);
        }
        
      • HttpMessageConverter 介面

        該介面是實作物件與 JSON 之間的轉換作業,使用時在SpringMVC的配置類把@EnableWebMvc當做標配配置上去,不省略

3.6 回應

3.6.1 環境準備

  • 依賴

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>priv.dandelion</groupId>
        <artifactId>05_response</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <dependencies>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.2.10.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.9.0</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.1</version>
                    <configuration>
                        <port>80</port>
                        <path>/</path>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    
  • 配置類

    • 服務器配置類

      public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
      
          protected Class<?>[] getServletConfigClasses() {
              return new Class[]{SpringMvcConfig.class};
          }
          protected String[] getServletMappings() {
              return new String[]{"/"};
          }
          protected Class<?>[] getRootConfigClasses() {
              return new Class[0];
          }
      
          @Override
          protected Filter[] getServletFilters() {
              CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
              characterEncodingFilter.setEncoding("UTF-8");
              return new Filter[]{characterEncodingFilter};
          }
      }
      
    • SpringMVC 配置類

      @Configuration
      @ComponentScan("priv.dandelion.controller")
      public class SpringMvcConfig {
      }
      
  • 物體類

    public class User {
        private String name;
        private int age;
        //getter...setter...toString省略
    }
    
  • webapp下創建頁面 page.jsp

    <html>
    <body>
    <h2>Hello Spring MVC!</h2>
    </body>
    </html>
    
  • Controller

    @Controller
    public class UserController {
    
    }
    

3.6.2 回應頁面(了解)

  • 注意此處不能使用@ResponseBody,否則會將回傳值內容作為字串回傳給前端
  • 注意進行頁面跳轉時,回傳值為頁面名稱,回傳值型別為字串
@RequestMapping("/toJumpPage")
public String toJumpPage() {
    System.out.println("跳轉頁面");
    return "page.jsp";
}

3.6.3 回傳文本資料(了解)

  • 注意此處 @ResponseBody 注解就不能省略
  • 如果省略了會把response text當前頁面名稱去查找,如果沒有回報404
@RequestMapping("/toText")
@ResponseBody
public String toText() {
    System.out.println("回傳純文本資料");
    return "response text";
}

3.6.4 回應 JSON 資料

準備作業:

  • 開啟SpringMVC注解驅動,用于開啟 json 資料型別自動轉換:@EnableWebMvc

    @Configuration
    @ComponentScan("priv.dandelion.controller")
    @EnableWebMvc
    public class SpringMvcConfig {
    }
    
3.6.4.1 回應 POJO 物件
@RequestMapping("/toJsonPOJO")
@ResponseBody
public User toJsonPOJO() {
    System.out.println("回傳JSON資料物件");
    User user = new User();
    user.setName("dandelion");
    user.setAge(12);
    return user;
}
3.6.4.2 回應 POJO 集合物件

此處回傳的是POJO的集合,基本資料型別的集合同理

@RequestMapping("/toJsonList")
@ResponseBody
public List<User> toJsonList() {
    System.out.println("回傳JSON資料物件");
    User user1 = new User();
    user1.setName("dandelion");
    user1.setAge(12);

    User user2 = new User();
    user2.setName("dandelion000");
    user2.setAge(15);

    List<User> users = new ArrayList<>();
    users.add(user1);
    users.add(user2);
    return users;
}
3.6.4.3 相關知識點:@ResponseBody
  • 整理

    名稱 @ResponseBody
    型別 方法\類注解
    位置 SpringMVC控制器方法定義上方和控制類上
    作用 設定當前控制器回傳值作為回應體,
    寫在類上,該類的所有方法都有該注解功能
    相關屬性 pattern:指定日期時間格式字串
  • 說明

    • 該注解可以寫在類上或者方法上

    • 寫在類上就是該類下的所有方法都有@ReponseBody功能

    • 當方法上有@ReponseBody注解后

      • 方法的回傳值為字串,會將其作為文本內容直接回應給前端
      • 方法的回傳值為物件,會將物件轉換成JSON回應給前端
    • 此處又使用到了型別轉換,內部還是通過Converter介面的實作類完成的,所以Converter除了前面所說的功能外,它還可以實作:

      • 物件轉Json資料(POJO -> json)

      • 集合轉Json資料(Collection -> json)

4、REST風格

4.1 REST 簡介

  • REST(Representational State Transfer),表現形式狀態轉換,它是一種軟體架構風格

  • REST風格與傳統風格的區別

    • 傳統風格資源描述形式

      • http://localhost/user/getById?id=1 查詢id為1的用戶資訊
      • http://localhost/user/saveUser 保存用戶資訊
    • REST風格描述形式

      • http://localhost/user/1
      • http://localhost/user
  • REST風格的優點

    • 隱藏資源的訪問行為,無法通過地址得知對資源是何種操作
    • 書寫簡化
  • REST風格的使用

    • 按照REST風格訪問資源時使用行為動作區分對資源進行了何種操作

      • GET(查詢)http://localhost/users 查詢全部用戶資訊
      • GET(查詢)http://localhost/users/1 查詢指定用戶資訊
      • POST(新增/保存)http://localhost/users 添加用戶資訊
      • PUT(修改/更新)http://localhost/users 修改用戶資訊
      • DELETE(洗掉)http://localhost/users/1 洗掉用戶資訊
    • 請求方式:按照不同的請求方式代表不同的操作型別

      • 發送GET請求是用來做查詢
      • 發送POST請求是用來做新增
      • 發送PUT請求是用來做修改
      • 發送DELETE請求是用來做洗掉
  • 注意

    • 上述行為是約定方式,約定不是規范,可以打破,所以稱REST風格,而不是REST規范
      • REST提供了對應的架構方式,按照這種架構設計專案可以降低開發的復雜性,提高系統的可伸縮性
      • REST中規定GET/POST/PUT/DELETE針對的是查詢/新增/修改/洗掉,但是我們如果非要用GET請求做洗掉,這點在程式上運行是可以實作的
      • 但是如果絕大多數人都遵循這種風格,你寫的代碼讓別人讀起來就有點莫名其妙了,
    • 描述模塊的名稱通常使用復數,也就是加s的格式描述,表示此類資源,而非單個資源,例如:users、books、accounts......
  • RESTful

    • 根據REST風格對資源進行訪問稱為RESTful

4.2 RESTful 入門案例

4.2.1 環境準備

  • 依賴

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>priv.dandelion</groupId>
        <artifactId>06_rest</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <dependencies>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.2.10.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.9.0</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.1</version>
                    <configuration>
                        <port>80</port>
                        <path>/</path>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    
    
  • 配置類

    • 服務器配置類

      public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
      
          protected Class<?>[] getServletConfigClasses() {
              return new Class[]{SpringMvcConfig.class};
          }
          protected String[] getServletMappings() {
              return new String[]{"/"};
          }
          protected Class<?>[] getRootConfigClasses() {
              return new Class[0];
          }
      
          @Override
          protected Filter[] getServletFilters() {
              CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
              characterEncodingFilter.setEncoding("UTF-8");
              return new Filter[]{characterEncodingFilter};
          }
      }
      
    • SpringMVC配置類,開啟json資料型別自動轉換

      @Configuration
      @ComponentScan("priv.dandelion.controller")
      @EnableWebMvc
      public class SpringMvcConfig {
      }
      
  • 物體類

    • User

      public class User {
          private String name;
          private int age;
          //getter...setter...toString省略
      }
      
    • Book

      public class Book {
          private String name;
          private double price;
           //getter...setter...toString省略
      }
      
  • Controller

    • UserController

      @Controller
      public class UserController {
          @RequestMapping("/save")
          @ResponseBody
          public String save(@RequestBody User user) {
              System.out.println("user save..."+user);
              return "{'module':'user save'}";
          }
      
          @RequestMapping("/delete")
          @ResponseBody
          public String delete(Integer id) {
              System.out.println("user delete..." + id);
              return "{'module':'user delete'}";
          }
      
          @RequestMapping("/update")
          @ResponseBody
          public String update(@RequestBody User user) {
              System.out.println("user update..." + user);
              return "{'module':'user update'}";
          }
      
          @RequestMapping("/getById")
          @ResponseBody
          public String getById(Integer id) {
              System.out.println("user getById..." + id);
              return "{'module':'user getById'}";
          }
      
          @RequestMapping("/findAll")
          @ResponseBody
          public String getAll() {
              System.out.println("user getAll...");
              return "{'module':'user getAll'}";
          }
      }
      
    • BookController

      @Controller
      public class BookController {
      
          @RequestMapping(value = "https://www.cnblogs.com/books",method = RequestMethod.POST)
          @ResponseBody
          public String save(@RequestBody Book book){
              System.out.println("book save..." + book);
              return "{'module':'book save'}";
          }
      
          @RequestMapping(value = "https://www.cnblogs.com/books/{id}",method = RequestMethod.DELETE)
          @ResponseBody
          public String delete(@PathVariable Integer id){
              System.out.println("book delete..." + id);
              return "{'module':'book delete'}";
          }
      
          @RequestMapping(value = "https://www.cnblogs.com/books",method = RequestMethod.PUT)
          @ResponseBody
          public String update(@RequestBody Book book){
              System.out.println("book update..." + book);
              return "{'module':'book update'}";
          }
      
          @RequestMapping(value = "https://www.cnblogs.com/books/{id}",method = RequestMethod.GET)
          @ResponseBody
          public String getById(@PathVariable Integer id){
              System.out.println("book getById..." + id);
              return "{'module':'book getById'}";
          }
      
          @RequestMapping(value = "https://www.cnblogs.com/books",method = RequestMethod.GET)
          @ResponseBody
          public String getAll(){
              System.out.println("book getAll...");
              return "{'module':'book getAll'}";
          }
      
      }
      

4.2.2 思路分析

  • 將之前的增刪改查替換成RESTful的開發方式
    • 修改前: 新增: /save ,修改: /update,洗掉 /delete...
  • 修改后:
    • 增刪改查: /users
    • 根據GET查詢、POST新增、PUT修改、DELETE洗掉對方法的請求方式進行限定

4.2.3 修改 RESTFUL 風格

4.2.3.1 各項操作
  • 新增

    • 請求

      POST http://localhost/users
      
    • 接收

      @RequestMapping(value = "https://www.cnblogs.com/users", method = RequestMethod.POST)
      @ResponseBody
      public String save(@RequestBody User user) {
          System.out.println("user save..."+user);
          return "{'module':'user save'}";
      }
      
  • 洗掉

    • 請求

      DELETE http://localhost/users/1
      
    • 接收

      • 使用REST風格時,引數寫在請求路徑中

      • 接收時對引數使用 @PathVariable 注解,在資源路徑的引數位置使用{},其中的值應和引數名稱保持一致

      • 若引數名稱和{}中的內容不一致,應手動進行系結,使用 @PathVariable 注解的 value 屬性,如下:

        @RequestMapping(value = "https://www.cnblogs.com/users/{id}", method = RequestMethod.DELETE)
        @ResponseBody
        public String delete(@PathVariable("id") Integer userId) {}
        
      @RequestMapping(value = "https://www.cnblogs.com/users/{id}", method = RequestMethod.DELETE)
      @ResponseBody
      public String delete(@PathVariable Integer id) {
          System.out.println("user delete..." + id);
          return "{'module':'user delete'}";
      }
      
  • 修改

    • 請求

      PUT http://localhost/users
      
    • 接收

      @RequestMapping(value = "https://www.cnblogs.com/users", method = RequestMethod.PUT)
      @ResponseBody
      public String update(@RequestBody User user) {
          System.out.println("user update..." + user);
          return "{'module':'user update'}";
      }
      
  • 查詢

    • 查詢單個

      • 請求

        GET http://localhost/users/1
        
      • 接收

        @RequestMapping(value = "https://www.cnblogs.com/users/{id}", method = RequestMethod.GET)
        @ResponseBody
        public String getById(@PathVariable Integer id) {
            System.out.println("user getById..." + id);
            return "{'module':'user getById'}";
        }
        
    • 查詢所有

      • 請求

        GET http://localhost/users
        
      • 接收

        @RequestMapping(value = "https://www.cnblogs.com/users", method = RequestMethod.GET)
        @ResponseBody
        public String getAll() {
            System.out.println("user getAll...");
            return "{'module':'user getAll'}";
        }
        
4.2.3.2 相關知識點:@PathVarlable
名稱 @PathVariable
型別 形參注解
位置 SpringMVC控制器方法形參定義前面
作用 系結路徑引數與處理器方法形參間的關系,要求路徑引數名與形參名一一對應
4.2.3.3 相關知識點:三種接收引數的注解
  • 區別

    • @RequestParam 用于接收url地址傳參或表單傳參
    • @RequestBody 用于接收json資料
    • @PathVariable 用于接收路徑引數,使用{引數名稱}描述路徑引數
  • 應用

    • 后期開發中,發送請求引數超過1個時,以json格式為主,@RequestBody 應用較廣
    • 如果發送非 json 格式資料,選用 @RequestParam 接收請求引數
    • 采用 RESTful 進行開發,當引數數量較少時,例如1個,可以采用 @PathVariable 接收請求路徑變數,通常用于傳遞id值

4.3 RESTFUL 快速開發

4.3.1 開發簡化

  • 未簡化代碼

    @Controller
    public class BookController {
    
        @RequestMapping(value = "https://www.cnblogs.com/books",method = RequestMethod.POST)
        @ResponseBody
        public String save(@RequestBody Book book){
            System.out.println("book save..." + book);
            return "{'module':'book save'}";
        }
    
        @RequestMapping(value = "https://www.cnblogs.com/books/{id}",method = RequestMethod.DELETE)
        @ResponseBody
        public String delete(@PathVariable Integer id){
            System.out.println("book delete..." + id);
            return "{'module':'book delete'}";
        }
    
        @RequestMapping(value = "https://www.cnblogs.com/books",method = RequestMethod.PUT)
        @ResponseBody
        public String update(@RequestBody Book book){
            System.out.println("book update..." + book);
            return "{'module':'book update'}";
        }
    
        @RequestMapping(value = "https://www.cnblogs.com/books/{id}",method = RequestMethod.GET)
        @ResponseBody
        public String getById(@PathVariable Integer id){
            System.out.println("book getById..." + id);
            return "{'module':'book getById'}";
        }
    
        @RequestMapping(value = "https://www.cnblogs.com/books",method = RequestMethod.GET)
        @ResponseBody
        public String getAll(){
            System.out.println("book getAll...");
            return "{'module':'book getAll'}";
        }
    
    }
    
  • 簡化程序(參見注釋)

    1. 將請求路徑中共同的部分抽取
    2. 將方法上@RequestMapping中不需要的value屬性省略
    3. 將所有方法上的@ResponseBody抽取
    4. 存在@Controller和@ResponseBody,可以使用@RestController代替
    5. 使用@PostMapping、@DeleteMapping等注解代替@RequestMapping
    // @Controller
    // 3.所有方法均需要@ResponseBody,抽取
    // @ResponseBody
    @RestController // 4.其中包含了@Controller和@ResponseBody
    // 1.以前綴的形式將請求路徑中相同的內容抽取出來
    @RequestMapping("/books")
    public class BookController {
    
        // 2.value屬性已經完全被抽取,可以省略
        // @RequestMapping(method = RequestMethod.POST)
        // 5.簡化上一行代碼,下同
        @PostMapping
        public String save(@RequestBody Book book){
            System.out.println("book save..." + book);
            return "{'module':'book save'}";
        }
    
        // @RequestMapping(value = "https://www.cnblogs.com/{id}",method = RequestMethod.DELETE)
        @DeleteMapping("/{id}")
        public String delete(@PathVariable Integer id){
            System.out.println("book delete..." + id);
            return "{'module':'book delete'}";
        }
    
        // @RequestMapping(method = RequestMethod.PUT)
        @PutMapping
        public String update(@RequestBody Book book){
            System.out.println("book update..." + book);
            return "{'module':'book update'}";
        }
    
        // @RequestMapping(value = "https://www.cnblogs.com/{id}",method = RequestMethod.GET)
        @GetMapping("/{id}")
        public String getById(@PathVariable Integer id){
            System.out.println("book getById..." + id);
            return "{'module':'book getById'}";
        }
    
        // @RequestMapping(method = RequestMethod.GET)
        @GetMapping
        public String getAll(){
            System.out.println("book getAll...");
            return "{'module':'book getAll'}";
        }
    }
    
  • 簡化后代碼

    @RestController
    @RequestMapping("/books")
    public class BookController {
    
        @PostMapping
        public String save(@RequestBody Book book){
            System.out.println("book save..." + book);
            return "{'module':'book save'}";
        }
    
        @DeleteMapping("/{id}")
        public String delete(@PathVariable Integer id){
            System.out.println("book delete..." + id);
            return "{'module':'book delete'}";
        }
    
        @PutMapping
        public String update(@RequestBody Book book){
            System.out.println("book update..." + book);
            return "{'module':'book update'}";
        }
    
        @GetMapping("/{id}")
        public String getById(@PathVariable Integer id){
            System.out.println("book getById..." + id);
            return "{'module':'book getById'}";
        }
    
        @GetMapping
        public String getAll(){
            System.out.println("book getAll...");
            return "{'module':'book getAll'}";
        }
    }
    

4.3.2 相關知識點

  • 知識點1:@RestController

    名稱 @RestController
    型別 類注解
    位置 基于SpringMVC的RESTful開發控制器類定義上方
    作用 設定當前控制器類為RESTful風格,
    等同于@Controller與@ResponseBody兩個注解組合功能
  • 知識點2:@GetMapping @PostMapping @PutMapping @DeleteMapping

    名稱 @GetMapping @PostMapping @PutMapping @DeleteMapping
    型別 方法注解
    位置 基于SpringMVC的RESTful開發控制器方法定義上方
    作用 設定當前控制器方法請求訪問路徑與請求動作,每種對應一個請求動作,
    例如@GetMapping對應GET請求
    相關屬性 value(默認):請求訪問路徑

4.4 RESTFUL 案例

4.4.1 需求分析

4.4.2 環境準備

  • 依賴

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>priv.dandelion</groupId>
        <artifactId>07_rest_case</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <dependencies>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.2.10.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.9.0</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.1</version>
                    <configuration>
                        <port>80</port>
                        <path>/</path>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    
  • 配置類

    • 服務器配置類

      public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
      
          protected Class<?>[] getServletConfigClasses() {
              return new Class[]{SpringMvcConfig.class};
          }
          protected String[] getServletMappings() {
              return new String[]{"/"};
          }
          protected Class<?>[] getRootConfigClasses() {
              return new Class[0];
          }
      
          @Override
          protected Filter[] getServletFilters() {
              CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
              characterEncodingFilter.setEncoding("UTF-8");
              return new Filter[]{characterEncodingFilter};
          }
      }
      
    • SpringMVC配置類

      @Configuration
      @ComponentScan("priv.dandelion.controller")
      @EnableWebMvc
      public class SpringMvcConfig {
      }
      
  • 物體類

    public class Book {
        private Integer id;
        private String type;
        private String name;
        private String description;
        //setter...getter...toString略
    }
    
  • 控制器

    @Controller
    public class BookController {
    
    }
    

4.4.3 后臺介面開發

@RestController
@RequestMapping("/books")
public class BookController {

    @PostMapping
    public String save(@RequestBody Book book) {
        System.out.println("book save ==> "+ book);
        return "{'module':'book save success'}";
    }

    @GetMapping
    public List<Book> getAll() {
        System.out.println("book getAll is running ...");
        List<Book> bookList = new ArrayList<Book>();

        Book book1 = new Book();
        book1.setType("計算機");
        book1.setName("SpringMVC1");
        book1.setDescription("123");
        bookList.add(book1);

        Book book2 = new Book();
        book2.setType("計算機");
        book2.setName("SpringMVC2");
        book2.setDescription("234");
        bookList.add(book2);

        Book book3 = new Book();
        book3.setType("計算機叢書");
        book3.setName("SpringMVC3");
        book3.setDescription("345");
        bookList.add(book3);

        return bookList;
    }
}

4.4.4 頁面訪問處理

  • 頁面準備

    • 環境:vue.js + axios + elementui

    • 頁面

      <!DOCTYPE html>
      
      <html>
          <head>
              <!-- 頁面meta -->
              <meta charset="utf-8">
              <title>SpringMVC案例</title>
              <!-- 引入樣式 -->
              <link rel="stylesheet" href="https://www.cnblogs.com/dandelion-000-blog/archive/2023/01/plugins/elementui/index.css">
              <link rel="stylesheet" href="https://www.cnblogs.com/dandelion-000-blog/archive/2023/01/plugins/font-awesome/css/font-awesome.min.css">
              <link rel="stylesheet" href="https://www.cnblogs.com/dandelion-000-blog/archive/2023/01/css/style.css">
          </head>
      
          <body >
      
              <div id="app">
      
                  <div >
                      <h1>圖書管理</h1>
                  </div>
      
                  <div >
                      <div >
                          <div >
                              <el-input placeholder="圖書名稱" style="width: 200px;" ></el-input>
                              <el-button >查詢</el-button>
                              <el-button type="primary"  @click="openSave()">新建</el-button>
                          </div>
      
                          <el-table size="small" current-row-key="id" :data="https://www.cnblogs.com/dandelion-000-blog/archive/2023/01/26/dataList" stripe highlight-current-row>
                              <el-table-column type="index" align="center" label="序號"></el-table-column>
                              <el-table-column prop="type" label="圖書類別" align="center"></el-table-column>
                              <el-table-column prop="name" label="圖書名稱" align="center"></el-table-column>
                              <el-table-column prop="description" label="描述" align="center"></el-table-column>
                              <el-table-column label="操作" align="center">
                                  <template slot-scope="scope">
                                      <el-button type="primary" size="mini">編輯</el-button>
                                      <el-button size="mini" type="danger">洗掉</el-button>
                                  </template>
                              </el-table-column>
                          </el-table>
      
                          <div >
                              <el-pagination
                                  
                                  @current-change="handleCurrentChange"
                                  :current-page="pagination.currentPage"
                                  :page-size="pagination.pageSize"
                                  layout="total, prev, pager, next, jumper"
                                  :total="pagination.total">
                              </el-pagination>
                          </div>
      
                          <!-- 新增標簽彈層 -->
                          <div >
                              <el-dialog title="新增圖書" :visible.sync="dialogFormVisible">
                                  <el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label->
                                      <el-row>
                                          <el-col :span="12">
                                              <el-form-item label="圖書類別" prop="type">
                                                  <el-input v-model="formData.type"/>
                                              </el-form-item>
                                          </el-col>
                                          <el-col :span="12">
                                              <el-form-item label="圖書名稱" prop="name">
                                                  <el-input v-model="formData.name"/>
                                              </el-form-item>
                                          </el-col>
                                      </el-row>
                                      <el-row>
                                          <el-col :span="24">
                                              <el-form-item label="描述">
                                                  <el-input v-model="formData.description" type="textarea"></el-input>
                                              </el-form-item>
                                          </el-col>
                                      </el-row>
                                  </el-form>
                                  <div slot="footer" >
                                      <el-button @click="dialogFormVisible = false">取消</el-button>
                                      <el-button type="primary" @click="saveBook()">確定</el-button>
                                  </div>
                              </el-dialog>
                          </div>
      
                      </div>
                  </div>
              </div>
          </body>
      
          <!-- 引入組件庫 -->
          <script src="https://www.cnblogs.com/dandelion-000-blog/archive/2023/01/js/vue.js"></script>
          <script src="https://www.cnblogs.com/dandelion-000-blog/archive/2023/01/plugins/elementui/index.js"></script>
          <script type="text/javascript" src="https://www.cnblogs.com/dandelion-000-blog/archive/2023/01/js/jquery.min.js"></script>
          <script src="https://www.cnblogs.com/dandelion-000-blog/archive/2023/01/js/axios-0.18.0.js"></script>
      
          <script>
              var vue = new Vue({
      
                  el: '#app',
      
                  data:{
      				dataList: [],//當前頁要展示的分頁串列資料
                      formData: {},//表單資料
                      dialogFormVisible: false,//增加表單是否可見
                      dialogFormVisible4Edit:false,//編輯表單是否可見
                      pagination: {},//分頁模型資料,暫時棄用
                  },
      
                  //鉤子函式,VUE物件初始化完成后自動執行
                  created() {
                      this.getAll();
                  },
      
                  methods: {
                      // 重置表單
                      resetForm() {
                          //清空輸入框
                          this.formData = https://www.cnblogs.com/dandelion-000-blog/archive/2023/01/26/{};
                      },
      
                      // 彈出添加視窗
                      openSave() {
                          this.dialogFormVisible = true;
                          this.resetForm();
                      },
      
                      //添加
                      saveBook () {
                          axios.post("/books",this.formData).then((res)=>{
      
                          });
                      },
      
                      //主頁串列查詢
                      getAll() {
                          axios.get("/books").then((res)=>{
                              this.dataList = res.data;
                          });
                      },
      
                  }
              })
          </script>
      </html>
      
  • 頁面訪問存在問題及解決方案

    • 存在問題:無法訪問到頁面

      • 報錯內容

        [WARNING] Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
        [WARNING] No mapping for GET /pages/books.html
        
      • 原因

        現在發送請求 http://localhost/pages/books.html 訪問這個頁面,但是被SpringMVC攔截,認為應該有一個 Controller 中方法的 RequestMapping 叫這個名字

    • 解決方案 放行非SpringMVC的請求

      • 遇到這種情況 SpringMVC 應當放行,由 web 服務器進行處理
      • web 服務器配置類中 getServletMappings 攔截了所有的請求,交給 SpringMVC 處理,需要為 SpringMVC 的配置添加過濾,放行頁面請求
      • 對頁面的過濾一般單獨抽成一個功能類
      • 功能類 SpringMvcSupport

        • 需要繼承 WebMvcConfigurationSupport,覆寫 addResourceHandlers ,操作 registry 屬性
        • 添加 @Configuration 注解,以用于包掃描
        @Configuration
        public class SpringMvcSupport extends WebMvcConfigurationSupport {
        
            // 添加資源過濾
            @Override
            protected void addResourceHandlers(ResourceHandlerRegistry registry) {
                // 當訪問頁面時,如果請求的是/pages/**,不走MVC,走/pages目錄下的內容
                registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
                registry.addResourceHandler("/js/**").addResourceLocations("/js/");
                registry.addResourceHandler("/css/**").addResourceLocations("/css/");
                registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
                // ...
            }
        }
        
      • 配置包掃描

        @Configuration
        @ComponentScan({"priv.dandelion.controller","priv.dandelion.config"})
        @EnableWebMvc
        public class SpringMvcConfig {
        }
        

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/542415.html

標籤:其他

上一篇:淺談PHP設計模式的中介者模式

下一篇:淺談PHP設計模式的享元模式

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more