SpringBoot之Banner源码深度分解
发布日期:2025-05-05 12:30:18 浏览次数:4 分类:精选文章

本文共 8539 字,大约阅读时间需要 28 分钟。

Spring Boot Banner 架构原理

##Banner 的基本作用 Banner 更多地作为一种人性化的标志,例如企业的标志、某个知名产品的标志、不同环境的标志等。在Spring Boot中,Banner的设计理念强调“大道至简”,即将Banner的非功能需求和部分功能需求封装好,让用户只需按照最傻瓜的操作步骤使用即可。

每一个应用都应该有自己的Banner,独一无二的设计使其更具个性化。


常规使用方法

Spring Boot 提供了默认的配置方式,无需修改任何代码,只需在 resources 目录下添加 banner.txt 文件即可。

banner.txt 文件格式

文件内容如下:

.___          _            __ _ _ 
/\\ / ___'__ __ _ _(_)_ __ __ _ \ \ \
\( ( )\___ | '_ | '_| | '_ \\/ _` | \ \ \
\ \\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Hello World ::
${application.formatted-version}

支持的变量参数

  • ${AnsiColor.BRIGHT_RED}:设置控制台中输出内容的颜色
  • ${application.version}:获取MANIFEST.MF文件中的版本号
  • ${application.formatted-version}:格式化后的版本信息
  • ${spring-boot.version}:Spring Boot的版本号
  • ${spring-boot.formatted-version}:格式化后的Spring Boot版本号
  • ${application.title}:应用标题

类组织关系解析:Spring Boot JAR

所有与Banner相关的类都位于 spring-boot 包中,主要包括以下几个关键类:

  • Banner:抽象接口,定义了Banner的打印方式。其中 Mode 枚举控制打印模式:LOGCONSOLEOFF
  • SpringBootBanner:实现Banner接口,用于打印“Spring Boot”的图案。
  • ImageBanner:实现Banner接口,用于将图片文件(如banner.gif、banner.jpg等)转换为ASCII字符图案。
  • ResourceBanner:实现Banner接口,用于处理文本文件(如banner.txt)的资源文件。
  • Banners:S..A..BannerPrinter 的私有静态类,封装多个Banner的集合。
  • PrintedBanner:S..A..BannerPrinter 的私有静态类,封装资源和对应的Banner。
  • SpringApplicationBannerPrinter:核心类,负责与SpringApplication对接,实现Banner的打印逻辑。

  • SpringApplication 类

    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);
    }
    public void setBanner(Banner banner) {
    this.banner = banner;
    }
    public void setBannerMode(Banner.Mode bannerMode) {
    this.bannerMode = bannerMode;
    }

    关键点

    • setBanner(Banner) 方法允许用户自定义Banner实现类,支持扩展。
    • setBannerMode(Banner.Mode) 方法控制Banner的打印模式,默认为 CONSOLE

    SpringApplicationBannerPrinter 类

    static final String BANNER_LOCATION_PROPERTY = "banner.location";
    static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location";
    static final String DEFAULT_BANNER_LOCATION = "banner.txt";
    static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
    private static final Banner DEFAULT_BANNER = new SpringBootBanner();
    private final ResourceLoader resourceLoader;
    private final Banner fallbackBanner;
    SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) {
    this.resourceLoader = resourceLoader;
    this.fallbackBanner = fallbackBanner;
    }
    public Banner print(Environment environment, Class
    sourceClass) {
    Banner banner = getBanner(environment, this.fallbackBanner);
    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, PrintStream out) {
    Banner banner = getBanner(environment, this.fallbackBanner);
    banner.printBanner(environment, sourceClass, out);
    return new PrintedBanner(banner, sourceClass);
    }
    private Banner getBanner(Environment environment, Banner definedBanner) {
    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;
    }
    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;
    }
    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;
    }
    private String createStringFromBanner(Banner banner, Environment environment, Class
    mainApplicationClass) throws UnsupportedEncodingException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    banner.printBanner(environment, mainApplicationClass, new PrintStream(baos));
    String charset = environment.getProperty("banner.charset", "UTF-8");
    return baos.toString(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);
    }
    }
    public boolean hasAtLeastOneBanner() {
    return !this.banners.isEmpty();
    }
    @Override
    public void printBanner(Environment environment, Class
    sourceClass, PrintStream out) {
    for (Banner banner : this.banners) {
    banner.printBanner(environment, sourceClass, out);
    }
    }
    }
    private static class PrintedBanner implements Banner {
    private final Banner banner;
    private final Class
    sourceClass;
    PrintedBanner(Banner banner, Class
    sourceClass) {
    this.banner = banner;
    this.sourceClass = sourceClass;
    }
    @Override
    public void printBanner(Environment environment, Class
    sourceClass, PrintStream out) {
    sourceClass = (sourceClass == null ? this.sourceClass : sourceClass);
    this.banner.printBanner(environment, sourceClass, out);
    }
    }

    关键点

    • ImageBanner 和 ResourceBanner:两者可以同时存在,ImageBanner 会优先打印。
    • Banners 和 PrintedBanner:封装多个Banner 的集合类,体现了工具类设计思想。

    public interface Banner {
    void printBanner(Environment environment, Class
    sourceClass, PrintStream out);
    enum Mode {
    OFF,
    CONSOLE,
    LOG
    }
    }

    关键点

    • @FunctionalInterface:允许使用Lambda表达式实现。
    • Mode 枚举:定义了三种打印模式。

    SpringBootBanner 类

    /** 
    * 默认的Banner实现类,用于打印“Spring Boot”的图案。
    */
    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 + ")");
    String padding = "";
    while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
    padding += " ";
    }
    printStream.println(AnsiOutput.toString(
    AnsiColor.GREEN, SPRING_BOOT,
    AnsiColor.DEFAULT, padding,
    AnsiStyle.FAINT, version));
    printStream.println();
    }
    }

    关键点

    • SpringBootBanner:默认实现,用于打印Spring Boot的图案。
    • 版本信息:通过Spring Boot Version获取版本号,并附加到banner中。

    ImageBanner 和 ResourceBanner

    • ImageBanner:处理图片文件,根据配置项 banner.image.location 加载图片文件,支持 gif、jpg、png 等格式。
    • ResourceBanner:处理文本文件,根据配置项 banner.location 加载 banner.txt 文件。

    PrintedBanner 和 Banners 类

    • PrintedBanner:封装Banner 实例和源类,用于存储和管理多个Banner。
    • Banners:封装多个Banner 的集合,支持动态添加和打印。

    总结

    Spring Boot 的 Banner 架构设计体现了面向对象和面向切面编程的思想,通过封装和抽象将非功能需求和部分功能需求集中管理。特别值得一提的是 PrintedBanner 和 Banners 类的设计,它们通过私有静态类的方式实现了工具类的功能,充分发挥了OOP思想的优势。

    技术的发展往往遵循一定的规律,从面向过程编程到面向对象编程,再到面向切面编程、面向服务编程,这些设计思想不断演化,体现了技术生产力的提升。

    上一篇:Pix2Pix如何工作?
    下一篇:pip更换源

    发表评论

    最新留言

    第一次来,支持一个
    [***.219.124.196]2026年06月17日 02时04分12秒