SpringBoot——banner解析

banner演示

SpringBoot項目啟動時會在控制臺打印一個默認(rèn)的LOGO满着,這個LOGO就是我們要講的banner。


制作自己的banner

實際上,Spring Boot 支持自定義 logo 的功能。讓我們來看看如何實現(xiàn)的。
只要你在 resources 目錄下放置名為 banner.txt谈喳、banner.gif 、banner.jpg 或 banner.png 的文件戈泼,Spring Boot 會自動加載婿禽,將其作為啟動時打印的 logo。

  • 對于文本文件大猛,Spring Boot 會將其直接輸出谈宛。
  • 對于圖像文件( banner.gif 、banner.jpg 或 banner.png )胎署,Spring Boot 會將圖像轉(zhuǎn)為 ASCII 字符吆录,然后輸出。

變量

banner.txt 文件中還可以使用變量來設(shè)置字體琼牧、顏色恢筝、版本號。

變量 描述
${application.version} MANIFEST.MF 中定義的版本巨坊。如:1.0
${application.formatted-version} MANIFEST.MF 中定義的版本撬槽,并添加一個 v 前綴。如:v1.0
${spring-boot.version} Spring Boot 版本趾撵。如:2.1.1.RELEASE.
${spring-boot.formatted-version} Spring Boot 版本侄柔,并添加一個 v 前綴。如:v2.1.1.RELEASE
${Ansi.NAME} (or ${AnsiColor.NAME}, ${AnsiBackground.NAME}, ${AnsiStyle.NAME}) ANSI 顏色占调、字體暂题。更多細(xì)節(jié),參考:AnsiPropertySource究珊。
${application.title} MANIFEST.MF 中定義的應(yīng)用名薪者。

示例:
在 Spring Boot 項目中的 resources 目錄下添加一個名為 banner.txt 的文件,內(nèi)容如下:

${AnsiColor.BRIGHT_YELLOW}${AnsiStyle.BOLD}
 ________  ___  ___  ________   ___       __   ___  ___
|\   ___ \|\  \|\  \|\   ___  \|\  \     |\  \|\  \|\  \
\ \  \_|\ \ \  \\\  \ \  \\ \  \ \  \    \ \  \ \  \\\  \
 \ \  \ \\ \ \  \\\  \ \  \\ \  \ \  \  __\ \  \ \  \\\  \
  \ \  \_\\ \ \  \\\  \ \  \\ \  \ \  \|\__\_\  \ \  \\\  \
   \ \_______\ \_______\ \__\\ \__\ \____________\ \_______\
    \|_______|\|_______|\|__| \|__|\|____________|\|_______|
${AnsiBackground.WHITE}${AnsiColor.RED}${AnsiStyle.UNDERLINE}
:: Spring Boot ::             (v${spring-boot.version})
:: Spring Boot Tutorial ::    (v1.0.0)

注:${} 設(shè)置字體顏色的變量之間不能換行或空格分隔剿涮,否則會導(dǎo)致除最后一個變量外言津,都不生效。

啟動應(yīng)用后取试,控制臺將打印如下 logo:


推薦兩個生成字符畫的網(wǎng)站悬槽,可以將生成的字符串放入這個banner.txt 文件:

配置

application.properties 中與 Banner 相關(guān)的配置:

# banner 模式。有三種模式:console/log/off
# console 打印到控制臺(通過 System.out)
# log - 打印到日志中
# off - 關(guān)閉打印
spring.main.banner-mode = off
# banner 文件編碼
spring.banner.charset = UTF-8
# banner 文本文件路徑
spring.banner.location = classpath:banner.txt
# banner 圖像文件路徑(可以選擇 png,jpg,gif 文件)
spring.banner.image.location = classpath:banner.gif
used).
# 圖像 banner 的寬度(字符數(shù))
spring.banner.image.width = 76
# 圖像 banner 的高度(字符數(shù))
spring.banner.image.height =
# 圖像 banner 的左邊界(字符數(shù))
spring.banner.image.margin = 2
# 是否將圖像轉(zhuǎn)為黑色控制臺主題
spring.banner.image.invert = false

編程方式設(shè)置banner的兜底邏輯

@SpringBootApplication
@MapperScan("com.yibo.source.code.mapper")//掃描Mapper接口
public class Application {

    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(Application.class);
        springApplication.setBanner(new ResourceBanner(new ClassPathResource("banner_bak.txt")));
        springApplication.run(args);
    }
}

banner_bak.txt

////////////////////////////////////////////////////////////////////
//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//             佛祖保佑          永無故障         永不修改             //
////////////////////////////////////////////////////////////////////

輸出:


注意:

默認(rèn)瞬浓,Spring Boot 會注冊一個 SpringBootBanner 的單例 Bean初婆,用來負(fù)責(zé)打印 Banner。

如果想完全個人定制 Banner,可以這么做:先實現(xiàn) org.springframework.boot.Banner#printBanner 接口來自己定制 Banner烟逊。在將這個 Banner 通過 SpringApplication.setBanner(…) 方法注入 Spring Boot。

banner獲取原理

banner的入口在SpringApplication的run方法中:

public class SpringApplication {
    public ConfigurableApplicationContext run(String... args) {
        ......
        Banner printedBanner = printBanner(environment);
    }
}

接著跟進(jìn)printBanner(environment);方法

private Banner printBanner(ConfigurableEnvironment environment) {
    //判斷是否關(guān)閉打印
    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);
}

接著跟進(jìn)bannerPrinter.print()方法

public Banner print(Environment environment, Class<?> sourceClass, Log logger) {
    Banner banner = getBanner(environment);
    try {
        logger.info(createStringFromBanner(banner, environment, sourceClass));
    }
    catch (UnsupportedEncodingException ex) {
        logger.warn("Failed to create String for banner", ex);
    }
    return new PrintedBanner(banner, sourceClass);
}

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

不管是輸出到日志還是打印到控制臺都會調(diào)用getBanner(Environment environment)方法

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;
}
getImageBanner(environment)
  • 可以通過spring.banner.image.location指定位置
  • 可以使用的圖片格式為"gif", "jpg", "png"
static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
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;
}
getTextBanner(environment)
  • 可以通過spring.banner.location指定位置
  • 默認(rèn)名字為banner.txt
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步驟

banner輸出原理

默認(rèn)輸出:
  • 先輸出banner指定內(nèi)容
  • 獲取version信息
  • 文本內(nèi)容前后對其
  • 文本內(nèi)容染色
  • 輸出文本內(nèi)容
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();
    }

}
文本輸出
  • 可以通過spring.banner.charset指定字符集
  • 獲取文本內(nèi)容
  • 替換占位符
  • 輸出文本內(nèi)容
private static class Banners implements Banner {

    private final List<Banner> banners = new ArrayList<>();

    public void addIfNotNull(Banner banner) {
        if (banner != null) {
            this.banners.add(banner);
        }
    }

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

}

public class ResourceBanner implements Banner {
    @Override
    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
        try {
            String banner = StreamUtils.copyToString(this.resource.getInputStream(),
                    environment.getProperty("spring.banner.charset", Charset.class, StandardCharsets.UTF_8));

            for (PropertyResolver resolver : getPropertyResolvers(environment, sourceClass)) {
                banner = resolver.resolvePlaceholders(banner);
            }
            out.println(banner);
        }
        catch (Exception ex) {
            logger.warn(
                    "Banner not printable: " + this.resource + " (" + ex.getClass() + ": '" + ex.getMessage() + "')",
                    ex);
        }
    }
}
圖片輸出
  • 可以通過spring.banner.image.*設(shè)置圖片屬性
  • 讀取圖片文件流
  • 輸出圖片內(nèi)容
private static class Banners implements Banner {

    private final List<Banner> banners = new ArrayList<>();

    public void addIfNotNull(Banner banner) {
        if (banner != null) {
            this.banners.add(banner);
        }
    }

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

}

public class ImageBanner implements Banner {

    private static final String PROPERTY_PREFIX = "spring.banner.image.";

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
        String headless = System.getProperty("java.awt.headless");
        try {
            System.setProperty("java.awt.headless", "true");
            printBanner(environment, out);
        }
        catch (Throwable ex) {
            logger.warn("Image banner not printable: " + this.image + " (" + ex.getClass() + ": '" + ex.getMessage()
                    + "')");
            logger.debug("Image banner printing failure", ex);
        }
        finally {
            if (headless == null) {
                System.clearProperty("java.awt.headless");
            }
            else {
                System.setProperty("java.awt.headless", headless);
            }
        }
    }

    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);
        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, out);
            sleep(frames[i].getDelayTime());
        }
    }
}

參考:
https://www.cnblogs.com/jingmoxukong/p/10159493.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铺根,一起剝皮案震驚了整個濱河市宪躯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌位迂,老刑警劉巖访雪,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異掂林,居然都是意外死亡臣缀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門泻帮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來精置,“玉大人,你說我怎么就攤上這事锣杂≈耄” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵元莫,是天一觀的道長赖阻。 經(jīng)常有香客問我,道長踱蠢,這世上最難降的妖魔是什么火欧? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮茎截,結(jié)果婚禮上苇侵,老公的妹妹穿的比我還像新娘。我一直安慰自己企锌,他們只是感情好衅檀,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著霎俩,像睡著了一般哀军。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上打却,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天杉适,我揣著相機與錄音,去河邊找鬼柳击。 笑死猿推,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蹬叭,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼藕咏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了秽五?” 一聲冷哼從身側(cè)響起孽查,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坦喘,沒想到半個月后盲再,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡瓣铣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年答朋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棠笑。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡梦碗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蓖救,到底是詐尸還是另有隱情叉弦,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布藻糖,位于F島的核電站淹冰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏巨柒。R本人自食惡果不足惜樱拴,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望洋满。 院中可真熱鬧晶乔,春花似錦、人聲如沸牺勾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驻民。三九已至翻具,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間回还,已是汗流浹背裆泳。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留柠硕,地道東北人工禾。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親闻葵。 傳聞我的和親對象是個殘疾皇子民泵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355