当前位置: 首页 > news >正文

4. SpringBoot 自定义Banner使用与原理解析

一. banner概述

Spring banner是Spring Boot应用启动时在控制台显示的横幅内容,通常包含ASCII艺术字、项目信息等,用于增强应用标识或展示版本信息。

  .   ____          _            __ _ _/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::               (v2.7.18)

一般SpringBoot有一个默认banner,同时SpringBoot也支持对banner进行自定义设置。

演示环境

java: 11
springboot: 2.7.18

二. 自定义banner

1. 文本
  1. 自定义文本banner时,只需要在resources下新建一个名为banner.txt的文件,然后编辑banner的文本即可,如下所示。

                       _ooOoo_o8888888o88" . "88(| -_- |)O\  =  /O____/`---'\____.'  \\|     |//  `./  \\|||  :  |||//  \/  _||||| -:- |||||_  \|   | \\\  -  /// |   || \_|  ''\---/''  |   |\  .-\__  `-`  ___/-. /___`. .'  /--.--\  `. . __."" '<  `.___\_<|>_/___.'  >'"".| | :  `- \`.;`\ _ /`;.`/ - ` : | |\  \ `-.   \_ __\ /__ _/   .-` /  /
    ======`-.____`-.___\_____/___.-`____.-'======`=---='佛祖保佑        永无BUG
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
  2. 如果自定义banner的文件名不是banner.txt,则需要通过在配置文件中配置spring.banner.location,如下所示。

    spring.banner.location: favorite.txt
    
2. 图片
  1. springboot还允许我们自定义图片banner。并且如果两种banner同时存在,则先输出图片banner、再输出文本banner。
  2. 默认地,springboot将从classpath类路径下获取banner.gif、banner.jpg、banner.png作为图片banner。当然也可以通过在配置文件中配置spring.banner.image.location来指定图片的位置。
  3. 在输出图片banner时,springboot将会把图片转化成ASCII艺术画输出,而非无脑式地将图片输出。
    在这里插入图片描述
    运行结果:
    在这里插入图片描述
3. 兜底banner

SpringBoot自定义banner也支持通过实现Banner接口的方式,然后通过setBanner进行添加,一般这种方式添加的banner叫做兜底banner。

public class MyBanner implements Banner {@Overridepublic void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {System.out.println("This is 兜底 banner");}
}
@SpringBootApplication
public class Main {public static void main(String[] args) {SpringApplication springApplication = new SpringApplication(Main.class);springApplication.setBanner(new MyBanner());springApplication.run(args);}
}

运行结果:

10:48:33.316 [Thread-0] DEBUG org.springframework.boot.devtools.restart.classloader.RestartClassLoader - Created RestartClassLoader org.springframework.boot.devtools.restart.classloader.RestartClassLoader@49a2f430
This is 兜底 banner
2025-11-10 10:48:33.692  INFO 11488 --- [  restartedMain] org.example.Main                         : Starting Main using Java 11.0.15.1 on LAPTOP-8BL7V074 with PID 11488 (D:\project\springboot_demo\target\classes started by chenli in D:\project\springboot_demo)
2025-11-10 10:48:33.692  INFO 11488 --- [  restartedMain] org.example.Main                         : No active profile set, falling back to 1 default profile: "default"
2025-11-10 10:48:33.739  INFO 11488 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2025-11-10 10:48:33.739  INFO 11488 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2025-11-10 10:48:34.873  INFO 11488 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
4. placeholder占位符

springboot允许我们在banner中使用${}格式的占位符,但仅限于文本banner。常见的内置的占位符有应用版本、springboot版本、应用名。

占位符中文名占位符英文名
应用版本${application.version} 或 ${application.formatted-version}
springboot版本${spring-boot.version} 或 ${spring-boot.formatted-version}
应用名${application.title}

5. 关闭banner

如果项目启动时候不需要输出banner,可以选择关闭bannner,关闭banner的方式有两种:

  1. 配置文件

    spring.main.banner_mode=off
    
  2. SpringApplication类setBannerMode方法设置

    public class Main {public static void main(String[] args) {SpringApplication springApplication = new SpringApplication(Main.class);// 关闭banner输出springApplication.setBannerMode(Banner.Mode.OFF);springApplication.run(args);}
    }
    

三. 源码解析

SpringBoot中banner输出的逻辑全部都在SpringApplication的printBanner方法中,它在启动时被run方法调用。

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

关于banner打印的核心逻辑可以分成两个部分:获取banner和打印banner。

1. 获取banner

获取banner的逻辑实现在getBanner()方法中

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

该方法的逻辑就是获取图片和文字banner,并添加到banners中,如果banners不为空则返回banners,否则返回兜底banner;如果兜底banner也不存在,则返回默认banner。

2. 打印banner

前面在getBanner()方法中获取到的banner集合被添加到banners中,类Banners是Banner的子类,在它实现的printBanner()方法中,通过遍历内部的banner集合并调用printBanner()方法对不同的banner进行打印。

@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {for (Banner banner : this.banners) {banner.printBanner(environment, sourceClass, out);}
}

下面对不同类型bannner的打印逻辑进行分析

  • 图片banner
    图片banner被封装在ImageBanner对象中,在打印图片banner时,会对java.awt.headless的配置进行处理,然后再调用其私有方法printBanner真正输出图片banner。

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {try {if (System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS) == null) {System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, "true");}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);}
    }
    

    以下是printBanner()私有方法,查看其真正输出图片banner的逻辑。

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

    在该方法中,从配置中获取图片banner的宽高等基本样式,然后将其输出,在输出过程中将图片转为ASCII艺术图。

  • 文本banner
    文本banner被封装在ResourceBanner对象中,banner打印的逻辑在printBanner()方法中。

    该方法的逻辑首先将文本banner按照配置文件中spring.banner.charset指定的字符集转换为对应的banner字符串;然后获取用于解析${}形式的占位符的解析器集合,利用解析器处理banner字符串中的占位符。最后将处理后的banner字符串输出。

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

    文本banner的占位符解析器主要有版本号解析器、文本格式解析器、应用标题解析器。

    protected List<PropertyResolver> getPropertyResolvers(Environment environment, Class<?> sourceClass) {MutablePropertySources sources = new MutablePropertySources();if (environment instanceof ConfigurableEnvironment) {((ConfigurableEnvironment) environment).getPropertySources().forEach(sources::addLast);}sources.addLast(getTitleSource(sourceClass));sources.addLast(getAnsiSource());sources.addLast(getVersionSource(sourceClass));List<PropertyResolver> resolvers = new ArrayList<>();resolvers.add(new PropertySourcesPropertyResolver(sources));return resolvers;}
    
  • 默认banner
    默认banner被封装在SpringBootBanner中,banner打印的逻辑在printBanner()方法中。

    该方法逻辑主要是按行输出BANNER字符串的内容,同时获取SpringBoot的版本号并输出。

    @Overridepublic 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();}
    
http://www.dtcms.com/a/593327.html

相关文章:

  • Docker环境搭建:Windows/macOS/Linux全平台教程
  • mac安装GIT
  • 开锁公司做网站网站对企业的好处
  • 我爱你域名的网站html5网站建设报价
  • Diffusion VS Flow Matching
  • 电子电气架构 --- 一个具体项目的需求管理(实例化)
  • 11.string(下)
  • OpenVINS代码解读---State.h
  • 提供深圳网站制作公司永久使用免费虚拟主机
  • 智能时代的缘起:从ChatGPT到修行之路
  • 智能守护绿水青山:视频融合平台EasyCVR在森林防火监控中的实战应用
  • 如何做好网站建设前期网站规划软文写手兼职
  • docsify 本地部署完整配置模板 || 将md文件放到网页上展示
  • Bash Shell脚本学习——唇读数据集格式修复脚本
  • 网站界面用什么软件做建设网站需申请什么
  • 底层视觉及图像增强-项目实践(十六-0-(8):端到端DeepHDRNet:从原理到LED显示工程的跨界实践):从奥运大屏,到手机小屏,快来挖一挖里面都有什么
  • 视频号视频下载到手机的详细教程,以及常使用的工具!
  • 禹城网站建设公司安卓网站开发视频
  • 江国青:从郧阳沃土到法治与媒体前沿的跨界行者
  • Mediasoup的SFU媒体服务转发中心详解(与传统SFU的区别)
  • 招标网站免费企业作风建设心得体会
  • 【Java SE 基础学习打卡】07 Java 语言概述
  • 淘宝/天猫获得淘宝买家秀API,python请求示例
  • MATLAB实现BiLSTM(双向长短时记忆网络)数值预测
  • Prefix-Tuning:大语言模型的高效微调新范式
  • 凡科做的网站为什么搜不到学校网站建设成功案例
  • 通过重新安装 Node.js 依赖来解决环境问题
  • 外贸网站建站注意事项天津市哪里有做网站广告的
  • [設計模式]設計模式的作用
  • git报错解决