自定义Springboot启动Banner


自定义Springboot启动Banner

相信使用过Springboot的朋友们肯定对于这个图不陌生,这就是Springboot服务启动时候的默认的启动Banner。

作为爱倒腾的程序员,肯定不满足一成不变的这个图,自定义是必须的,例如打印个“LOVE”,或者把你女朋友的照片打印出来。于是乎,就要弄懂Banner打印的原理以及如何自定义打印的Banner图。

一、自定义Banner

先来看看效果打印一个"I LOVE JAVA"的ASCII图形的效果,如下图

自定义Springboot启动Banner

在Springboot中打印实现这样子的一个效果非常简单,只需要在类路径下(Springboot的resources文件夹下)放置banner.txt文件,文件中的内容放置这个图形的ASCII文本内容即可。

那么这样子的ASCII内容如何制作呢,其实有很多第三方的网站提供了工具,例如
https://www.bootschool.net/ascii,你可以到网站上轻松的制作并下载,可以选择各种类型的字体。

除了已经制作好的ASCII文本内容,Springboot还支持gif、jpg、png格式的图像,同样命名为banner.gif/jpg/png放置在resources文件夹下即可。

第二种实现自定义banner的选择是设置spring.banner.location属性指定要打印的文本文件的位置,或者使用
spring.banner.image.location指定图形文件的位置,如果文件所使用的不是UTF-8的编码,你还可以通过spring.banner.charset设置指定的文件编码。

注意:banner.gif/jpg/png的优先级高于banner.txt。

我们尝试将百度的Logo以banner.png的形式放置在resources目录下,重新启动应用,可以看到打印出了百度Logo的ASCII图形。

自定义Springboot启动Banner

二、Banner打印的原理

根据以上过程的操作可以看到Springboot应用启动的过程中会根据设置的Banner图片或者ASCII图形进行解析,根据解析出来的图片像素点对应不同的字符,将ASCII字符打印出来。

我们追踪SpringApplicaiton.run()方法,可以看到以下的代码:

public ConfigurableApplicationContext run(String... args) {
  ......
  Banner printedBanner = printBanner(environment);
  ......
}
private Banner printBanner(ConfigurableEnvironment environment) {
        if (this.bannerMode == Banner.Mode.OFF) {
            return null;
        }
        ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
                : new DefaultResourceLoader(null);
        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);
    }

可以看到打印的功能主要由
SpringApplicationBannerPrinter.print()方法实现,我们一起来看看这个方法(以System.out分支为例):

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

可以看到,实际上Springboot提供了集中不同的Banner实现,有ImageBanner、TextBanner和自定义的fallbackBanner,会根据实际的情况选择不同的Banner实现进行Banner的打印。

以ImageBanner为例,跟踪发现其最终打印的实现代码如下:

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());
        }
    }
private void printBanner(BufferedImage image, int margin, boolean invert, BitDepth bitDepth, PixelMode pixelMode,
            PrintStream out) {
        AnsiElement background = invert ? AnsiBackground.BLACK : AnsiBackground.DEFAULT;
        out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
        out.print(AnsiOutput.encode(background));
        out.println();
        out.println();
        AnsiElement lastColor = AnsiColor.DEFAULT;
        AnsiColors colors = new AnsiColors(bitDepth);
        for (int y = 0; y < image.getHeight(); y++) {
            for (int i = 0; i < margin; i++) {
                out.print(" ");
            }
            for (int x = 0; x < image.getWidth(); x++) {
                Color color = new Color(image.getRGB(x, y), false);
                AnsiElement ansiColor = colors.findClosest(color);
                if (ansiColor != lastColor) {
                    out.print(AnsiOutput.encode(ansiColor));
                    lastColor = ansiColor;
                }
                out.print(getAsciiPixel(color, invert, pixelMode));
            }
            out.println();
        }
        out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
        out.print(AnsiOutput.encode(AnsiBackground.DEFAULT));
        out.println();
    }
public enum PixelMode {
        /**
         * Use text chars for pixels.
         */
        TEXT(' ', '.', '*', ':', 'o', '&', '8', '#', '@'),
        /**
         * Use unicode block chars for pixels.
         */
        BLOCK(' ', '\u2591', '\u2592', '\u2593', '\u2588');
        private char[] pixels;
        PixelMode(char... pixels) {
            this.pixels = pixels;
        }
        char[] getPixels() {
            return this.pixels;
        }
    }

可以看到对于图片像素的解析过程以及对于打印出来的字符的配置,类似的可以自己实现自定义的Banner打印类,根据自己的需要设置打印不同的字符,通过
SpringApplicaiton.setBanner(Banner banner)来使用自定义的Banner。

上一篇:Spring Boot加载自定义配置方案


下一篇:IBM Aix系统 rootvg 镜像卷更换坏硬盘步骤