基本概念

  • Spring Boot启动时显示的字符图案就是banner。
  • 五种banner输出:
  1. 默认banner
    默认的banner
  2. 文字banner
    • 设置banner.txt
    • 设置spring.banner.location
  3. 图片banner
    • 设置banner.gif/png/jpg
    • 设置spring.banner.image.location
  4. 兜底banner:在没有显式设置文字或图片时显示的banner
    • springApplication.setBanner()
  5. 关闭banner
    • 设置spring.main.banner-mode=off

源码分析

获取banner

// 在run()方法中调用
private Banner printBanner(ConfigurableEnvironment environment) {
    // banner关闭
    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 print(Environment environment, Class<?> sourceClass, PrintStream out) {
    // 获取banner并打印
    Banner banner = getBanner(environment);
    banner.printBanner(environment, sourceClass, out);
    return new PrintedBanner(banner, sourceClass);
}

// 获取banner
private Banner getBanner(Environment environment) {
    Banners banners = new Banners();
    // 图片banner
    banners.addIfNotNull(getImageBanner(environment));
    // 文字banner
    banners.addIfNotNull(getTextBanner(environment));
    if (banners.hasAtLeastOneBanner()) {
        return banners;
    }
    // 兜底banner
    if (this.fallbackBanner != null) {
        return this.fallbackBanner;
    }
    // 默认banner
    return DEFAULT_BANNER;
}

// 获取图片banner
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;
    }
    // IMAGE_EXTENSION集合包括gif/jpg/png三个值,存在相应文件就读取
    for (String ext : IMAGE_EXTENSION) {
        Resource resource = this.resourceLoader.getResource("banner." + ext);
        if (resource.exists()) {
            return new ImageBanner(resource);
        }
    }
    return null;
}

// 获取文字banner
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

// 1. 打印默认Banner
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
    // BANNER是一个定义在源码中的字符串,遍历每一行进行打印
    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();
}

// 2. 打印文字banner
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(LogMessage.format("Banner not printable: %s (%s: '%s')", this.resource, ex.getClass(),
                ex.getMessage()), ex);
    }
}

// 3. 打印图片banner
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
    // 是否在headless模式下工作
    String headless = System.getProperty("java.awt.headless");
    try {
        System.setProperty("java.awt.headless", "true");
        // 获取图片的信息(长宽边界旋转等,使用java readFrames来打印)
        printBanner(environment, out);
    }
    catch (Throwable ex) {
        logger.warn(LogMessage.format("Image banner not printable: %s (%s: '%s')", 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);
        }
    }
}