天天看點

基建立SpringBoot項目并進行入門分析

建立SpringBoot項目并進行入門分析

文章目錄

  • ​​基建立SpringBoot項目并進行入門分析​​
  • ​​SpringBoot 項目建立​​
  • ​​建立Module​​
  • ​​項目結構分析​​
  • ​​SpringBoot 項目啟動分析​​
  • ​​啟動入口​​
  • ​​啟動過程概要分析​​
  • ​​SpringBoot 快速入門分析​​
  • ​​業務描述​​
  • ​​API設計分析​​
  • ​​代碼編寫及運作​​
  • ​​運作過程中的BUG分析​​
  • ​​SpringBoot 項目中的對象特性分析​​
  • ​​準備工作​​
  • ​​延遲加載​​
  • ​​對象作用域分析​​
  • ​​對象生命周期方法​​
  • ​​SpringBoot 項目中的依賴注入過程分析​​
  • ​​準備工作​​
  • ​​案例設計及分析​​
  • ​​代碼編寫及測試分析​​
  • ​​編寫及測試過程中的BUG分析​​
  • ​​總結(Summary)​​
  • ​​結語​​
基建立SpringBoot項目并進行入門分析

SpringBoot 項目建立

建立Module

基于IDEA建立項目Module,子產品名為04-springboot-start,組id和包名為com.cy,如圖所示:

基建立SpringBoot項目并進行入門分析

填寫module資訊,如圖所示:

基建立SpringBoot項目并進行入門分析

選擇項目module版本,暫時不需要自己手動添加任何依賴,如圖所示:

基建立SpringBoot項目并進行入門分析

填寫Module名稱,完成module建立,如圖所示

基建立SpringBoot項目并進行入門分析

項目結構分析

項目Module建立好以後,其代碼結構分析,如圖所示:

基建立SpringBoot項目并進行入門分析

SpringBoot 項目啟動分析

啟動入口

SpringBoot 工程中由SpringBootApplication注解描述的類為啟動入口類,例如:

package com.cy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {//Application.class
  public static void main(String[] args) {//Main Thread
    SpringApplication.run(Application.class, args);
  }
}      

啟動過程概要分析

SpringBoot工程啟動時其簡易初始化過程,如圖所示:

基建立SpringBoot項目并進行入門分析

在啟動過程中底層做了哪些事情,大緻描述如下:

1)基于配置加載類(通過ClassLoader将指定位置的類讀到記憶體->底層通過線程調用IO從磁盤讀取到記憶體)。

2)對類進行分析(建立位元組碼對象-Class類型,通過反射擷取器配置資訊)。

3)對于指定配置(例如由spring特定注解描述)的對象存儲其配置資訊(借助BeanDefinition對象存儲)。

4)基于BeanDefinition對象中class的配置建構類的執行個體(Bean對象),并進行bean對象的管理(可能會存儲到bean池)。

SpringBoot 快速入門分析

業務描述

在項目Module中定義一個類,類名為DefaultCache,然後将此類對象交給Spring建立并管理。最後通過單元測試對類的執行個體進行分析。

API設計分析

基于業務描述,進行API及關系設計,如圖所示:

基建立SpringBoot項目并進行入門分析

代碼編寫及運作

基于業務及API設計,進行代碼編寫,其過程如下:

第一步:定義DefaultCache類

package com.oak.libin.common.cache;
import org.springframework.stereotype.Component;
/**
 * @Component 注解描述的類,表示此類交給Spring架構管理。
 */
@Component
public class DefaultCache {
}      

第二步:定義DefaultCacheTests單元測試類

package com.oak.libin.common.cache;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@SpringBootTest
public class DefaultCacheTests {
    /**
 * @Autowired 注解描述的屬性由spring架構按照一定規則為其注入值(指派)
 * 指派過程是怎樣的?
 *  1)依賴查找?(請問查找規則是什麼?)
 *  2)依賴注入?(需要借助什麼技術?)
 */ @Autowired
 private DefaultCache defaultCache;
    @Test
 void testDefaultCache(){
        System.out.println(defaultCache.toString());
        //FAQ? defaultCache變量引用的對象是由誰建立的,存儲 到了哪裡?bean pool
 }
}      

第三步:運作單元測試類進行應用分析

啟動運作單元測試方法,檢測其輸出結果,基于結果分析:

1)SpringBoot項目中Bean對象的建構。

2)SpringBoot項目中Bean對象的擷取。

運作過程中的BUG分析

  • Bean類型找不到,如圖所示:
基建立SpringBoot項目并進行入門分析
  • 空指針異常(NullPointerExcetpion-NPE),如圖所示:
基建立SpringBoot項目并進行入門分析
  • 啟動類找不到,如圖所示:
基建立SpringBoot項目并進行入門分析
  • 啟動類有多個,如圖所示:
基建立SpringBoot項目并進行入門分析
  • NoSuchBeanDefinition異常,如圖所示:
基建立SpringBoot項目并進行入門分析
  • 單元測試類中的方法添加了參數,如圖所示:
基建立SpringBoot項目并進行入門分析

SpringBoot 項目中的對象特性分析

準備工作

第一步:建立項目Module,例如名字為05-springboot-features,如圖所示:

基建立SpringBoot項目并進行入門分析

第二步:添加業務類ObjectPool,代碼如下:

package com.oak.libin.common.pool;
@Component
public class ObjectPool{//假設此對象為一個對象池
    public ObjectPool(){//假設運作項目啟動類,此構造方法執行了,說明此類對象建構了。
      Systemd.out.println("ObjectPool()")
    }
}      

思考:一般池對象有什麼特點?

1)在JVM記憶體會開辟一塊相對比較大的空間。

2)在這塊空間中存儲一些對象(思考基于什麼存儲結構進行存儲-數組,連結清單,散清單)。

3)基于“享元模式”設計思想,實作記憶體中對象的可重用性。

第三步:定義單元測試,代碼如下:

package com.oak.libin.pool;
import com.oak.libin.common.pool.ObjectPool;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class ObjectPoolTests {

    @Autowired
    private ObjectPool objectPool01;
    @Autowired
    private ObjectPool objectPool02;
    @Test
    void testObjectPool01(){
       System.out.println(objectPool01==objectPool02);
    }
}      

延遲加載

現在思考一個問題,對于ObjectPool這個類,假如項目啟動以後,暫時不會用到這個池對象,是否有必要對其進行建立(預設是會建立的)?我們知道沒必要,因為占用記憶體。那如何在啟動時不建立此類對象呢?借助Spring架構提供的延遲加載特性進行實作。例如,我們可以在需要延遲加載的類上使用@Lazy注解進行描述,代碼如下:

package com.oak.libin.common.pool;
@Lazy
@Component
public class ObjectPool{//假設此對象為一個對象池
    public ObjectPool(){//假設運作項目啟動類,此構造方法執行了,說明此類對象建構了。
      Systemd.out.println("ObjectPool()")
    }
}      

此時,我們再去運作運作啟動類,檢測ObjectPool對象是否建立了,假如沒有建立,說明延遲加載生效了。此時,我們總結一下,什麼對象适合使用延遲加載特性呢?大對象,稀少用(項目啟動以後,暫時用不到)的對象。

注意:延遲加載并不是延遲對類進行加載,而是在啟動時,暫時不建立類的執行個體。假如想看一下記憶體中的類是否被加載了,可以通過JVM參數進行檢測,參數為-XX:+TraceClassLoading。

對象作用域分析

在實際的項目中記憶體中的對象有一些可能要反複應用很多次,有一些可能用完以後再也不用了或者說應用次數很少了。對于經常要重複使用的對象我可考慮存儲到池中(例如交給spring架構進行管理),應用次數很少的對象那就沒必要放到池中了,用完以後讓它自己銷毀就可以了。在Spring項目工程中為了對這樣的對象進行設計和管理,提供了作用域特性的支援,具體應用:

package com.oak.libin.common.pool;
@Scope("singleton")
@Lazy
@Component
public class ObjectPool{//假設此對象為一個對象池
    public ObjectPool(){//假設運作項目啟動類,此構造方法執行了,說明此類對象建構了。
      Systemd.out.println("ObjectPool()")
    }
}      

其中,在上面的代碼中,我們使用了@Scope注解對類進行描述,用于指定類的執行個體作用域。不寫@Scope預設就是單例(singleton)作用域,這個作用域會配合延遲加載(@Lazy)特性使用,表示此類的執行個體在需要時可以建立一份并且将其存儲到spring的容器中(Bean池),需要的時候從池中取,以實作對象的可重用。假如一些對象應用次數非常少,可以考慮不放入池中,進而使用@Scope(“prototype”)作用域對類進行描述,讓此類的對象何時需要何時建立,用完以後,當此對象不可達了,則可以直接被GC系統銷毀。

對象生命周期方法

程式中的每個對象都有生命周期,對象建立,初始化,應用,銷毀的這個過程稱之為對象的生命周期。在對象建立以後要初始化,應用完成以後要銷毀時執行的一些方法,我們可以稱之為生命周期方法。但不見得每個對象都會定義生命周期方法。在實際項目中往往一些池對象通常會定義這樣的一些生命周期方法(例如連接配接池)。那這樣的方法在spring工程中如何進行辨別呢?通常要借助@PostConstruct和@PreDestroy注解對特定方法進行描述,例如:

package com.oak.libin.common.pool;
@Scope("singleton")
@Lazy
@Component
public class ObjectPool{//假設此對象為一個對象池
    public ObjectPool(){
      Systemd.out.println("ObjectPool()")
    }
    @PostConstruct
    public void init(){
       System.out.println("init()");
    }
    @PreDestroy
    public void destory(){
     System.out.println("destory()");
    }
}      

其中:

1)@PostConstruct 注解描述的方法為生命周期初始化方法,在對象建構以後執行.

2)@PreDestroy 注解描述的方法為生命周期銷毀方法,此方法所在的對象,假如存儲到了spring容器,那這個對象在從spring容器移除之前會先執行這個生命周期銷毀方法(prototype作用域對象不執行此方法).

SpringBoot 項目中的依賴注入過程分析

在SpringBoot工程中,假如類與類之間存在着一定的依賴關系,Spring是如何進行依賴注入的呢,現在我們就通過一個案例做一個分析。

準備工作

第一步:建立一個項目module,如圖所示:

基建立SpringBoot項目并進行入門分析

第二步:啟動運作項目,檢測是否能成功啟動

案例設計及分析

為了更好了解spring架構的底層注入機制,現在進行案例API設計,如圖所示:

基建立SpringBoot項目并進行入門分析

在這個案例中單元測試類CacheTests中定義一個Cache接口類型的屬性,然後由Spring架構完成對cache類型屬性值的注入。

代碼編寫及測試分析

第一步:定義Cache接口,代碼如下:

package com.oak.libin.common.cache;
public interface Cache {
 
}      

第二步:定義Cache接口實作類SoftCache,代碼如下:

package com.oak.libin.common.cache;
 
@Component
public class SoftCache implements Cache{

}      

第三步:定義Cache接口實作類WeakCache,代碼如下:

package com.oak.libin.common.cache;
 
@Component
public class WeakCache implements Cache{

}      

第四步:定義CacheTests單元測試類,代碼如下:

package com.oak.libin.common.cache;
import org.junit.jupiter.api.Test;

@SpringBootTest    
public class CacheTests {
    @Autowired
    @Qualifier("softCache")
    private Cache cache;
    
    @Test
    public void testCache() {
        System.out.println(cache);
    }
}      

其中,@Autowired由spring架構定義,用于描述類中屬性或相關方法(例如構造方法)。Spring架構在項目運作時假如發現由他管理的Bean對象中有使用@Autowired注解描述的屬性或方法,可以按照指定規則為屬性指派(DI)。其基本規則是:首先要檢測容器中是否有與屬性或方法參數類型相比對的對象,假如有并且隻有一個則直接注入。其次,假如檢測到有多個,還會按照@Autowired描述的屬性或方法參數名查找是否有名字比對的對象,有則直接注入,沒有則抛出異常。最後,假如我們有明确要求,必須要注入類型為指定類型,名字為指定名字的對象還可以使用@Qualifier注解對其屬性或參數進行描述(此注解必須配合@Autowired注解使用)。

第五步:運作CacheTests檢測輸出結果,基于結果了解其注入規則。

編寫及測試過程中的BUG分析

  • 依賴注入異常,如圖所示:

總結(Summary)