Spring Boot - 自定義 Banner 圖案

陳皮的JavaLib發表於2021-06-05

我是陳皮,一個在網際網路 Coding 的 ITer,微信搜尋「陳皮的JavaLib」第一時間閱讀最新文章,回覆【資料】,即可獲得我精心整理的技術資料,電子書籍,一線大廠面試資料和優秀簡歷模板。

前言

我們在啟動 Spring Boot 專案時,預設會在控制檯列印 Spring logo 和版本等資訊,如下:

這就是 Spring Boot 的 Banner 列印功能,其實我們可以自定義列印的 banner ,也可以禁用和啟用列印 banner 功能。在真實專案中,我們一般不會去自定義 banner 圖案,它其實就是專案啟動時列印圖案或者文字而已,沒實際意義。推薦在自己個人專案玩玩這個彩蛋即可,順便簡單瞭解下它內部實現原理。

比如,自定義一個 banner 之後,專案啟動控制檯列印如下所示:

實現原理

Spring Boot 有一個介面 org.springframework.boot.Banner 專門實現這個操作。要想自定義列印 banner ,只要自定義一個類實現這個介面,重寫 printBanner 方法進行列印即可。Springboot 專案啟動時,會建立我們的實現類物件,並呼叫物件的 printBanner 方法。

package org.springframework.boot;

import java.io.PrintStream;

import org.springframework.core.env.Environment;

/**
 * Interface class for writing a banner programmatically.
 * 用於以程式設計方式編寫 banner 的介面類
 * @since 1.2.0
 */
@FunctionalInterface
public interface Banner {

	/**
	 * Print the banner to the specified print stream.
	 * 將 banner 列印到指定的列印流。
	 * @param environment the spring environment
	 * @param sourceClass the source class for the application
	 * @param out the output print stream
	 */
	void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);

	// 用於配置Banner的的列舉值
	enum Mode {
		// 關閉 banner 列印
		OFF,
        // 列印 banner 到 控制檯
		CONSOLE,
		// 列印 banner 到日誌檔案
		LOG
	}
}

預設 Banner 實現類

Springboot 已經有幾個自帶的 Banner 實現類,Springboot 啟動時會根據條件選擇不同的 Banner 實現類進行列印 banner 資訊。主要是 ImageBannerResourceBannerSpringBootBanner 這三個實現類。

  1. 專案啟動時,會判斷是否某些條件成立(專案中是否存在 banner 檔案),成立則建立 ImageBannerResourceBanner 類物件,並且使用它們來列印 banner。
  2. 如果不成立檢查是否存在我們自定義的 Banner 實現類 fallbackBanner,如果存在則使用它來列印 banner 圖案。
  3. 否則,則使用預設的 SpringBootBanner 實現類來列印 banner,也就是我們經常看到 Spring 圖案。
// 獲取可用的 Banner 實現類
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;
	}
	// SpringBootBanner 實現類
	return DEFAULT_BANNER;
}

ImageBanner

org.springframework.boot.ImageBanner 類是專門載入和列印圖片 banner 的。它檢查配置檔案 application.proeprties 是否有配置的 spring.banner.image.location 變數的值,這個值可用來指定要載入的圖片,如果存在則構建 ImageBanner 物件。如果沒有配置變數,則還會檢查 Classpath 下是否存在以 banner 開頭,以 .gif.jpg.png 結尾的圖片檔案,如果有也會構建 ImageBanner 物件。

class SpringApplicationBannerPrinter {

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

	static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };

	// 獲取 ImageBanner 物件
	private Banner getImageBanner(Environment environment) {
	    // 載入 spring.banner.image.location 指定的檔案,檔案存在則構建 ImageBanner 物件
		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;
		}
		// 查詢 banner.gif,banner.jpg,banner.png 檔案
		for (String ext : IMAGE_EXTENSION) {
			Resource resource = this.resourceLoader.getResource("banner." + ext);
			if (resource.exists()) {
				return new ImageBanner(resource);
			}
		}
		return null;
	}
}

ResourceBanner

org.springframework.boot.ResourceBanner 類是專門載入和列印字元 banner 的。它檢查配置檔案 application.proeprties 是否有配置的 spring.banner.location 變數的值,這個值可用來指定要載入的檔案,如果存在則構建 ResourceBanner 物件。如果沒有配置變數,則還會檢查資源路徑下是否存在 banner.txt 檔案,如果存在也會構建 ResourceBanner 物件。

class SpringApplicationBannerPrinter {

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

	static final String DEFAULT_BANNER_LOCATION = "banner.txt";

	// 獲取 ResourceBanner 物件
	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,我們一般在專案的 resources 資源目錄下建立 banner.txt 檔案,然後在裡面填入我們想要的列印的文字內容即可。例如我在 banner.txt 檔案中填充了 Chen Pi 內容,然後啟動專案。

SpringBootBanner

如果專案沒有設定以上兩種自定義的 banner(ImageBanner 和 ResourceBanner),則預設情況下,會使用 SpringBootBanner 實現類列印 banner ,也就是我們啟動 Springboot 專案時在控制檯看到的列印 Spring 圖案。原始碼如下:

package org.springframework.boot;

import java.io.PrintStream;

import org.springframework.boot.ansi.AnsiColor;
import org.springframework.boot.ansi.AnsiOutput;
import org.springframework.boot.ansi.AnsiStyle;
import org.springframework.core.env.Environment;

/**
 * Default Banner implementation which writes the 'Spring' banner.
 */
class SpringBootBanner implements Banner {
	// 這個就是我們啟動 Springboot 專案時在控制檯看到的圖案
	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 類,重寫列印方法,實現自定義 banner 列印功能。

package com.chenpi;

import java.io.PrintStream;
import org.springframework.boot.Banner;
import org.springframework.core.env.Environment;

/**
 * @Description 自定義 Banner 實現類
 * @Author Mr.nobody
 * @Date 2021/6/4
 * @Version 1.0
 */
public class MyBanner implements Banner {

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {

      String banner = "       .__                           .__ \n"
          + "  ____ |  |__   ____   ____   ______ |__|\n"
          + "_/ ___\\|  |  \\_/ __ \\ /    \\  \\____ \\|  |\n"
          + "\\  \\___|   Y  \\  ___/|   |  \\ |  |_> >  |\n"
          + " \\___  >___|  /\\___  >___|  / |   __/|__|\n"
          + "     \\/     \\/     \\/     \\/  |__|       ";

      out.println(banner);
    }
}

建立自定義的 Banner 實現類物件,設定到 SpringApplication 類物件的 banner 屬性,最終這個屬性的值會會被賦值到 SpringApplicationBannerPrinter 物件的 fallbackBanner 屬性中,感興趣的可以啟動 debug 跟蹤下。

package com.chenpi;

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

@SpringBootApplication
public class SpringBootBannerApplication {
	public static void main(String[] args) {
		SpringApplication springApplication = new SpringApplication(SpringBootBannerApplication.class);
		// 設定自定義 Banner
		springApplication.setBanner(new MyBanner());
		// 啟動 SpringBoot
		springApplication.run(args);
	}
}

文章一開始的佛祖圖形,你會發現是翠綠色的。其實 Springboot 支援我們修改 banner 的顏色,字型斜體,粗體等樣式。SpringBoot 為我們提供了三個列舉類來設定這些樣式。

  1. AnsiColor:設定字元的前景色;參考 org.springframework.boot.ansi.AnsiColor 列舉類。
  2. AnsiBackground:設定字元的背景色;參考 org.springframework.boot.ansi.AnsiBackground 列舉類。
  3. AnsiStyle:設定字元的加粗、斜體、下劃線等等;參考 org.springframework.boot.ansi.AnsiStyle 列舉類。

而且,在 banner.txt 檔案中還可以引用一些全域性變數,例如:

  1. ${spring-boot.version}:Spring Boot 版本號;
  2. ${spring-boot.formatted-version}:格式化後的 Spring Boot 版本號資訊。
  3. ${application.version}:MANIFEST.MF 檔案中的版本號;
  4. ${application.formatted-version}:格式化後的 MANIFEST.MF 檔案中的版本號資訊;

不僅如此,還可以引用我們在配置檔案 application.properties 中定義的變數,例如在配置檔案中定義瞭如下變數:

application.auth=chenpi

定義的 banner.txt 檔案內容如下:

${AnsiColor.BRIGHT_GREEN}
////////////////////////////////////////////////////////////////////
//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//            佛祖保佑       永不當機     永無BUG                     //
////////////////////////////////////////////////////////////////////
${AnsiColor.BRIGHT_CYAN}
Application Version: ${application.version}${application.formatted-version}
Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}

By -- ${application.auth}

啟動專案,會在控制檯列印的 banner 如下:

在 Banner 介面中有定義一個列舉類,這個列舉定義了配置 Banner 的可能列舉值,如下:

@FunctionalInterface
public interface Banner {

	// 用於配置Banner的的列舉值
	enum Mode {
		// 關閉 banner 列印
		OFF,
        // 列印 banner 到 控制檯
		CONSOLE,
		// 列印 banner 到日誌檔案
		LOG
	}
}

所以我們可以選擇關閉 banner,banner 列印到控制檯還是日誌檔案,如下:

package com.chenpi;

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

@SpringBootApplication
public class SpringBootBannerApplication {
    public static void main(String[] args) {
         SpringApplication springApplication = new SpringApplication(SpringBootBannerApplication.class);
         // 關閉 banner
         springApplication.setBannerMode(Mode.OFF);
         // 啟動 SpringBoot
         springApplication.run(args);
    }
}

也可以配置檔案中設定此值,如下

spring.main.banner-mode=off

如果啟動類跟配置檔案中都配置了對banner開關的設定,配置檔案中設定的banner開關會優先於啟動類中設定的開關。

可能有人會問佛祖的圖案怎麼編輯出來的,其實網上有很多工具可以根據我們輸入的內容或者圖片,個性化製作ASCII字元和圖案,推薦網址如下:

相關文章