天天看點

Spring Boot并不重複“造輪子”

2.1 Spring Boot簡介

Spring Boot是由Pivotal團隊提供的基于Spring的全新架構,其設計目的是簡化Spring應用的搭建和開發過程。該架構遵循“約定大于配置”原則,采用特定的方式進行配置,進而使開發者無須進行大量的XML配置。Spring Boot緻力于成為蓬勃發展的快速應用開發領域的上司者。

Spring Boot并不重複“造輪子”,而是在原有Spring架構的基礎上進行封裝,并且它內建了一些類庫,用于簡化開發。換句話說,Spring Boot就是一個大容器。

關于Spring Boot,其官網是這樣描述的:

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.

We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration.

從上面的描述中,我們可以了解到,Spring Boot帶給了我們全新的應用部署方案,通過它可以很友善地建立獨立的、生産級的基于Spring的應用程式。同時,通過Spring平台和第三方庫可以輕松建構視圖。

其實,Spring Boot預設內建了Tomcat,是以我們可以隻編譯成jar包,通過Java指令啟動應用,大多數Spring Boot應用程式隻需要很少的Spring配置。

2.2 第一個Spring Boot工程

本節中,我們将建立第一個Spring Boot工程,讀者可以按照下面的步驟進行操作。

(1) 打開IntelliJ IDEA,依次點選File→New→Module,在彈出的對話框中選擇Maven,并點選Next按鈕,建立一個Maven項目。這裡我們在ArtifactId一欄中輸入demo-lesson-one,在GroupId一欄中輸入com.lynn.boot。建立好工程後,為pom.xml增加以下内容:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
           

其中,标簽聲明了Spring Boot的父項目,版本号定義為2.0.3.RELEASE。我們還可以注意到,标簽中聲明了spring-boot-starter-web依賴,它提供了對Spring MVC的支援。

(2) 編寫應用啟動類Application:

package com.lynn.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

Spring Boot的強大之處在于可以直接通過main方法啟動Web應用程式。在上述代碼中,我們提供了應用程式的入口,通過調用SpringApplication.run()來啟動内置Web容器。我們注意到,在Application類中添加了@SpringBootApplication注解,我們将在2.4節中介紹它的作用。

預設情況下,Spring Boot内置了Tomcat。當然,它還支援其他容器,如Jetty。倘若我們要将預設容器改為Jetty,可以将pom.xml檔案修改成下面這樣:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
           

在上述代碼中,我們通過标簽将Tomcat的依賴包移除,并增加了Jetty的依賴包。

(3) 編寫控制器以驗證Spring Boot架構:

package com.lynn.boot.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @RequestMapping(value = "hello")
    public String hello(){
       return "Hello World!";
    }
}
           

在上述代碼中,@RestController注解訓示了該類為控制器類,與它對應的注解是 @Controller。@RestController注解相當于 @Controller注解和@ResponseBody注解的結合。@RequestMapping注解的作用是定義一個HTTP請求位址,預設不限制請求方式,可以是GET、POST亦或其他方法,如果要限制請求方法,可以在注解後面增加method屬性,如method=RequestMethod.GET表示隻有GET請求才能調用該HTTP位址。

上面提到的注解均為Spring MVC注解,我們之是以能夠在這裡很友善地使用Spring MVC注解,是因為第(1)步的依賴中添加了spring-boot-starter-web依賴,該依賴內建了Spring MVC。

(4) 運作Application類的main方法,并通路localhost:8080/hello,即可看到如圖2-1所示的界面。

Spring Boot并不重複“造輪子”

通過以上示例,我們可以知道:

 使用Spring Boot建立一個工程非常簡單,既沒有XML配置檔案,也沒有Tomcat,通過幾個簡單的注解,運作main方法就能啟動一個Web應用;

 Spring Boot預設内置Tomcat;

 Spring Boot用注解代替了煩瑣的XML配置。

2.3 使用YAML檔案配置屬性

在上一節中,我們實作了一個最簡單的Web工程,沒有建立任何配置檔案。當然,Spring Boot的任何配置都可以通過代碼實作。為了便于擴充,它引入了PROPERTIES格式和YAML格式 ①的檔案,可以在其中定義一些常用屬性或自定義屬性。

2.3.1 YAML的基本用法

下面我們先來看一下Spring Boot的一般配置,步驟如下。

(1) 在src/main/resources目錄下建立一個名為application.yml的配置檔案,并編寫以下内容:

server:
    servlet:
        #定義上下文路徑
        context-path: /demo
    #定義工程啟動的端口
    port: 8081
           

在上述配置中,我們通過server.servlet.context-path定義了應用的上下文路徑為/demo,它的預設值為/,server.port定義應用的啟動端口,其預設值為8080,這裡設定為8081。

(2) 啟動工程并通路localhost:8081/demo/hello,就可以看到如圖2-1所示的界面。

① 本書的所有示例都使用了YAML 栺式的配置檔案。
           

在2.2節中,我們啟動工程時的監聽端口為8080,上下文路徑為/,但是我們并沒有配置任何資訊,那是因為所有配置屬性都有預設值,如端口的預設值為8080。

接下來,我們看一下YAML檔案的結構,其基本格式為:

key1:
    key2:
        key3: value
           

我們将它替換成properties的形式,即key1.key2.key3=value。當然,key的個數不是固定的。這裡需要說明的是,YAML格式非常嚴格。如果目前key後面需要跟value,則冒号後面必須至少有一個空格,否則編譯不會通過;其次,每個子屬性之間需要通過空格或制表符(即按下Tab鍵)分隔,否則可能無法正确取到屬性值。

如果我們将上面例子中的YAML檔案改成以下形式:

server:
    servlet:
        context-path: /demo
    #冒号後面直接跟端口号
    port:8081
           

那麼啟動工程後,控制台會列印如下的報錯資訊:

Caused by: org.yaml.snakeyaml.scanner.ScannerException: while scanning a simple key in 'reader', line 6, column 3:
    port:8081
    ^
could not find expected ':'
in 'reader', line 6, column 12:
    port:8081
           

2.3.2 多環境配置

在一個企業級應用中,我們可能開發時使用開發環境,測試時使用測試環境,上線時使用生産環境。每個環境的配置都不一樣,比如開發環境的資料庫是本地位址,而測試環境的資料庫是測試位址。是以會遇到這樣一個問題:我們在打包的時候,如何生成不同環境的包呢?

這裡的解決方案有很多,具體如下。

 每次編譯之前,手動把所有配置資訊修改成目前運作的環境資訊。這種方式導緻每次都需要修改,相當麻煩,也容易出錯。

 利用Maven,在pom.xml裡配置多個環境,每次編譯之前将settings.xml修改成目前要編譯的環境ID。這種方式的缺點就是每次都需要手動指定環境,而且如果環境指定錯誤,釋出前是不知道的。

 建立多個針對不同環境的配置檔案,通過啟動指令指定。這個方案就是本節重點介紹的,也是我強烈推薦的方式。

接下來,我們看一下配置多環境的步驟。

(1) 将application.yml檔案修改如下:

server:
    servlet: 
        context-path: /demo 
    port: 8081
spring:
    profiles:
        active: dev
           

這裡通過spring.profiles.active指了明目前啟動的環境。

(2) 建立多環境配置檔案,檔案命名格式為application-{profile}.yml,其中{profile}即為上述配置将要指定的環境名,如新增名為application-dev.yml的檔案,我們可以在裡面添加配置:

server:
    port: 8080
           

并将spring.profiles.active設定為dev。

此時啟動工程,可以看到工程的監聽端口已變為8080。

你可以繼續建立多環境檔案,比如命名為application-test.yml,将監聽端口改為8082,然後将spring.profiles.active改為test,再啟動工程觀察效果。在實際項目釋出的過程中,不會手動修改spring.profiles.active的值,而是通過啟動指令來動态修改,具體細節見2.7節。

2.4 常用注解

前面提到過,Spring Boot主要是以注解形式代替煩瑣的XML配置。在這一節中,我将帶領大家了解一些常用注解的用法。

2.4.1 @SpringBootApplication

在前面的章節中,讀者是否注意到,Spring Boot支援main方法啟動。在我們需要啟動的主類中加入注解 @SpringBootApplication,就可以告訴Spring Boot這個類是工程的入口。如果不加這個注解,啟動就會報錯。讀者可以嘗試去掉該注解,看一下效果。

檢視 @SpringBootApplication注解的源碼,可以發現該注解由 @SpringBootConfiguration、@EnableAutoConfiguration和 @ComponentScan組成。我們可以将 @SpringBootApplication替換為以上3個注解,如:

package com.lynn.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class Application {

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

此時代碼的運作效果與2.2節一緻。

2.4.2 @SpringBootConfiguration

加入了 @SpringBootConfiguration注解的類會被認為是Spring Boot的配置類。我們既可以在application.yml中進行一些配置,也可以通過代碼進行配置。

如果要通過代碼進行配置,就必須在這個類中添加 @SpringBootConfiguration注解。我們既可以在标注了這個注解的類中定義Bean,也可以通過它用代碼動态改變application.yml的一些配置。例如,建立WebConfig類,并改變工程啟動的端口号:

package com.lynn.boot;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;

@SpringBootConfiguration
public class WebConfig implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        //給代碼設定應用啟動端口
        factory.setPort(8888);
    }
}
           

啟動工程,可以看到監聽端口已經變成了8888。

說明:如果YAML配置檔案和代碼配置了同樣的屬性,則會以代碼配置為準。因為在Spring Boot應用啟動後,會先加載配置檔案,然後再執行被 @SpringBootConfiguration标注的類,是以它會覆寫配置檔案配置的屬性。

此外,也可以使用 @Configuration注解,它和 @SpringBootConfiguration的效果一樣,不過Spring Boot官方推薦采用 @SpringBootConfiguration注解。

2.4.3 @Bean

@Bean注解是方法級别的注解,主要添加在 @SpringBootConfiguration注解的類中,有時也添加在 @Component注解的類中。它的作用是定義一個Bean,類似于Spring XML配置檔案的。

下面我們就來看一下如何通過 @Bean注解注入一個普通類。

(1) 建立一個普通類Person,為了便于測試,我們為該類增加了一個字段name:

package com.lynn.boot.bean;

public class Person {
    private String name;

    public void setName(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
           

(2) 在2.4.2節建立的WebConfig類中增加以下代碼:

@Bean
public Person person(){
    Person person = new Person();
    person.setName("lynn");
    return person;
}
           

在上述代碼中,我們通過一個 @Bean注解就可以将Person對象加入Spring容器中,它簡化了傳統的Spring XML的方式。

(3) 進行單元測試。首先,添加單元測試依賴:

org.springframework.boot
spring-boot-starter-test
test
           

Spring Boot預設內建JUnit測試架構,通過添加spring-boot-starter-test依賴就可以內建它。然後在src/main/test目錄下建立一個測試類,并編寫測試代碼:

package com.lynn.boot.test;

import com.lynn.boot.Application;
import com.lynn.boot.bean.Person;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@SpringBootTest(classes = Application.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class MyTest {

    @Autowired
    private Person person;

    @Test
    public void test(){
        System.out.println(person);
    }
}
           

在上述代碼中,我們添加 @SpringBootTest注解來指定入口類為Application,再添加 @RunWith注解指定單元測試的運作環境為SpringJUnit4ClassRunner,即使用JUnit4的單元測試架構,接着通過 @Autowired注解注入了Person類,最後通過test方法列印person資訊。

注意:在test方法中需要添加 @Test注解才能啟用單元測試。

啟動單元測試時,可以看到控制台列印出了以下資訊:

Person{name='lynn'}
           

2.4.4 @Value

通常情況下,我們需要定義一些全局變量,此時想到的方法是定義一個public static常量并在需要時調用它。那麼是否有其他更好的方案呢?答案是肯定的,這就是本節要講的 @Value注解。

(1) 在application.yml裡自定義一個屬性data:

self:
    message:
        data: 這是我自定義的屬性
           

上述配置不是Spring Boot内置屬性,而是我們自定義的屬性。

(2) 修改HelloController類:

package com.lynn.boot.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Value("${self.message.data}")
    private String value;

    @RequestMapping(value = "hello",produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String hello(){
        return value;
    }
}
           

其中,@Value注解的參數需要使用 ${} 将目标屬性包裝起來,該屬性既可以是Spring内置的屬性,也可以是自定義的屬性。

注意:如果傳回的是String類型的值,那麼需要注明produces為application/json并且charset=utf8,否則可能會出現亂碼;如果傳回的是對象,則無須注明。因為Spring MVC不會對傳回的String類型的值做任何處理,而如果傳回對象的話,會執行Spring預設的JSON轉換器,它會處理編碼問題。

(3) 啟動工程并通路localhost:8080/demo/hello,可以看到如圖2-2所示的界面。

Spring Boot并不重複“造輪子”

說明:@Value注解可以擷取YAML檔案的任何屬性值,它的好處如下:

 可以通過啟動參數動态改變屬性值,而不用修改代碼;
           

 交給Spring統一管理常量,便于擴充和維護。

2.5 Spring Boot內建模闆引擎

在傳統的Spring MVC架構中,我們一般将JSP、HTML頁面放到webapps目錄下。但Spring Boot沒有webapps,更沒有web.xml,如果要寫界面的話,該如何做呢?

我們可以內建模闆引擎。Spring Boot官方提供了幾種模闆引擎:FreeMarker、Velocity、Thymeleaf、Groovy、Mustache和JSP。本節中,我們以FreeMarker為例講解Spring Boot是如何內建模闆引擎的。

首先,在pom.xml中添加對FreeMarker的依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
           

在resources目錄下建立static和templates兩個目錄,如圖2-3所示。其中static目錄用于存放靜态資源,譬如CSS、JavaScript和HTML等,templates目錄用于存放模闆引擎檔案。

Spring Boot并不重複“造輪子”

然後在templates目錄下面建立index.ftl ①檔案,并添加如下内容:

<!DOCTYPE html>
<html>
    <head>
    </head>
    <body>
        <h1>Hello World!</h1>
    </body>
</html>
           

接着建立控制器類:

@Controller
public class PageController {

    @RequestMapping("index.html")
    public String index(){
        return "index";
    }
}
           
① freemarker 檔案的預設字尾為.ftl。
           

最後,啟動Application.java,通路localhost:8080/demo/index.html,就可以看到如圖2-4所示的界面。

Spring Boot并不重複“造輪子”

在上述代碼中,我們要傳回FreeMarker模闆頁面,是以必須将其定義為 @Controller,而不是前面定義的 @RestController。@RestController相當于 @Controller和 @ResponseBody的結合體。标注為 @RestController注解時,SpringMVC的視圖解析器(ViewResolver)将不起作用,即無法傳回HTML或JSP頁面。ViewResolver的主要作用是把一個邏輯上的視圖名解析為一個真正的視圖。當我們将一個控制器标注為 @Controller并傳回一個視圖名時,ViewResolver會通過該視圖名找到實際的視圖,并呈現給用戶端。

2.6 更改預設的JSON轉換器

Spring Boot預設使用Jackson引擎去解析控制器傳回的對象,該引擎在性能和便捷性上與第三方引擎(FastJson和Gson等)還有一定的差距,本節将介紹如何将預設轉換器替換為FastJson轉換器。

(1) 在pom.xml中添加對FastJson的依賴:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>
           

(2) 修改WebConfig類,為其添加方法并設定FastJson轉換器:

@SpringBootConfiguration
public class WebConfig extends WebMvcConfigurationSupport{
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        FastJsonHttpMessageConverter fastConverter=new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig=new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.PrettyFormat
        );
        List<MediaType> mediaTypeList = new ArrayList<>();
        //設定編碼為UTF-8
        mediaTypeList.add(MediaType.APPLICATION_JSON_UTF8);
        fastConverter.setSupportedMediaTypes(mediaTypeList);
        fastConverter.setFastJsonConfig(fastJsonConfig);
        converters.add(fastConverter);
    }
}
           

首先應繼承WebMvcConfigurationSupport類,該類提供了Spring Boot對Spring MVC的支援。然後重寫configureMessageConverters方法,該方法配置了消息轉換器。如果第三方架構希望處理Spring MVC中的請求和響應時,那麼需要實作HttpMessageConverter接口。而在上述代碼中,FastJsonHttpMessageConverter便是如此,它實作了HttpMessageConverter接口,并通過FastJsonConfig設定FastJson的處理參數,如通過MediaType設定編碼為UTF-8,最後添加到HttpMessageConverter中。

這樣Spring MVC在處理響應時就可以将JSON解析引擎替換為FastJson。

說明:前面提到,如果控制器傳回的是String類型的值,則需要顯式設定編碼。我們替換成FastJson後,由于已經設定了編碼,是以無論是字元串還是對象,都無須設定編碼方式,讀者可以試一試。

2.7 打包釋出到伺服器上

Spring Boot支援使用jar和war兩種方式啟動應用,下面分别來介紹這兩種方式是怎麼啟動的。

2.7.1 使用内置Tomcat釋出jar包

由于Spring Boot内置了Tomcat,我們可以将工程打包成jar,通過Java指令運作我們的Web工程,具體步驟如下。

(1) 在pom.xml檔案中添加以下内容:

<build>
    <finalName>api</finalName>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <fork>true</fork>      
         <mainClass>com.lynn.boot.Application</mainClass>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-resources-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <encoding>UTF-8</encoding>
                <useDefaultDelimiters>true</useDefaultDelimiters>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.18.1</version>
            <configuration>
                <skipTests>true</skipTests>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
 </build>
           

在pom.xml中,

<build>

标簽定義了關于Maven編譯和打包的一些資訊。其中,

<finalName>

為打包後的檔案名,

<plugins>

設定了編譯的一些參數。Maven支援第三方插件,而Spring Boot的編譯插件就是spring-boot-maven-plugin,并通過

<mainClass>

指定了啟動類。後面maven-surefire-plugin就是Maven官方提供的用于建構測試用例的插件,如果有單元測試類,它在編譯完成後會執行單元測試,單元測試成功後才會打包;如果不希望執行單元測試,那麼将

<skipTests>

設定為true即可。我建議将設定為true,如果設定為false,會導緻打包時間過長。如果單元測試類中存在對資料庫的增删改測試,編譯時執行了它,可能會對原有資料造成影響。maven-compiler-plugin為Maven官方提供的指定編譯器版本的插件,上述代碼中的1.8表示使用JDK 1.8版本編譯。

(2) 通過mvn clean package編譯并打包,如圖2-5所示。

Spring Boot并不重複“造輪子”

(3) 将打包的内容上傳到伺服器中,運作指令:

java -jar api.jar
           

這樣就能啟動一個Spring Boot應用。前面提到,可以通過指令參數來設定不同環境或者動态設定參數,那麼如何設定呢?下面以設定環境為例,輸入指令:

java -jar api.jar --spring.profiles.active=dev
           

應用啟動時,就會拉取application-dev.yml内的配置資訊。如果你想改變任何屬性值,在--後面加上相應的屬性名和要改變的屬性值即可。

2.7.2 打包成war包釋出

除了編譯成jar包釋出外,Spring Boot也支援編譯成war包部署到Tomcat。

(1) 在pom.xml中将應用打包格式改成war:

<packaging>war</packaging>
           

這裡的就是告訴Maven,需要編譯成何種字尾的檔案。

(2) 将标簽下的内容修改如下:

<build>
    <finalName>api</finalName>
    <resources>
        <resource>
             <directory>src/main/resources</directory>
             <filtering>true</filtering>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <artifactId>maven-resources-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.18.1</version>
            <configuration>
                <skipTests>true</skipTests>
            </configuration>
        </plugin>
    </plugins>
</build>
           

上述内容和2.7.1節中的内容相似,增加了maven-resources-plugin插件,它用于編譯resources目錄下的檔案。而在spring-boot-maven-plugin插件中無須指定

<mainClass>

,因為編譯後的war部署在外部Tomact上,它依托于Tomcat容器運作,不會執行main方法。

(3) 添加Tomcat依賴,将

<scope>

設定為provided。這樣做的目的是編譯時去掉tomcat包,否則啟動時可能會報錯。我們也不能直接通過

<exclusion>

标簽去掉tomcat包,因為在本地開發時,需要通過Application類啟動。相關代碼如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>
           

(4) 修改啟動類Application,它繼承了SpringBootServletInitializer類,并重寫了configure方法,以便Tomcat在啟動時能加載Spring Boot應用:

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
}
           

在上述代碼中,如果我們是通過外部Tomcat啟動應用,則可以去掉main方法。因為Tomcat在啟動時會執行configure方法,而configure方法會調用source方法并指定Application類,其作用與main方法一緻。

(5) 使用mvn clean package編譯并打包成WAR格式的檔案,然後将其複制到Tomcat中。啟動Tomcat,可以看到應用能夠被正常通路。如果通過外部Tomcat啟動應用,則server.port指定的端口失效,轉而使用Tomcat設定的端口号。

通過war啟動程式無法像jar包那樣,在啟動時指定運作環境或其他想要動态改變的參數值,且上下文路徑以war包的名字為準,還需要自己安裝Tomcat,比較麻煩,是以我推薦優先考慮jar包的啟動方式。

注意:如果以war方式部署多個Spring Boot工程到一個Tomcat下,可能會報錯,其原因是Spring Boot的資源管理是預設打開的,而兩個項目同時使用會沖突。此時需要在每個項目中增加以下配置:

spring:
    jvm:
       default-domain: api
           

其中,在default-domain後面需要設定domain名,以保證每個工程的domain不一緻,這樣才能同時啟動多個工程。

2.8 WebFlux快速入門

Spring Boot 2.0為我們帶來了WebFlux,它采用Reactor作為首選的流式架構,并且提供了對RxJava的支援。通過WebFlux,我們可以建立一個異步的、非阻塞的應用程式。接下來,我們就一起來領略WebFlux的風采。

(1) 建立一個基于Spring Boot的Maven工程,将其命名為demo-lesson-one-webflux,然後在pom.xml檔案中添加對WebFlux的依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
           

(2) 編寫一個Handler,用于包裝資料:

@Component
public class HelloHandler {

    public Mono<ServerResponse> hello(ServerRequest request) {
        return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
            .body(BodyInserters.fromObject("Hello, World!"));
    }
}
           

該類自定義了一個方法,該方法傳回Mono ①對象。這裡在ServerResponse的body方法中設定要傳回的資料。

(3) 編寫接口類,即定義我們通常所說的路由位址(接口位址):

@SpringBootConfiguration
public class HelloRouter {
    @Bean
    public RouterFunction<ServerResponse> routeHello(HelloHandler helloHandler) {
        return RouterFunctions.route(RequestPredicates.GET("/hello")
            .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), helloHandler::hello);
    }
}
           
① Mono 是WebFlux 中屬于publisher(収布者)的類。在WebFlux 中,開収者的方法隻需傳回Mono 或Flux 類即可。
           

因為路由需要注冊到Spring容器中,是以該類也需要添加 @SpringBootConfiguration注解,而将傳回的路由辨別為一個Bean,這樣才能注冊到Spring容器中。

在上述代碼中,我們定義一個方法routeHello并且傳回了RouterFunction對象。在RouterFunction中,指定路由位址為/hello,并指定Handler和對應的方法,即前面建立的HelloHandler。這樣通過路由位址/hello就可以傳回Handler的hello方法所設定的資料。

(4) 啟動Application.java并通路位址localhost:8080/hello,可以看到浏覽器正常顯示HelloWorld。

通過控制台,我們可以很清楚地看到它是通過NettyServer① 啟動的:

Netty started on port(s): 8080
           

這樣我們就建立了一個路由位址。細心的讀者可以發現,上述代碼過于煩瑣,Spring Boot也考慮到了這一點。為了便于将MVC應用遷移到WebFlux,Spring Boot官方相容了WebFlux和MVC,即我們可以使用MVC的注解來建立WebFlux的路由位址。

(1) 建立HelloController類并編寫以下代碼:

@RestController
public class HelloController {
    @RequestMapping(value = "hello")
    public Mono<String> hello(){
        return Mono.just("Hello World!");
    }
}
           

可以看到,上述代碼和前面編寫的代碼很相似,隻是這裡我們需要傳回Mono對象,WebFlux将資料都包裝到Mono傳回,通過調用just方法即可。just方法傳入的參數類型取決于Mono後面的泛型指定的類型。

(2) 啟動Application.java,我們可以得到和前面代碼一樣的結果。

說明:如果我們通過 @Controller和 @Router兩種方式定義了相同名字的路由位址,則會優先采用@Router方式。

① Netty 是一個異步的、事件驅動的網絡應用程式架構。
           

2.9 小結

通過本章的學習,我們了解了Spring Boot的基本用法并感受到了YAML的優雅。本章涵蓋了一些實際項目中可能會用到的知識點,如常用注解、Spring Boot預設引擎的內建、JSON轉換器的更改以及編譯部署應用等。最後還介紹了目前較為流行的WebFlux架構。在後面的内容中,我們将進一步學習Spring Boot的其他特性。

本文摘自《Spring Cloud實戰演練》

Spring Boot并不重複“造輪子”