【SpringBoot-In-Action】一、Spring Boot快速入門
本系列教程根據本人實際學習使用 SpringBoot2.x 過程總結整理而來。
1、Spring Boot 簡介
Spring Boot 用來簡化 Spring 應用開發,約定大於配置,刪繁就簡,just run 就能建立一個獨立的、產品級的應用
-
出現背景:
J2EE(例如 Spring ) 笨重的開發、繁多的配置、低下的開發效率、負責的部署流程和第三方技術整合難度大。
-
解決方案:
“Spring 全家桶” 時代:
Spring Boot → J2EE 一站式解決方案
Spring Cloud → 分散式整體解決方案
-
優點:
快速建立獨立執行的 Spring 專案以及與主流框架整合
使用嵌入式的 Servlet 容器,應用無需打成 war包
starters 自動依賴於版本控制
大量的自動配置,簡化開發,也可以修改預設屬性值
開箱即用,無需配置 xml,無程式碼生成
生產環境的執行時應用監控
與雲端計算天然整合
總結:
- 簡化Spring應用開發的一個框架;
- 整個Spring技術棧的一個大整合;
- J2EE開發的一站式解決方案;
2、微服務
- 2014由martin fowler提出
- 微服務:架構風格(服務微化)
- 一個應用應該是一組小型服務;可以通過HTTP的方式進行互通;
- 單體應用:ALL IN ONE
- 微服務:每一個功能元素最終都是一個可獨立替換和獨立升級的軟體單元;
- 詳細參照微服務檔案
單體應用:
微服務:
3、環境準備
開發環境:
- jdk1.8:Spring Boot 推薦jdk1.7及以上;java version "1.8.0_221"
- maven3.x:maven 3.3以上版本;apache-maven-3.6.1
- IntelliJIDEA2019:IntelliJ IDEA 2019.1.3 x64
- SpringBoot 2.2.1.RELEASE:2.2.1;
開發配置:
1、MAVEN設定:
給 maven 的 settings.xml
配置檔案的 profiles
標籤新增下面的配置,設定 maven 的建立專案時的預設編譯版本使用 jdk8
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
複製程式碼
2、IDEA設定:
Idea 整合 Maven:
4、建立 Spring Boot HelloWorld
1、快速開始
首先我們先建立一個統一的 maven 工程,用來管理我們之後學習中所有的專案:
1、新建專案
2、選擇Maven專案
3、填寫GroupId、ArtifactId
GroupId:com.demo.springboot
ArtifactId:SpringBoot-In-Action
5、填寫專案名和專案路徑
6、建立一個空的maven專案完成,選擇允許自動匯入
7、刪除多餘的目錄
只保留一個pom.xml檔案即可,同時在pom檔案中新增springboot依賴和打包型別
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<packaging>pom</packaging>
複製程式碼
2、現在開始我們的 Spring Boot HelloWorld 專案:
一個功能:瀏覽器傳送hello請求,伺服器接受請求並處理,響應Hello World字串
;
1、建立一個maven工程;(jar)
在SpringBoot-In-Action專案中新建一個module,選擇maven工程,命名為 spring-boot-01-helloworld
2、匯入spring boot相關的依賴
因為springboot的依賴已經在父pom中匯入
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
複製程式碼
所以在當前的pom中只需要匯入spring-boot-starter-web的依賴即可:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
複製程式碼
完整 pom
檔案如下:
<?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">
<parent>
<artifactId>SpringBoot-In-Action</artifactId>
<groupId>com.demo.springboot</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-01-helloworld</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
複製程式碼
3、編寫一個主程式;啟動Spring Boot應用
/**
* @SpringBootApplication 來標註一個主程式類,說明這是一個Spring Boot應用
*/
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
// Spring應用啟動起來
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}
複製程式碼
4、編寫相關的Controller、Service
為了演示,我在這裡只建立了 Controller
類,省略了 Service
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/hello")
public String hello() {
return "Hello World!";
}
}
複製程式碼
5、執行主程式測試效果
選中 HelloWorldMainApplication
右鍵單擊選擇 Run ...
,或者開啟 HelloWorldMainApplication
按下快捷鍵 Ctrl+Shift+F10
執行程式,可以看到專案在 8080
埠啟動:
在瀏覽器中訪問 http://localhost:8080/hello 可以看到正常返回了 Hello World
。
至此,我們的第一個 Spring Boot 專案算是建立完了。
6、補充:簡化部署
在當前專案,即 spring-boot-01-helloworld
的 pom
檔案中新增下面的配置
<!-- 這個外掛,可以將應用打包成一個可執行的jar包;-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
複製程式碼
然後選中當前 pom
檔案,右鍵單擊,選擇 Run Maven
→ package
,將這個應用打成 jar
包,可以看到 target
資料夾下生成一個名為 spring-boot-01-helloworld-1.0-SNAPSHOT.jar
的 jar
檔案,我們可以直接在命令列使用 java -jar xxx.jar
命令進行執行,啟動後在瀏覽器中訪問 http://localhost:8080/hello 可以看到正常返回了 Hello World
。若要結束程式,關閉命令列視窗即可。
思考:為什麼我們打包後的程式可以直接在命令列使用
java jar
命令就可以部署?
帶著疑問我們來分析一下 spring-boot-01-helloworld-1.0-SNAPSHOT.jar
,我們使用壓縮檔案開啟 spring-boot-01-helloworld-1.0-SNAPSHOT.jar
,可以看到如下所示的目錄結構:
我們開啟 BOOT-INF
資料夾看到有 classes
和 lib
兩個資料夾,我們寫的程式碼的二進位制位元組碼就在 classes
目錄下,而 lib
下可以看到是我們專案依賴的所有 jar
包,最關鍵的,我們的專案是一個 web
專案,可以看到所依賴的內嵌的 tomcat
jar包,這是我們專案能獨立部署的關鍵:
其他目錄 META-INF
存放專案的 pom
檔案,org
目錄下存放的是 springframework
相關程式碼。
5、Hello World 探究
1、POM檔案
1、父專案
依賴路徑入下圖所示:
spring-boot-01-helloworld
的 pom:
<parent>
<artifactId>SpringBoot-In-Action</artifactId>
<groupId>com.demo.springboot</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
複製程式碼
它的父專案是 SpringBoot-In-Action
中的 pom:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<packaging>pom</packaging>
<groupId>com.demo.springboot</groupId>
<artifactId>SpringBoot-In-Action</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring-boot-01-helloworld</module>
</modules>
複製程式碼
而
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
複製程式碼
的父 pom 是 spring-boot-starter-parent-2.2.1.RELEASE.pom
:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
<artifactId>spring-boot-starter-parent</artifactId>
複製程式碼
而它的父 pom 是 spring-boot-dependencies-2.2.1.RELEASE.pom
:
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<packaging>pom</packaging>
在這個pom裡邊可以看到,它是用來真正管理Spring Boot應用裡面的所有依賴版本,定義了每一個依賴的版本;
複製程式碼
spring-boot-dependencies-2.x.RELEASE.pom
可以成稱為 Spring Boot的版本仲裁中心
;
以後我們匯入其他依賴預設是不需要寫版本;(當然沒有在dependencies裡面管理的依賴自然需要宣告版本號)
2、匯入的其他依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
複製程式碼
spring-boot-starter-web
:我們可以將其拆分為 spring-boot-starter
和 web
兩部分
- spring-boot-starter:spring-boot 場景啟動器;spring-boot-starter-web 幫我們匯入了 web 模組正常執行所依賴的元件。
我們按下 Ctrl
點選 spring-boot-starter-web 進入 spring-boot-starter-web-2.2.1.RELEASE.pom
,可以看到它匯入了很多 web 模組正常執行需要的相關依賴:
Spring Boot 為我們提供了簡化企業級開發的絕大多數場景的 starter pom(啟動器),只要引入了相應場景的 starter pom ,相關技術的絕大部分配置將會消除(即自動配置),從而簡化我們的開發。很多業務場景中我們就會使用到 Spring Boot 為我們自動配置的 bean,同時 Spring Boot 對這些場景依賴的 jar 也做了嚴格的測試與版本控制,我們可以不必擔心 jar 版本的適配問題。
在 Spring Boot 中還有很多這樣的啟動器,我們開啟官方檔案可以看到關於啟動器的描述:
Spring Boot 將所有的功能場景都抽取出來,做成一個個的 starters(啟動器),只需要在專案裡面引入這些starter 相關場景的所有依賴都會匯入進來。要用什麼功能就匯入什麼場景的啟動器。而我們一般的開發也是圍繞著這些 starter 來展開。
2、主程式類(主入口類)
/**
* @SpringBootApplication 來標註一個主程式類,說明這是一個Spring Boot應用,沒有這個註解專案無法啟動
*/
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
// Spring應用啟動起來
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}
複製程式碼
1、@SpringBootApplication
@SpringBootApplication: Spring Boot應用
標註在某個類上說明這個類是SpringBoot的主配置類,SpringBoot 就執行這個類的 main
方法來啟動 SpringBoot 應用;
進入 @SpringBootApplication
註解內部,可以看到它實際上是一個複合註解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}
),@Filter(
type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
複製程式碼
2、@SpringBootConfiguration
@SpringBootConfiguration:Spring Boot 的配置類;
作用
:標註在某個類上,表示這是一個Spring Boot 的配置類;
進入 @SpringBootConfiguration
內部可以看到,它被 Spring 的 @Configuration
註解所註解:
@Configuration
public @interface SpringBootConfiguration {
複製程式碼
@Configuration:配置類上來標註這個註解;配置類 === 配置檔案;
進入 @Configuration
內部可以看到,它被 Spring 的 @Component
註解所註解;配置類其實也是容器中的一個元件。
@Component
public @interface Configuration {
複製程式碼
3、@EnableAutoConfiguration
@EnableAutoConfiguration:開啟自動配置功能
以前在 Spring 中我們需要配置的東西,現在 Spring Boot 幫我們自動配置;@EnableAutoConfiguration告訴SpringBoot 開啟自動配置功能,這樣自動配置才能生效;
進入 @EnableAutoConfiguration
內部可以看到下面的程式碼:
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
複製程式碼
@AutoConfigurationPackage:自動配置包,將主配置類(@SpringBootApplication
標註的類)的所在包及下面所有子包裡面的所有元件掃描到 Spring 容器
進入 @Configuration
內部可以看到它被 @Import
註解所註解。Spring 的底層註解 @Import,給容器中匯入一個元件;匯入的元件由 Registrar.class 指定;
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
複製程式碼
進入 Registrar 靜態內部類可以看到如下程式碼:
static class Registrar implements ImportBeanDefinitionRegistrar,DeterminableImports {
Registrar() {
}
// 註冊一些 Bean 定義資訊,進行元件的匯入工作;metadata:註解的元資訊
public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry,(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
複製程式碼
我們在程式碼中打上斷點來看一下:
@Import({AutoConfigurationImportSelector.class}):給容器中匯入元件xxx
AutoConfigurationImportSelector:匯入哪些元件的選擇器;
我們進入 AutoConfigurationImportSelector
中可以看到如下程式碼:
// 將所有需要匯入的元件以全類名的方式返回;這些元件就會被新增到容器中
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata,attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata,attributes);
this.checkExcludedClasses(configurations,exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations,autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations,exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations,exclusions);
}
}
複製程式碼
將所有需要匯入的元件以全類名的方式返回;這些元件就會被新增到容器中。即AutoConfigurationImportSelector
會給容器中匯入非常多的自動配置類(xxxAutoConfiguration);就是給容器中匯入這個場景需要的所有元件,並配置好這些元件。
我們在程式碼中打斷點可以看到如下的結果:
有了自動配置類,就免去了我們手動編寫配置注入功能元件等的工作。
進入上面程式碼中的 getCandidateConfigurations
方法,可以看到如下程式碼:
// 獲取候選配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(),this.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(EnableAutoConfiguration.class,ClassLoader)
方法,我們在這個進入這個方法可以看下面的程式碼:
public static List<String> loadFactoryNames(Class<?> factoryType,@Nullable ClassLoader classLoader) {
// 獲取工廠名
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName,Collections.emptyList());
}
private static Map<String,List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String,String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
// 用類載入器從類路徑 "META-INF/spring.factories" 中獲取資源
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
// 從獲取的資源中拿出 properties 配置檔案,把獲取的資源當作 properties 配置檔案
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?,?> entry = (Entry)var6.next();
// 從 properties 檔案中拿到 factoryTypeName
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName,factoryImplementationName.trim());
}
}
}
cache.put(classLoader,result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]",var13);
}
}
}
複製程式碼
所以根據上面的分析我的得知:Spring Boot 在啟動的時候從類路徑下的 META-INF/spring.factories 中獲取 EnableAutoConfiguration 指定的值,將這些值作為自動配置類匯入到容器中,自動配置類就生效,幫我們進行自動配置工作
。以前我們在 Spring 中需要自己配置的東西,在 Spring Boot 中自動配置類都幫我們配了。
綜上,J2EE 的整體整合解決方案和自動配置都在 spring-boot-autoconfigure-2.2.1.RELEASE.jar (spring-boot-autoconfigure-x.x.x.RELEASE.jar )中:
6、使用 Spring Initializer 快速建立 Spring Boot專案
1、IDEA:使用 Spring Initializer快速建立專案
所有的 IDE 都支援使用 Spring 的專案建立嚮導快速建立一個 Spring Boot 專案,下面我們來看一下如何在 idea 中快速建立一個 Spring Boot 專案。
1、選擇File
→ New
→ Project
或是在一個已經存在的工程中選中工程名然後 New
→ Module
:
2、填寫基本資訊 GroupID
、ArtifactID
3、選擇我們需要依賴的模組
4、點選 Finish
,接下來嚮導會聯網建立 Spring Boot 專案
檢視預設生成的 Spring Boot 專案,我們可以看到:
- 主程式已經生成好了,我們只需要寫我們自己的邏輯
-
resources
資料夾中目錄結構:-
static
:儲存所有的靜態資源: js、css、 images等; -
templates
:儲存所有的模板頁面(Spring Boot 預設 jar 包使用嵌入式的 Tomcat,預設不支援 JSP 頁面),可以使用模板引擎(freemarker
、thymeleaf
等); -
application.properties
:Spring Boot 應用的配置檔案;可以修改一些預設設定,比如埠等
-
我們在 com.demo.spring.controller
包下新見一個 HelloController
來測試一下我們新建的專案
HelloController 程式碼如下:
//這個類的所有方法返回的資料直接寫給瀏覽器,(如果是物件轉為json資料)
//@ResponseBody
//@Controller
// 上面兩行一般多用在 Spring 專案中
@RestController // 在 Spring Boot 和 Spring4.2.x 之後使用這個來簡化開發,返回 RESTAPI 規範的介面
public class HelloController {
@RequestMapping("/hello")
public String hello(){
return "hello world quick!";
}
}
複製程式碼
在瀏覽器中訪問 http://localhost:8080/hello 可以得到下面的返回效果:
2、通過瀏覽器訪問https://start.spring.io/快速建立專案
執行完上面的操作後,瀏覽器會自動下載一個建立好的專案,我們使用idea開啟即可。
以上,就是我們 Spring Boot 快速入門的全部內容,更多詳細內容請檢視原始碼瞭解。