springboot 源碼解析(4)打印的banner原理以及如何修改

繼續上一節

發現兩個參數的構造方法繼續調用了四個參數的構造參數

這個沒有什麼神奇的操作。簡單的賦值

我們繼續返回看那個bind方法

我們先看看

Bindable.ofInstance(this)幹啥了

instance是SpringApplication本身 type取到類,這裏有個of(type)我們瞧瞧他又是幹啥的

先看ResolvableType.forClass(type)鬧啥了

new了一個ResolvableType對象。感覺沒有那麼簡單。我們進去看看他怎麼構建這個對象的

如果這個類是空的。給resolved賦值Object.class其他的都很簡單。我們繼續返回看

of(ResolvableType.forClass(type));

 

我們看看box又在玩什麼花樣

Class<?> resolved = type.resolve();獲取到類
if (resolved != null && resolved.isPrimitive())   如果不爲空並且他是原始類型
isPrimitive 類的內部方法,是否是原始類型
Object array = Array.newInstance(resolved, 1);創建一個數組
Class<?> wrapperType = Array.get(array, 0).getClass();獲取數組的第一個類
return ResolvableType.forClass(wrapperType);返回包裝了原始類型的ResolvableType對象
if (resolved != null && resolved.isArray())  如果不爲空並且是個數組返回一個數組的ResolvableType對象 否則直接返回
return type;

我們繼續返回看

new Bindable<>(type, boxedType, null, NO_ANNOTATIONS);創建了一個沒有註解的bindable對象

然後返回繼續看

我們跟進去看

調用了以後還是返回了一個Bindable對象

我們繼續看

我們跟進去看bind

我們先看ConfigurationPropertyName.of(name)  此時name是“spring.main”

我們繼續看這個of方法做了什麼

static ConfigurationPropertyName of(CharSequence name, boolean returnNullIfInvalid) {
   if (name == null) {
      Assert.isTrue(returnNullIfInvalid, "Name must not be null");
      return null;
   }
   if (name.length() == 0) {
      return EMPTY;
   }
   if (name.charAt(0) == '.' || name.charAt(name.length() - 1) == '.') {
      if (returnNullIfInvalid) {
         return null;
      }
      throw new InvalidConfigurationPropertyNameException(name,
            Collections.singletonList('.'));
   }
   Elements elements = new ElementsParser(name, '.').parse();
   for (int i = 0; i < elements.getSize(); i++) {
      if (elements.getType(i) == ElementType.NON_UNIFORM) {
         if (returnNullIfInvalid) {
            return null;
         }
         throw new InvalidConfigurationPropertyNameException(name,
               getInvalidChars(elements, i));
      }
   }
   return new ConfigurationPropertyName(elements);
}

上面的校驗不多解釋。都能看懂。方法返回了一個配置文件的對象

我們返回去看bind方法

BindHandler.DEFAULT

這裏用到一個java8的新特性接口DEFAULT  接口中可以有方法的實現,必須用DEFAULT和static修飾,此方法可以被重寫

Context context = new Context();終於看到一個大家非常熟悉的東西了。new了一個context,這裏就用了一個new,但是之前的分析感覺他並不只是new一個對象。我們跟進去看

真是玄機無處不在。不過這個我們之前分析過了。這裏就不在分析了。他只是給賦值而已

我們繼續往下看

T bound = bind(name, target, handler, context, false);

我們看看這個bind有搞什麼名堂,我點!點進去看

剛進來就clear,剛進家就打掃下家?這肯定是消滅家裏藏人的證據!

context.clearConfigurationProperty();

我們繼續往下瞅,嗯?handler剛纔不是個接口嗎。怎麼直接調用方法了。剛纔咱們說過了

default修飾的方法是可以再接口中實現的。這個好像叫什麼默認實現?

 

我們繼續往下看

Object bound = bindObject(name, target, handler, context,
      allowRecursiveBinding);

大家有點耐心我們繼續往下看

ConfigurationProperty property = findProperty(name, context);這個不多解釋了。從content中獲取配置信息

這裏返回了null並沒有找到,這不是關鍵的。我們繼續往下看

 handleBindResult(name, target, handler, context, bound);

這個裏面邏輯也是比較簡單。我們先不做贅述了。他返回的依然是null

主要的是我們看這個。他返回了一個BindResult

我們再回到

org.springframework.boot.SpringApplication#prepareEnvironment

if (!this.isCustomEnvironment) {
   environment = new EnvironmentConverter(getClassLoader())
         .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}

創建了一個標準的環境StandardEnvironment

然後我們再返回到

org.springframework.boot.SpringApplication#run(java.lang.String...)

看到

configureIgnoreBeanInfo(environment);

這個的大概意思是獲取配置

spring.beaninfo.ignore  生成bean的模式默認是單例模式,讀取配置文件讀取不到就設置爲單例模式。然後設置到系統配置中
Banner printedBanner = printBanner(environment);

顧名思義打印banner,我們看看他有哪些實現

this.bannerMode == Banner.Mode.OFF是否關閉打印banner
ResourceLoader resourceLoader = (this.resourceLoader != null)
      ? this.resourceLoader : new DefaultResourceLoader(getClassLoader());  獲取默認資源加載器
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
      resourceLoader, this.banner);

構建一個spring的banner

if (this.bannerMode == Mode.LOG) {
   return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}

判斷是否打印到log的

我們進入print看

我們看getBanner

Banners banners = new Banners();創建一個banners的對象
banners.addIfNotNull(getImageBanner(environment));我們跟進去看獲取圖片banner

static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";我們發現她是查找配置文件是否配置了這個屬性
if (StringUtils.hasLength(location)) {
   Resource resource = this.resourceLoader.getResource(location);
   return resource.exists() ? new ImageBanner(resource) : null;
}

如果存在配置 並且文件存在獲取這個圖片的banner返回

如果沒有配置往下繼續

for (String ext : IMAGE_EXTENSION) {
   Resource resource = this.resourceLoader.getResource("banner." + ext);
   if (resource.exists()) {
      return new ImageBanner(resource);
   }
}
static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };他開始尋找能否找到resource下面banner+後綴的的圖片

如果存在就返回。也就是說如果我們想替換banner可以直接在resource下面放一個banner.png|jpg|gif的文件就可以了

我們再看文本格式的banner

banners.addIfNotNull(getTextBanner(environment));

static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
static final String DEFAULT_BANNER_LOCATION = "banner.txt";

這個原理一樣首先查找配置,如果配置不存就查找banner.txt

if (banners.hasAtLeastOneBanner()) {
   return banners;
}

如果包含至少一個banner返回

如果不存在

private static final Banner DEFAULT_BANNER = new SpringBootBanner();

這個熟悉了吧

banner.printBanner(environment, sourceClass, out);打印banner

如果存在兩個兩個都會打印。不信你看

好了先到這裏。打印banner已經分析完畢。下一步我們開始

createApplicationContext創建應用上下文

 

 

 

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