Spring Boot(一):快速入門及啓動原理

一、Spring Boot 簡介

Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置(約定優於配置),從而使開發人員不再需要定義樣板化的配置。

個人認爲,springboot 並非全新的框架,而是對spring框架的再封裝,通過 starter 的方式輕鬆集成第三方的框架,集成一個框架只需要引入一個 starter,去掉了 XML 的配置,全部用註解代替,然後在屬性文件中配置一些值,整個集成的過程就結束了。總的來說,使用springboot框架可以非常方便、快捷的進行spring項目的開發,提高我們開發的效率。

二、springBoot特點

  • 創建獨立的Spring應用程序
  • 嵌入的Tomcat,無需部署WAR文件
  • 簡化Maven配置
  • 自動配置Spring
  • 提供生產就緒型功能,如指標,健康檢查和外部配置
  • 絕對沒有代碼生成和對XML沒有要求配置

三、使用 IDEA 的 Spring Initializr 創建 springboot 項目

使用 IDEA Spring Initializr 創建項目,可以非常方便的創建一個 spring 項目,真正做到 just run

1、開發環境

  • –jdk1.8:Spring Boot 推薦jdk1.7及以上;java version “1.8.0_152”

  • –maven3.x:maven 3.3以上版本;Apache Maven 3.6.1

  • –IntelliJ IDEA:IntelliJ IDEA 2020.1

    使用IDEA Spring Initializr創建項目其實就是集成了springboot官網提供的 Spring Initializr 模板來創建項目,是需要連接網絡的,如果沒有網絡,建議使用maven方式創建項目。

2、點擊文件 -> 新建 -> 項目

由於使用了IntelliJ IDEA官方提供的中文插件,所以顯示的是中文,我發現換成中文後,自己反而好不習慣,後面已經改回英文了。。。
新建

3、選擇 Spring Initializr ,選擇JDK

需要注意的是springboot只支持 jdk 1.8及以上版本

在這裏插入圖片描述

4、填寫項目信息

springboot框架內嵌了Tomcat和Jetty容器,直接打成jar包也可直接運行,spring-boot-starter-web默認使用Tomcat容器運行,如需切換爲Jetty容器,只需在POM文件中添加Jetty的依賴即可。

在這裏插入圖片描述

5、選擇需要的依賴包

在這裏插入圖片描述

6、選擇存放項目的路徑

需要注意的是,這裏有可能會生成項目失敗,原因是因爲連接https://start.spring.io/網站超時,多嘗試幾次即可。

在這裏插入圖片描述

7、項目創建成功

看到這個界面,我們的第一個springboot項目就創建成功了(可以直接啓動,默認地址爲localhost:8080)。是不是很開心。。。

在這裏插入圖片描述

四、Hello World

上面我們創建了一個springboot的項目,已經可以成功運行了,只是項目裏面什麼都沒有,接下來我們來編寫一個hello world程序,並在瀏覽器中訪問。

1、編寫HelloController

package com.xiaoming.springboot.controller;

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

@RestController
public class HelloController {
    
    @RequestMapping("/hello")
    public String hello() {
        return "Hello World!";
    }
    
}

2、啓動程序

編寫好我們的controller類,我們就可以運行,並且從瀏覽器訪問了,是不是so easy。簡直不要太簡單了。

那麼如何啓動程序呢,我們需要找到程序的主入口類,在本篇就是SpringBootHelloApplication.class,(需要注意的是,在一個項目中,主入口類有且只能有一個),運行main方法即可。

在這裏插入圖片描述

3、頁面訪問

在瀏覽器中請求 localhost:8080/hello 地址,頁面就會給我們顯示出 Hello World! ,到這裏一個入門程序就已經完成了,各位程序猿,沖沖衝!!!

在這裏插入圖片描述

4、修改啓動端口和根路徑

在實際的開發中,程序的端口號是需要根據實際需求改動的,並且一般訪問路徑上需要帶上項目的名稱。此時我們需要修改springboot的默認端口和根路徑的配置,springboot提供了一個全局的配置文件給我們修改springboot自動配置的默認值。配置文件的名字是固定:(本人更喜歡yml格式的配置文件)

​ •application.properties

​ •application.yml

關於配置文件就不在這裏細說了,springboot配置相關的知識點我會再寫一篇博客。

如何修改配置文件呢,我們只需要將我們的application.properties文件先改名爲application.yml(不改也行,當然不該的格式和這裏不同),然後在配置文件中加入如下代碼即可:

server:
  port: 8888
  servlet:
    context-path: /xiaoming

修改完成重新啓動一下程序,可以看到我們的端口號和根路徑都變成了我們配置文件中的值。

在這裏插入圖片描述

五、揭開 springboot 的神祕面紗

在上面我們創建並運行了一個 springboot 項目,我們什麼都沒有配置,他憑什麼就能夠正常運行呢?相信各位小夥伴肯定是比較疑惑的。下面我們就來探究一下 springboot 爲什麼能夠這麼便捷的運行起來。

1、POM文件

既然我們的項目是一個maven項目,那麼我們就先來看看程序的 POM 文件裏面長什麼樣吧。

1)、parent 父工程

打開 POM 文件,我們可以在文件的頭部位置看到一個 parent 項目

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.2.7.RELEASE</version>
	<relativePath/> <!-- lookup parent from repository -->
</parent>

這個項目有什麼作用呢?

  1. 依賴的版本控制

    我們繼續查看他的POM文件,點進去可以看到他又有一個 parent 工程

    <parent>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-dependencies</artifactId>
    	<version>2.2.7.RELEASE</version>
    	<relativePath>../../spring-boot-dependencies</relativePath>
    </parent>
    

    繼續點進去,我們發現這個工程裏面保存了基本的依賴和版本的定義,如下圖
    在這裏插入圖片描述

    可以說這個項目是Spring Boot的版本仲裁中心,真正管理Spring Boot應用裏面的所有依賴版本。所以我們導入依賴默認是不需要寫版本(沒有在這個項目裏面管理的依賴自然還是需要聲明版本號),媽媽再也不擔心我們在開發大型項目時因爲依賴衝突產生的問題了。

  2. 定義了 Java 編譯版本爲 1.8

    回到 spring-boot-starter-parent 工程,還可以看到如下內容:

    <properties>
       <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
       <!-- 這也是爲什麼我們的jdk環境爲什麼需要1.8及以上了 -->
       <java.version>1.8</java.version>
       <resource.delimiter>@</resource.delimiter>
       <maven.compiler.source>${java.version}</maven.compiler.source>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <maven.compiler.target>${java.version}</maven.compiler.target>
    </properties>
    
  3. 設置使用 UTF-8 格式編碼。

  4. 執行打包操作的配置。

  5. 自動化的資源過濾。

  6. 自動化的插件配置。

  7. 針對 application.properties 和 application.yml 的資源過濾,包括通過 profile 定義的不同環境的配置文件,例如 application-dev.properties 和 application-dev.yml。

    <resources>
     <resource>
     <filtering>true</filtering>
     <directory>${basedir}/src/main/resources</directory>
     <includes>
       <include>**/application*.yml</include>
       <include>**/application*.yaml</include>
       <include>**/application*.properties</include>
     </includes>
     </resource>
     <resource>
     <directory>${basedir}/src/main/resources</directory>
     <excludes>
       <exclude>**/application*.yml</exclude>
       <exclude>**/application*.yaml</exclude>
       <exclude>**/application*.properties</exclude>
     </excludes>
     </resource>
    </resources>
    

2)、啓動器

接着往下查看項目導入的依賴,可以看到以下內容

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

可以發現其中有兩個依賴,一個是我們自己導入的 spring-boot-starter-web 依賴,另一個則是 Spring Initializr 創建項目時自動導入的測試依賴包(細心的小夥伴是不是已經發現了我們導入的依賴都沒有標明版本號)。我們主要關注我們自己導入的 spring-boot-starter-web 依賴包。

我們發現兩個依賴都有類似的名稱 spring-boot-starter-xxx,其中我們稱spring-boot-starter爲springboot啓動器,其實就是一系列依賴描述的組合信息,當我們需要用到那個模塊,我們只需要導入那個模塊的啓動器即可,springboot會自動的幫我們導入該模塊需要的一系列組件,並且有 parent 工程定義導入組件的版本,保證我們的程序能夠正常運行

點進 spring-boot-starter-web 項目的 POM 文件,不出所料,spring-boot-starter-web 啓動器幫我們導入了一系列的依賴包

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.2.7.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
    <version>2.2.7.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <!-- 這裏可以看出爲什麼我們的程序默認使用Tomcat容器運行 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>2.2.7.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>2.2.7.RELEASE</version>
    <scope>compile</scope>
    <exclusions>
      <exclusion>
        <artifactId>tomcat-embed-el</artifactId>
        <groupId>org.apache.tomcat.embed</groupId>
      </exclusion>
    </exclusions>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.6.RELEASE</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.6.RELEASE</version>
    <scope>compile</scope>
  </dependency>
</dependencies>

2、主入口類

我們開發任何一個Spring Boot項目,項目中都會有且只有一個啓動類,如下

package com.xiaoming.springboot;

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

@SpringBootApplication
public class SpringBootHelloApplication {

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

}

從上面代碼可以看出,我們需要關注的點有兩個,一個是@SpringBootApplication註解,另一個就是main方法中的SpringApplication.run()語句。

1)、@SpringBootApplication

@SpringBootApplication註解是Spring Boot的核心註解,它標註在某個類上就說明這個類是Spring Boot應用的主配置類,SpringBoot就應該運行這個類的main方法來啓動Spring Boot應用;我們點進這個註解中可以發現@SpringBootApplication註解其實是多個註解的組合。

@Target(ElementType.TYPE) // 註解的適用範圍,其中TYPE用於描述類、接口(包括包註解類型)或enum聲明
@Retention(RetentionPolicy.RUNTIME) // 註解的生命週期,保留到class文件中(三個生命週期)
@Documented // 表明這個註解應該被javadoc記錄
@Inherited // 子類可以繼承該註解
@SpringBootConfiguration // 繼承了Configuration,表示當前是註解類
@EnableAutoConfiguration // 開啓springboot的註解功能,springboot的四大神器之一,其藉助@import的幫助
@ComponentScan(excludeFilters = { // 包掃描路徑
   @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), 
   @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) 
})
public @interface SpringBootApplication {
   ...
}

雖然定義使用了7個註解進行了原信息標註,但實際上重要的只有三個註解:

  • @SpringBootConfiguration(點開查看發現裏面還是應用了@Configuration註解)
  • @EnableAutoConfiguration
  • @ComponentScan

我們可以這樣認爲:@SpringBootApplication ≈ @Configuration + @EnableAutoConfiguration + @ComponentScan

如果我們不嫌麻煩的話,我們的啓動類也可以寫成這樣子

package com.xiaoming.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class SpringBootHelloApplication {

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

}

可以看到,運行效果和@SpringBootApplication是一樣
在這裏插入圖片描述

2)、@SpringBootConfiguration

@SpringBootConfiguration註解標註在某個類上,表示這是一個Spring Boot的配置類,底層使用的是@Configuration註解,需要注意的是,配置類本身也是IOC容器中的一個組件。

3)、@ComponentScan

@ComponentScan這個註解在Spring中很重要,它的功能其實就是自動掃描並加載符合條件的組件(比如@Component和@Repository等)或者bean定義,最終將這些bean定義加載到IOC容器中。

注意:如果你的其他包都在使用了@SpringBootApplication註解的main app所在的包及其下級包,則你什麼都不用做,SpringBoot會自動幫你把其他包都掃描了。

如果你有一些bean所在的包,不在main app的包及其下級包,那麼你需要手動加上@ComponentScan註解並指定那個bean所在的包。

所以SpringBoot的啓動類最好是放在root package下,因爲默認不指定basePackages。

4)、@EnableAutoConfiguration

@EnableAutoConfiguration 註解告訴Spring Boot開啓自動配置功能,這樣自動配置才能生效。他的底層是使用@Import註解,將所有符合自動配置條件的bean定義加載到IOC容器。

@EnableAutoConfiguration作爲一個複合Annotation,其自身定義關鍵信息如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...
}

其中主要的註解就是,@AutoConfigurationPackage和 @Import(EnableAutoConfigurationImportSelector.class)

  • @AutoConfigurationPackage 的底層其實也是調用了 @Import(AutoConfigurationPackages.Registrar.class) 註解

    @Import(AutoConfigurationPackages.Registrar.class)的其實就是由AutoConfigurationPackages.Registrar類將主配置類所在包及下面所有子包裏面的所有組件掃描並註冊到IOC容器中
    在這裏插入圖片描述

  • @Import(EnableAutoConfigurationImportSelector.class)中的EnableAutoConfigurationImportSelector就是自動配置導入選擇器,它來決定那些組件能夠被導入進去,它會將所有需要導入的組件以全類名的方式返回,返回的組件就會別添加到IOC容器中。

    // springboot 獲取自動配置類的源碼
    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
    		AnnotationMetadata annotationMetadata) {
    	if (!isEnabled(annotationMetadata)) {
    		return EMPTY_ENTRY;
    	}
    	AnnotationAttributes attributes = getAttributes(annotationMetadata);
    	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    	configurations = removeDuplicates(configurations);
    	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    	checkExcludedClasses(configurations, exclusions);
    	configurations.removeAll(exclusions);
    	configurations = filter(configurations, autoConfigurationMetadata);
    	fireAutoConfigurationImportEvents(configurations, exclusions);
    	return new AutoConfigurationEntry(configurations, exclusions);
    }
    

    進入源碼debug查看返回了那些類,如下圖
    在這裏插入圖片描述

    我們發現@Import(EnableAutoConfigurationImportSelector.class)會給容器中導入非常多的自動配置類(xxxAutoConfiguration),就是給容器中導入這個場景需要的所有組件,並配置好這些組件,有了這些自動配置類,就免去了我們手動編寫配置注入功能組件的工作;

    在 getAutoConfigurationEntr() 方法中調用了一個非常關鍵的方法,就是 getCandidateConfigurations(),源代碼如下

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
    			getBeanClassLoader());
    	Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
    			+ "are using a custom packaging, make sure that file is correct.");
    	return configurations;
    }
    

    該方法中的SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader()); 就是我們能夠獲取到這麼多自動配置類的關鍵。

    SpringFactoriesLoader屬於Spring框架私有的一種擴展方案,其主要功能就是從指定的配置文件META-INF/spring.factories加載配置。

    配合@EnableAutoConfiguration使用的話,它更多是提供一種配置查找的功能支持,即根據@EnableAutoConfiguration的完整類名org.springframework.boot.autoconfigure.EnableAutoConfiguration作爲查找的Key,獲取對應的一組@Configuration類。我們去到源代碼查看,如下圖
    在這裏插入圖片描述
    所以,@EnableAutoConfiguration自動配置就是:從classpath中搜尋所有的META-INF/spring.factories配置文件,並將其中org.springframework.boot.autoconfigure.EnableutoConfiguration對應的配置項通過反射實例化爲對應的標註了@Configuration的JavaConfig形式的IOC容器配置類,然後彙總爲一個並加載到IOC容器中。(詳細的自動配置原理,我會放到springboot配置相關的博客中講解的,邊學習springboot邊寫博客,讓自己理解的更加透徹一點,嘻嘻嘻)

    在上面編寫的Hello World程序中我們添加了spring-boot-starter-web啓動器,程序會自動添加Tomcat和Spring MVC的依賴,然後Spring Boot就會對Tomcat和Spring MVC進行自動配置。

看到這裏各位小夥伴心中的疑惑是不是已經解開了呢?明明我們什麼都沒有配置,程序卻能正常運行,其實和我們用spring開發時的配置相比一樣沒少,只不過是springboot將以前需要我們程序猿配置的東西,自動配置好了,真正做到了 “just run”。這就是 springboot 這麼火爆的理由之一吧。

感謝你看到這裏

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