springboot banner配置

springboot 啓動時是如何打印banner的, 以及如何配置一個自己的banner

入口方法

private Banner printBanner(ConfigurableEnvironment environment) {
  if (this.bannerMode == Banner.Mode.OFF) {
    return null;
  }
  ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
      : new DefaultResourceLoader(getClassLoader());
  SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
  if (this.bannerMode == Mode.LOG) {
    return bannerPrinter.print(environment, this.mainApplicationClass, logger);
  }
  return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

printBanner是banner打印的入口方法, 該方法根據不同的Banner.Mode爲枚舉類型, 值有OFF, CONSOLE, LOG, 分別對應不打印, 控制檯輸出, 輸出到日誌文件.
對於CONSOLELOG類型需要通過ResourceLoader resourceLoader加載banner資源. banner打印需要通過SpringApplicationBannerPrinter類中的print方法(重載)進行處理, 對於CONSOLE類型, 第三個參數爲PrintStream類型, 傳入System.out進行處理, LOG類型則通過日誌對象logger(其定義爲private static final Log logger = LogFactory.getLog(SpringApplication.class);)進行處理. 不過兩個重載方法邏輯是一致的.

print

CONSOLE類型爲例, 其代碼爲:

Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
  Banner banner = getBanner(environment);
  banner.printBanner(environment, sourceClass, out);
  return new PrintedBanner(banner, sourceClass);
}

首先要獲取一個Banner對象(函數式), 調用打印方法printBanner輸出banner, 打印完畢, 構造一個PrintedBanner繼承於Banner返回.

下面具體看一下getBanner方法:

private Banner getBanner(Environment environment) {
  Banners banners = new Banners();
  banners.addIfNotNull(getImageBanner(environment));
  banners.addIfNotNull(getTextBanner(environment));
  if (banners.hasAtLeastOneBanner()) {
    return banners;
  }
  if (this.fallbackBanner != null) {
    return this.fallbackBanner;
  }
  return DEFAULT_BANNER;
}

Banners類是Banner接口的實現, 內部維護一個private final List<Banner> banners = new ArrayList<>();用來存放所有的Banner, 該類也重寫printBanner爲:

@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
  for (Banner banner : this.banners) {
    banner.printBanner(environment, sourceClass, out);
  }
}

即: 將banners中的banner對象全部打印. 而banners中的banner來自於getImageBannergetTextBanner加載的資源. 下面分別來看一下這兩個方法.

getImageBanner


static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";

private Banner getImageBanner(Environment environment) {
  String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
  if (StringUtils.hasLength(location)) {
    Resource resource = this.resourceLoader.getResource(location);
    return resource.exists() ? new ImageBanner(resource) : null;
  }
  for (String ext : IMAGE_EXTENSION) {
    Resource resource = this.resourceLoader.getResource("banner." + ext);
    if (resource.exists()) {
      return new ImageBanner(resource);
    }
  }
  return null;
}

BANNER_IMAGE_LOCATION_PROPERTY配置用來表示圖片類型banner資源的加載位置, 之後resourceLoader會生成一個對應位置資源的操作對象Resource resource.
如果該位置下存在資源則返回ImageBanner類型的對象, 該類也是Banner的一個實現.

如果沒有定義BANNER_IMAGE_LOCATION_PROPERTYresourceLoader加載以banner爲前綴, "gif", "jpg", "png"等格式的文件. 如果存在則直接返回一個ImageBanner類型的對象.

注意如果不存在由於addIfNotNull會判斷Banner是否爲null, 是則不會將其放入banners中.

getTextBanner


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

private Banner getTextBanner(Environment environment) {
  String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
  Resource resource = this.resourceLoader.getResource(location);
  if (resource.exists()) {
    return new ResourceBanner(resource);
  }
  return null;
}

此時會從BANNER_LOCATION_PROPERTY出加載banner.txt文件, 並返回ResourceBanner對象.

對於一個默認的系統配置, 無法加載圖片和文本banner此時直接返回一個默認的DEFAULT_BANNER, 其爲SpringBootBanner類型. 該類定義如下, 可以看到BANNER爲系統的默認
輸出.

class SpringBootBanner implements Banner {
private static final String[] BANNER = { "", "  .   ____          _            __ _ _",
    " /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
    " \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )", "  '  |____| .__|_| |_|_| |_\\__, | / / / /",
    " =========|_|==============|___/=/_/_/_/" };

private static final String SPRING_BOOT = " :: Spring Boot :: ";

private static final int STRAP_LINE_SIZE = 42;

@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
  for (String line : BANNER) {
    printStream.println(line);
  }
  String version = SpringBootVersion.getVersion();
  version = (version != null) ? " (v" + version + ")" : "";
  StringBuilder padding = new StringBuilder();
  while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
    padding.append(" ");
  }

  printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
      AnsiStyle.FAINT, version));
  printStream.println();
}

}

自定義banner

從上面的分析我們可知, banner有三種形式:

  1. 圖片ImageBanner, banner是在spring.banner.image.location位置下的, 或者是默認路徑下的banner.png|gif|jpg文件
  2. 文本ResourceBanner, banner是在spring.banner.location位置下的, 或者是默認路徑下的banner.txt文件.
  3. 系統默認SpringBootBanner, banner樣式是寫在一個String數組中的

對於圖片, 在資源目錄下添加一個banner.png文件, 運行:
2020-05-10-14-04-01

另外springboot還提供了圖片打印的一些參數控制, 比如長度,寬度,bitdepth(值爲4, 8, 分別只16色和256色), pixelmode(值爲block, 默認text, 分別對應方塊輸出和字符畫)
一通配置之後的打印banner.
2020-05-10-14-10-34

那麼, 這些參數又是在什麼時候生效的呢? 這就要看ImageBanner中重寫的printBanner

下面的方法是核心方法, 就不分析了.

private void printBanner(Environment environment, PrintStream out) throws IOException {
  int width = getProperty(environment, "width", Integer.class, 76);
  int height = getProperty(environment, "height", Integer.class, 0);
  int margin = getProperty(environment, "margin", Integer.class, 2);
  boolean invert = getProperty(environment, "invert", Boolean.class, false);
  BitDepth bitDepth = getBitDepthProperty(environment);
  PixelMode pixelMode = getPixelModeProperty(environment);
  Frame[] frames = readFrames(width, height);
  for (int i = 0; i < frames.length; i++) {
    if (i > 0) {
      resetCursor(frames[i - 1].getImage(), out);
    }
    printBanner(frames[i].getImage(), margin, invert, bitDepth, pixelMode, out);
    sleep(frames[i].getDelayTime());
  }
}

對於文本, 在資源目錄下添加banner.txt文件. 運行:
2020-05-10-13-52-09

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