極簡SpringBoot指南-Chapter03-基於SpringBoot的Web服務

倉庫地址

w4ngzhen/springboot-simple-guide: This is a project that guides SpringBoot users to get started quickly through a series of examples (github.com)html

Chapter03-基於SpringBoot的Web服務

前言

終於,咱們的教程來到了SpringBoot核心功能的介紹。對於本章,各位讀者至少具有如下的知識:前端

  1. maven的使用
  2. HTTP

SpringBoot的maven配置介紹

對於一個簡單的maven結構的項目來講,如下結構:java

${basedir}
    |-- pom.xml
    |-- src
        |-- main
            |-- java
                || com.xxx.xxx 項目源碼
            |-- resources
                || 項目配置文件 .xml等
        |-- test
            |-- java
            |-- resources

在咱們前面的教程中,咱們都是在編寫着代碼,從未關注過maven的pom.xml到底是如何配置的,由於項目腳手架已經配置完成了,也許有細心的同窗已經看過了pom.xml文件的內容了:git

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">

	<!-- ... 省略了當前項目自己信息的配置,若有須要,請讀者查看源碼 -->
    
    <!-- 當前pom的父級 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <dependencies>
		<!-- springboot框架web功能starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
		<!-- springboot框架的測試依賴starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- 構建流程使用SpringBoot的maven插件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

對於一個普通的maven項目,其實核心的就是dependencies節點-dependency節點了。經過dependency節點,咱們能夠GAV座標(Group-Artifact-Version)引入不一樣的庫jar包,便於咱們的項目使用這些庫。github

那麼爲何在上面的pom出現了一個parent節點呢?實際上,pom容許所謂的繼承:咱們能夠把一堆的pom依賴和配置,放到一個公共的pom裏面,而後各個項目經過parent節點去引用這個公共的pom文件。因爲SpringBoot並非一個單一的jar包構成的框架,它的內部其實依賴了下面的核心庫:web

spring-core
spring-beans
spring-context
...還有不少

若是手動一項又一項編寫dependency來使用Spring框架,不只僅容易遺漏,並且十分不方便進行這些依賴庫的版本管理。基於此,SpringBoot官方提供了父級pom:spring

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version> <!-- 2021/08/09最新 -->
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

那麼再看咱們的pom文件中,還依賴了spring-boot-starter-web,這又是什麼呢?難道有一個jar包叫作spring-boot-starter-web嗎?其實否則。咱們上面提到了parent POM,可是Spring框架下的依賴包特別多,而且有些包是核心的包,有些包則是在某些功能須要的狀況下才依賴的包。若是一股腦的所有經過parent引入會讓你的項目依賴十分臃腫,因此Spring官方再次按照包的功能進行了必定的組合,造成了所謂的starter,若是你只是想作web API的服務開發,用spring-boot-starter-web就能夠了,要是須要使用AOP(面向切面編程,做切面開發),加上spring-boot-starter-aop依賴便可。apache

一個簡單的Controller

// 使用註解 @RestController,代表當前類是一個基於REST 規範的HTTP API Controller
@RestController 
// @RequestMapping 註解放在Controller上,用於標記 HTTP url的路徑入口,
// 例如 http://localhost:8080/hello/xxx 纔會進入當前Controller
@RequestMapping("hello") 
public class HelloController {

    // @RequestMapping 註解放在Controller的裏面的方法上,
    // 將會與Controller上的RequestMapping組合成:"/hello/say"
    // method用於指示經過何種HTTP方法訪問
    // 在程序啓動後,咱們可使用GET方法訪問:http://localhost:8080/hello/say
    @RequestMapping(value = "say", method = RequestMethod.GET)
    public String say() {
        return "hello, world";
    }

}

編寫啓動類,啓動咱們的SpringBoot程序:編程

@SpringBootApplication
public class Chapter03App {

    public static void main(String[] args) {
        SpringApplication.run(Chapter03App.class, args);
    }

}

啓動成功後,經過HTTP訪問:瀏覽器

http://localhost:8080/hello/say
# 輸出:hello, world

關於Controller須要注意的點

上面的例子中,咱們使用註解@RestController來標記了咱們的Controller類,會有初學者使用@Controller來標記Controller,讓咱們改爲它試試:

@Controller // 改爲使用 @Controller註解,其餘不變,再次訪問,看看有什麼問題
@RequestMapping("hello")
public class HelloController {
// ...
}

重啓啓動應用後再次訪問對應地址。若是是在瀏覽器中,你會看到:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Mon Aug 09 10:16:59 CST 2021
There was an unexpected error (type=Not Found, status=404).

type=Not Found, status=404,404!爲何會這樣呢?

  1. 若是使用@Controller標記,那麼將使用SpringMVC架構(自行了解),若是對應的方法返回的是字符串,則這個字符串代表須要查找對應的視圖(View)名稱,並將對應的視圖經過視圖解析器(InternalResourceViewResolver)解析修改成前端web頁面。
  2. 若是使用@RestController註解Controller,則Controller中的方法沒法返回jsp頁面,或者html,配置的視圖解析器不起做用,返回的內容就是return裏的內容。

針對狀況1,解決方法就是在方法的返回上加上註解:@ResponseBody

@ResponseBody // 若是當前Controller被@Controller註解,又想返回字符串或其餘原始數據
    @RequestMapping(value = "say", method = RequestMethod.GET)
    public String say() {
        return "hello, world";
    }

Controller如何被加載的?

在以前的文章,咱們已經介紹了SpringBoot是如何初始化Bean而且將其放在IOC容器的。咱們提到了三種方式:一、@Component;二、Java配置類;三、XML配置。對於第二、3點,好像目前咱們的樣例中並無作手動配置的事情。那麼是否是咱們的Controller已經被@Component標記了呢?

查看@RestController的定義:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller // 原來你RestController也是一個Controller註解啊!
@ResponseBody // 而且,已經添加了@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

@RestController其實組合了@Controller@ResponseBody兩個註解了。咱們再看看@Controller的定義:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 原來你Controller註解也組合了Component註解
public @interface Controller {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

原來@Controller也已經組合了@Component註解啊,難怪咱們的定義的Controller能被加載。

Controller如何處理HTTP請求的?

要回答這個問題,須要你有必定的關於Java Tomcat Web容器的知識。本系列主要是對SpringBoot的快速入門,不便於講的過細。簡單一點就是:

  1. Tomcat是一個用Java編寫的Web容器,能夠接受HTTP請求。
  2. SpringBoot內置了Tomcat容器,而且對於HTTP請求,轉到了內部處理
  3. 對於這些HTTP請求,根據請求的路徑等上下文,匹配Bean容器中的Controller進行處理
相關文章
相關標籤/搜索