☰
SpringBoot Banner自定义指南:从加载机制到工程化实践
2026/10/12 18:16:59 网站建设 项目流程

1. 项目概述与核心价值拆解

SpringBoot 应用每次启动时,控制台第一眼跳出来的那段字符画,就是 Banner。很多人把它当成一个花哨的小装饰,启动完就翻篇了。但如果你真正在团队协作、多环境部署、服务治理这些场景里待过,就会发现这个 Banner 其实承载了不少实用价值。它不只是一个 ASCII 艺术字展示,更是应用启动阶段的一个身份标识、环境提示和调试锚点。

我第一次认真对待 Banner,是因为一个多模块项目。当时同一个 jar 包在开发、测试、预发三套环境里跑,日志文件混在一起排查问题时,经常要翻好几屏才能确认当前到底是哪个环境、哪个版本。后来在 Banner 里加上了环境标识和构建版本号,启动瞬间就能确认,排查效率提升非常明显。从那以后,Banner 就成了我每个 SpringBoot 项目的标配。

这个内容适合所有使用 SpringBoot 的开发者,不管你是刚接触框架的新手,还是已经写过多个项目的老手。新手可以学会怎么自定义 Banner 让项目更有辨识度,老手可以了解 Banner 背后的加载机制、条件判断逻辑,以及怎么把它和构建流程、环境配置结合起来用。核心关键词包括 SpringBoot、Banner、ASCII-Art、自定义启动图案、banner.txt、SpringApplication、启动配置。

Banner 的本质是 SpringBoot 启动过程中,由SpringApplication在run方法早期阶段打印的一段文本。它默认从 classpath 下寻找banner.txt、banner.gif、banner.jpg、banner.png等资源文件,如果找不到就使用内置的默认 Banner。这个机制看起来简单,但里面涉及资源加载顺序、字符编码、占位符替换、条件属性控制等多个细节,值得单独拿出来讲透。

2. Banner 加载机制与核心原理

2.1 默认 Banner 是怎么来的

SpringBoot 内置的默认 Banner 其实就是一个硬编码的字符串常量,定义在SpringBootBanner类里。它长这样:

class SpringBootBanner implements Banner { private static final String[] BANNER = { "", " . ____ _ __ _ _", " /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\", " \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )", " ' |____| .__|_| |_|_| |_\\__, | / / / /", " =========|_|==============|___/=/_/_/_/", " :: Spring Boot :: (v" + SpringBootVersion.getVersion() + ")" }; }

这段字符画就是那个经典的 Spring Boot 叶子图案。注意最后一行会动态拼接当前 SpringBoot 版本号,所以不同版本启动时看到的版本信息是自动更新的。这个设计思路很值得借鉴:Banner 不是完全静态的,它可以包含动态内容。

默认 Banner 的打印时机是在SpringApplication.run方法中,环境准备完成之后、ApplicationContext 刷新之前。这个时间点很关键,意味着此时配置文件的属性已经可以读取了,但 Bean 还没有完全初始化。所以如果你在 Banner 里引用了某个自定义配置项,它是能生效的,但不要指望能拿到数据库连接之类的运行时资源。

2.2 自定义 Banner 的加载优先级

当你决定自定义 Banner 时,SpringBoot 会按照一套固定的优先级去查找资源。理解这个优先级,能帮你避免“明明放了文件却不生效”的困惑。

查找顺序大致是这样的:

  1. 首先检查spring.banner.location属性指定的位置,如果配置了就优先加载这个。
  2. 如果没有配置该属性,则在 classpath 根目录下依次查找banner.txt、banner.gif、banner.jpg、banner.png。
  3. 如果以上都没找到,再检查spring.banner.image.location指定的图片位置。
  4. 如果还是没有,最后回退到内置的默认 Banner。

这里有个容易踩的坑:banner.txt的优先级高于图片文件。也就是说,如果你同时放了banner.txt和banner.png,文本文件会赢。很多人放了图片发现没显示,就是因为 classpath 里还残留着一个banner.txt。

另外,spring.banner.location可以指向 classpath 之外的路径,比如file:./config/banner.txt,这在需要动态替换 Banner 的部署场景里很有用。但要注意路径写法,classpath 资源用classpath:前缀,文件系统资源用file:前缀,不写前缀默认按 classpath 处理。

2.3 字符编码与占位符替换

Banner 文件默认使用 UTF-8 编码读取。如果你在 Windows 环境下用记事本编辑banner.txt,保存时可能默认是 GBK,启动后就会看到乱码。这个问题我遇到过好几次,排查半天才发现是编码问题。解决办法很简单:用支持指定编码的编辑器保存为 UTF-8,或者在配置里显式指定编码。

SpringBoot 还支持在 Banner 中使用占位符,格式是${...}。常用的占位符包括:

占位符含义示例值
${spring-boot.version}SpringBoot 版本3.2.0
${spring-boot.formatted-version}格式化版本v3.2.0
${application.version}应用版本1.0.0
${application.formatted-version}格式化应用版本v1.0.0
${AnsiColor.BRIGHT_GREEN}ANSI 颜色绿色
${AnsiStyle.BOLD}ANSI 样式加粗

这些占位符是在 Banner 打印前由Banner接口的实现类进行替换的。application.version来自MANIFEST.MF文件中的Implementation-Version,如果你用 Maven 或 Gradle 构建,需要确保打包时写入了版本信息。我一般会在pom.xml里配置maven-jar-plugin的manifest条目,把版本号写进去,这样 Banner 里就能显示真实构建版本。

ANSI 颜色占位符需要终端支持 ANSI 转义序列。在 IDEA 的控制台里通常没问题,但如果你把日志重定向到文件,这些转义字符会变成乱码。所以我的经验是:颜色可以用,但不要过度依赖,关键信息还是靠文字表达。

3. 自定义 Banner 的完整实操流程

3.1 生成 ASCII 艺术字

制作 Banner 的第一步是生成字符画。手工敲是不现实的,得借助工具。常用的方式有几种:

第一种是在线生成网站。你输入文字,选择字体和宽度,它会输出对应的 ASCII 字符画。这类工具很多,搜“ASCII art generator”就能找到。优点是方便快捷,缺点是生成的图案风格有限,而且有些网站输出的字符在等宽字体下对齐会出问题。

第二种是用命令行工具figlet。Linux 和 macOS 上可以直接安装,Windows 上可以通过包管理器或者 WSL 使用。基本用法:

figlet -f slant "MY APP"

-f参数指定字体,slant是一种斜体风格,还有standard、big、small、banner等多种字体可选。figlet的输出是纯 ASCII 字符,兼容性最好。如果想让输出更宽或更窄,可以用-w指定宽度。

第三种是用toilet工具,它是figlet的增强版,支持颜色和更丰富的字体。用法类似:

toilet -f mono12 -F metal "MY APP"

-F参数指定滤镜效果,metal会生成带金属质感的字符画。不过这种带滤镜的输出可能包含非 ASCII 字符,在某些终端下显示会有问题,用之前最好先测试。

我个人的习惯是用figlet生成基础字符画,然后手工微调。因为自动生成的图案往往太宽,在标准 80 列终端里会换行,看起来很难受。微调主要是删减一些装饰性字符,保证每行不超过 80 个字符。

3.2 编写 banner.txt 文件

生成好字符画之后,把它保存为banner.txt,放到src/main/resources目录下。这是 Maven 和 Gradle 项目的标准资源目录,打包后会自动进入 classpath 根目录。

一个完整的banner.txt通常包含这几部分:

${AnsiColor.BRIGHT_CYAN} __ __ _ _ _ | \/ |_ _| | __ _ | | __ _| |_ __ _ | |\/| | | | | |/ _` | | |/ _` | __/ _` | | | | | |_| | | (_| | | | (_| | || (_| | |_| |_|\__, |_|\__,_| |_|\__,_|\__\__,_| |___/ ${AnsiColor.BRIGHT_GREEN} :: Application :: ${application.title} :: Version :: ${application.formatted-version} :: Profile :: ${spring.profiles.active:default} :: Started at :: ${timestamp} ${AnsiColor.DEFAULT}

这里有几个细节值得说明。${spring.profiles.active:default}这种写法表示如果spring.profiles.active没有配置,就显示default。冒号后面的部分是默认值,这个语法在 Spring 的占位符解析里是通用的。

${timestamp}这个占位符不是 SpringBoot 内置的,直接写会原样输出。如果你想显示启动时间,需要自己实现一个Banner接口,或者在 Banner 打印后单独输出。我一般不在 Banner 里放时间戳,因为 Banner 打印时应用还没完全启动,这个时间意义不大。

另外注意${AnsiColor.DEFAULT}要放在最后,把颜色重置回默认值,否则后续的控制台输出都会带着颜色,看起来会很乱。

3.3 配置属性控制 Banner 行为

SpringBoot 提供了一组属性来精细控制 Banner 的显示行为,这些属性可以写在application.properties或application.yml里。

属性名默认值说明
spring.banner.locationclasspath:banner.txt文本 Banner 位置
spring.banner.charsetUTF-8Banner 文件编码
spring.banner.image.locationclasspath:banner.gif图片 Banner 位置
spring.main.banner-modeconsole显示模式:console/log/off
spring.banner.image.width76图片 Banner 宽度(字符数)
spring.banner.image.height自动计算图片 Banner 高度
spring.banner.image.margin2图片左边距
spring.banner.image.invertfalse是否反转图片颜色

spring.main.banner-mode这个属性特别实用。设为off可以完全关闭 Banner,适合生产环境减少日志噪音;设为log会把 Banner 输出到日志文件而不是控制台,适合需要保留启动记录但不希望控制台太乱的场景。

我在实际项目里的做法是:开发环境用console模式,方便一眼看到应用信息;生产环境用log模式,Banner 进日志文件,控制台保持干净。这个可以通过 profile 配置轻松实现:

spring: config: activate: on-profile: dev main: banner-mode: console --- spring: config: activate: on-profile: prod main: banner-mode: log

3.4 图片 Banner 的处理逻辑

除了文本,SpringBoot 还支持图片 Banner。它会把图片转换成 ASCII 字符画来显示。支持的格式有 GIF、JPG、PNG。

图片 Banner 的转换逻辑大致是:读取图片像素,根据亮度映射到不同的字符,然后按指定宽度输出。亮度高的像素用稀疏字符(如.、'),亮度低的用密集字符(如@、#)。这样就能在终端里还原出图片的轮廓。

配置图片 Banner 时,spring.banner.image.width控制输出宽度,默认 76 个字符。这个值不要设太大,否则终端会换行。spring.banner.image.height如果不设,会根据图片宽高比自动计算。spring.banner.image.invert用于深色背景终端,把颜色映射反过来。

不过说实话,图片 Banner 在实际项目里用得不多。一是转换后的效果往往不如手工设计的文本 Banner 精致,二是图片文件会增加打包体积,三是不同终端的字符宽度不一致,显示效果很难保证。我试过几次之后还是回到了文本 Banner。

4. 进阶玩法与工程化实践

4.1 编程式自定义 Banner

如果配置文件的方式满足不了需求,比如你想根据运行环境动态生成 Banner 内容,可以实现Banner接口:

public class CustomBanner implements Banner { @Override public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) { String appName = environment.getProperty("spring.application.name", "Unknown"); String profile = String.join(",", environment.getActiveProfiles()); out.println("================================"); out.println(" Application: " + appName); out.println(" Profile: " + profile); out.println(" Java: " + System.getProperty("java.version")); out.println("================================"); } }

然后在启动类里替换默认 Banner:

@SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(MyApplication.class); app.setBanner(new CustomBanner()); app.run(args); } }

这种方式的好处是可以在 Banner 里输出任何运行时能获取到的信息,比如 JVM 参数、系统属性、环境变量等。我在一个需要严格审计启动信息的项目里用过这招,把启动用户、主机名、JVM 堆大小都打出来,运维排查问题时非常方便。

注意printBanner方法的out参数是PrintStream,你可以直接往里写内容。但不要在这里做耗时操作,因为 Banner 打印是在启动早期,阻塞太久会影响启动速度。

4.2 Banner 与构建流程集成

把 Banner 和构建流程结合起来,可以实现版本号、构建时间等信息的自动更新。以 Maven 为例,可以用maven-resources-plugin的过滤功能:

<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>banner.txt</include> </includes> </resource> </resources> </build>

然后在banner.txt里使用 Maven 属性占位符:

:: Version :: ${project.version} :: Built at :: ${maven.build.timestamp}

这样每次构建时,Banner 里的版本号和时间戳会自动更新。但要注意,开启过滤后,banner.txt里的${...}会被 Maven 先处理一遍,SpringBoot 的占位符可能被误替换。解决办法是用不同的分隔符,或者只对特定文件开启过滤。

Gradle 的话可以用processResources任务配合expand方法:

processResources { filesMatching('banner.txt') { expand(project.properties) } }

这个配置会让banner.txt里的${project.version}等占位符在构建时被替换。同样要注意和 SpringBoot 占位符的冲突问题。

4.3 多环境 Banner 切换

一个常见的需求是:不同环境显示不同的 Banner。比如开发环境显示彩色大字,生产环境显示简洁的版本信息。实现方式有几种:

第一种是用 profile 特定的配置文件指定不同的spring.banner.location:

# application-dev.yml spring: banner: location: classpath:banner-dev.txt # application-prod.yml spring: banner: location: classpath:banner-prod.txt

第二种是在 Banner 文件里用条件占位符。SpringBoot 的占位符支持默认值语法,但不支持条件判断。如果想根据 profile 显示不同内容,还是得靠编程式 Banner。

第三种是用 Maven 或 Gradle 的资源过滤,在构建时根据 profile 替换 Banner 内容。这种方式适合 Banner 差异不大的场景。

我一般用第一种方式,简单直接,不同环境的 Banner 文件分开维护,互不干扰。

4.4 Banner 在微服务架构中的实践

在微服务架构里,服务数量多,启动日志混杂。Banner 可以作为一个轻量的服务标识手段。我的做法是在每个服务的banner.txt里加上服务名和端口号:

${AnsiColor.BRIGHT_YELLOW} _____ _ _ | ___| _ _ __ ___| |__ ___| |_ | |_ | | | | '_ \ / __| '_ \ / _ \ __| | _|| |_| | | | | (__| | | | __/ |_ |_| \__,_|_| |_|\___|_| |_|\___|\__| ${AnsiColor.BRIGHT_GREEN} :: Service :: user-service :: Port :: ${server.port} :: Profile :: ${spring.profiles.active:default} ${AnsiColor.DEFAULT}

这样在容器日志里,一眼就能区分是哪个服务在启动。配合日志采集系统,还可以基于 Banner 内容做启动事件的监控告警。

但要注意,Banner 输出到日志时,ANSI 颜色转义字符会变成乱码。所以如果生产环境用log模式,建议去掉颜色占位符,或者用spring.banner.image.invert之类的属性控制。我通常准备两份 Banner 文件,一份带颜色给控制台用,一份纯文本给日志用。

5. 常见问题与排查技巧实录

5.1 Banner 不生效的排查思路

这是最常见的问题:明明放了banner.txt,启动时还是显示默认的 Spring Boot 图案。排查步骤可以按这个顺序来:

首先确认文件位置。banner.txt必须在 classpath 根目录下,也就是src/main/resources/banner.txt。如果你放在了子目录里,比如src/main/resources/config/banner.txt,默认是找不到的,需要配置spring.banner.location=classpath:config/banner.txt。

其次检查文件编码。用file命令或者编辑器的编码检测功能确认是 UTF-8。如果是 GBK,改成 UTF-8 再试。

然后检查是否有多个 Banner 文件冲突。比如同时存在banner.txt和banner.png,文本文件会优先。如果你期望显示图片,就得把banner.txt删掉或改名。

最后检查spring.main.banner-mode是否被设为了off。这个属性可能写在配置文件里,也可能通过命令行参数传入,还可能被环境变量覆盖。用--debug启动可以看到配置属性的来源。

5.2 占位符不替换的问题

有时候 Banner 里的${application.version}原样输出了,没有替换成实际版本号。原因通常是MANIFEST.MF里没有Implementation-Version条目。

Maven 项目需要在pom.xml里配置:

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addDefaultImplementationEntries>true</addDefaultImplementationEntries> </manifest> </archive> </configuration> </plugin>

Gradle 项目在jar任务里配置:

jar { manifest { attributes( 'Implementation-Version': project.version ) } }

配置后重新打包,application.version就能正确替换了。注意这个占位符读取的是MANIFEST.MF,不是pom.xml或build.gradle里的版本号,所以必须确保打包时写入了 manifest。

5.3 终端显示乱码或对齐错位

ASCII 字符画在不同终端下的显示效果可能不一样,主要原因是字体不是等宽字体,或者终端宽度不够导致换行。

解决办法:一是确保终端使用等宽字体,比如 Consolas、Monaco、Fira Code 等;二是控制 Banner 每行宽度不超过 80 个字符,兼容大多数终端;三是避免使用非 ASCII 字符,比如某些 Unicode 制表符,在不同终端下宽度可能不一致。

如果 Banner 里有中文,还要注意中文字符的宽度问题。在等宽字体下,一个中文通常占两个英文字符的宽度,如果混排时没考虑这点,对齐就会乱。我的建议是 Banner 里尽量只用 ASCII 字符,中文信息放在 Banner 下方的普通文本行里。

5.4 常见问题速查表

问题现象可能原因解决方法
显示默认 Banner文件位置不对放到src/main/resources/根目录
显示乱码文件编码不是 UTF-8用编辑器另存为 UTF-8
占位符未替换MANIFEST 缺少版本信息配置打包插件写入 manifest
Banner 不显示banner-mode 为 off检查配置,改为 console 或 log
图片 Banner 变形终端字符宽高比不一致调整spring.banner.image.width
颜色转义字符乱码输出到不支持 ANSI 的地方日志模式下去掉颜色占位符
启动变慢Banner 文件过大精简字符画,控制行数和宽度

5.5 几个实操避坑心得

第一个坑:IDEA 里运行正常,打包后 Banner 不生效。这通常是因为 IDEA 运行时 classpath 包含了src/main/resources,而打包后资源文件的位置变了。检查pom.xml或build.gradle里有没有排除banner.txt,或者资源目录配置是否正确。

第二个坑:SpringBoot 版本升级后 Banner 占位符失效。不同版本的 SpringBoot 支持的占位符可能不同,比如${spring-boot.version}在 2.x 和 3.x 里都能用,但某些格式化占位符的行为有变化。升级版本后最好重新测试一下 Banner 输出。

第三个坑:Banner 里的${}和 Maven 过滤冲突。如果开启了资源过滤,Maven 会先处理${...},导致 SpringBoot 的占位符被替换成空值。解决办法是在banner.txt里用@...@作为 Maven 占位符(需要配置maven-resources-plugin的delimiter),或者对banner.txt关闭过滤。

第四个坑:Banner 输出到日志文件时带上了 ANSI 转义字符。这些字符在文本编辑器里看起来是乱码,影响日志可读性。如果生产环境用log模式,建议单独准备一份不带颜色的 Banner 文件,通过 profile 切换。

6. 一些扩展思路与个人体会

Banner 这个功能看似边缘,但用好了能解决不少实际问题。除了前面说的环境标识、版本追踪,还可以做一些有意思的扩展。

比如结合启动参数动态显示 Banner。通过实现Banner接口,读取args里的自定义参数,在 Banner 里显示本次启动的特殊配置。这在需要频繁调整参数的调试场景里很有用,一眼就能确认参数有没有生效。

再比如把 Banner 和健康检查结合。在 Banner 里输出应用的关键依赖状态,比如数据库连接、缓存服务等。但要注意 Banner 打印时机很早,此时很多 Bean 还没初始化,能获取的信息有限。如果确实需要,可以在ApplicationRunner里输出补充信息。

还有一个思路是用 Banner 做简单的启动耗时统计。在main方法开始时记录时间戳,在 Banner 打印时计算差值,输出“启动耗时 XX ms”。这个对性能优化有帮助,能快速判断启动是否变慢。

我个人在实际操作中的体会是:Banner 的价值不在于好看,而在于信息传达。一个设计良好的 Banner 应该让运维和开发在启动瞬间就能获取关键信息——这是什么服务、什么版本、什么环境、什么端口。颜色和图案是锦上添花,但不要为了花哨牺牲可读性。另外,Banner 文件要纳入版本管理,和代码一起维护,避免不同环境不一致导致排查困难。

最后分享一个小技巧:如果你不确定 Banner 文件有没有被正确加载,可以在启动时加上--debug参数,SpringBoot 会输出自动配置报告,其中包含 Banner 的加载信息。或者直接看启动日志的前几行,Banner 内容会原样打印出来,有没有生效一目了然。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询