SpringBoot——banner解析

banner演示

SpringBoot项目启动时会在控制台打印一个默认的LOGO,这个LOGO就是我们要讲的banner。


SpringBoot——banner解析_第1张图片

制作自己的banner

实际上,Spring Boot 支持自定义 logo 的功能。让我们来看看如何实现的。
只要你在 resources 目录下放置名为 banner.txt、banner.gif 、banner.jpg 或 banner.png 的文件,Spring Boot 会自动加载,将其作为启动时打印的 logo。

  • 对于文本文件,Spring Boot 会将其直接输出。
  • 对于图像文件( banner.gif 、banner.jpg 或 banner.png ),Spring Boot 会将图像转为 ASCII 字符,然后输出。

变量

banner.txt 文件中还可以使用变量来设置字体、颜色、版本号。

变量 描述
${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 颜色、字体。更多细节,参考:AnsiPropertySource
${application.title} MANIFEST.MF 中定义的应用名。

示例:
在 Spring Boot 项目中的 resources 目录下添加一个名为 banner.txt 的文件,内容如下:

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

注:${} 设置字体颜色的变量之间不能换行或空格分隔,否则会导致除最后一个变量外,都不生效。

启动应用后,控制台将打印如下 logo:


SpringBoot——banner解析_第2张图片

推荐两个生成字符画的网站,可以将生成的字符串放入这个banner.txt 文件:

  • http://www.network-science.de/ascii/
  • http://patorjk.com/software/taag/

配置

application.properties 中与 Banner 相关的配置:

# banner 模式。有三种模式:console/log/off
# console 打印到控制台(通过 System.out)
# log - 打印到日志中
# off - 关闭打印
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 的宽度(字符数)
spring.banner.image.width = 76
# 图像 banner 的高度(字符数)
spring.banner.image.height =
# 图像 banner 的左边界(字符数)
spring.banner.image.margin = 2
# 是否将图像转为黑色控制台主题
spring.banner.image.invert = false

编程方式设置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                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//             佛祖保佑          永无故障         永不修改             //
////////////////////////////////////////////////////////////////////

输出:


SpringBoot——banner解析_第3张图片
注意:

默认,Spring Boot 会注册一个 SpringBootBanner 的单例 Bean,用来负责打印 Banner。

如果想完全个人定制 Banner,可以这么做:先实现 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);
    }
}

接着跟进printBanner(environment);方法

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);
}

接着跟进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);
}

不管是输出到日志还是打印到控制台都会调用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指定位置
  • 默认名字为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步骤
SpringBoot——banner解析_第4张图片

banner输出原理

默认输出:
  • 先输出banner指定内容
  • 获取version信息
  • 文本内容前后对其
  • 文本内容染色
  • 输出文本内容
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指定字符集
  • 获取文本内容
  • 替换占位符
  • 输出文本内容
private static class Banners implements Banner {

    private final List 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.*设置图片属性
  • 读取图片文件流
  • 输出图片内容
private static class Banners implements Banner {

    private final List 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

你可能感兴趣的:(SpringBoot——banner解析)