Spring Boot banner详解

Spring Boot 3.x系列文章

  1. Spring Boot 2.7.8 中文参考指南(一)
  2. Spring Boot 2.7.8 中文参考指南(二)-Web
  3. Spring Boot 源码阅读初始化环境搭建
  4. Spring Boot 框架整体启动流程详解
  5. Spring Boot 系统初始化器详解
  6. Spring Boot 监听器详解
  7. Spring Boot banner详解

自定义banner

Spring Boot 默认打印的banner是这样的,Java工程师看都看腻了。
在这里插入图片描述

一般的公司如果有自己脚手架,都会选择自定义banner,放一个公司Logo或者框架别名。

简易版banner

首先生成一个自己的banner,比如我生成的
在这里插入图片描述

生成的网站很多,可以用"banner 生成器"自行搜索

把生成的内容copy到txt中,命名为"banner.txt"(UTF-8),然后放到resources下。
启动Spring Boot 即可看到效果。

在这里插入图片描述

自定义banner路径

上述的banner.txt 只能放在resources根目录下,不能在resources子目录或其他的目录,使用spring.banner.location指定该文件的路径,如果该文件不是UTF-8编码,使用spring.banner.charset指定文件编码,比如我将文件放到resources的子目录static中。

在这里插入图片描述

自定义banner 样式

光一个Logo也还是太单调,如果能再打印个Spring Boot 版本、应用程序版本就更好了,Spring Boot 都给我们提供了相关变量,可以在banner.txt中使用。

  • ${application.version} 应用程序版本
  • ${application.formatted-version} 格式化的应用程序版本,前缀是v
  • ${spring-boot.version} Spring Boot框架版本
  • ${spring-boot.formatted-version} 格式化的Spring Boot框架版本,前缀是v
  • ${Ansi.NAME} (or ${AnsiColor.NAME}, ${AnsiBackground.NAME}, ${AnsiStyle.NAME})
    给文字加颜色,加背景,加样式,NAME的值可以在这里获取ansi
  • ${application.title} 应用程序的标题

如图我给banner 加上了颜色,加上了版本

在这里插入图片描述
配置如下:

${AnsiColor.GREEN}
  /$$$$$$   /$$$$$$  /$$$$$$$  /$$   /$$       /$$$$$$ /$$$$$$$$ /$$$$$$   /$$$$$$  /$$     /$$ /$$$$$$   /$$$$$$  /$$     /$$
 /$$__  $$ /$$__  $$| $$__  $$| $$$ | $$      |_  $$_/|__  $$__//$$__  $$ /$$__  $$|  $$   /$$//$$__  $$ /$$__  $$|  $$   /$$/
| $$  \__/| $$  \__/| $$  \ $$| $$$$| $$        | $$     | $$  | $$  \__/| $$  \ $$ \  $$ /$$/| $$  \__/| $$  \ $$ \  $$ /$$/
| $$      |  $$$$$$ | $$  | $$| $$ $$ $$ /$$$$$$| $$     | $$  |  $$$$$$ | $$$$$$$$  \  $$$$/ |  $$$$$$ | $$$$$$$$  \  $$$$/
| $$       \____  $$| $$  | $$| $$  $$$$|______/| $$     | $$   \____  $$| $$__  $$   \  $$/   \____  $$| $$__  $$   \  $$/
| $$    $$ /$$  \ $$| $$  | $$| $$\  $$$        | $$     | $$   /$$  \ $$| $$  | $$    | $$    /$$  \ $$| $$  | $$    | $$
|  $$$$$$/|  $$$$$$/| $$$$$$$/| $$ \  $$       /$$$$$$   | $$  |  $$$$$$/| $$  | $$    | $$   |  $$$$$$/| $$  | $$    | $$
 \______/  \______/ |_______/ |__/  \__/      |______/   |__/   \______/ |__/  |__/    |__/    \______/ |__/  |__/    |__/
${AnsiColor.DEFAULT}
::Spring Boot Version: ${AnsiColor.RED}${spring-boot.formatted-version}${AnsiColor.DEFAULT}
::Application Version: ${AnsiColor.RED}${application.formatted-version}${AnsiColor.DEFAULT}

  • 如果颜色不起作用,那就是需要开启一下:spring.output.ansi.enabled=always
  • ${AnsiColor.DEFAULT} 是将颜色重置,防止前面的颜色影响下面的
  • ${application.formatted-version} 、 ${application.version}、 ${application.title} 要使用jar包启动才会打印
  • banner.txt 中可以配置环境变量environment中的任何键值

使用图片做banner

在Spring Boot 3.x版本中已经不被支持

编码方式定义banner

自定义一个CustomBanner类,实现Banner接口,如:

import org.springframework.boot.Banner;
import org.springframework.core.env.Environment;

import java.io.PrintStream;

public class CustomBanner implements Banner {

    public static final String BANNER = "  /$$$$$$   /$$$$$$  /$$$$$$$  /$$   /$$       /$$$$$$ /$$$$$$$$ /$$$$$$   /$$$$$$  /$$     /$$ /$$$$$$   /$$$$$$  /$$     /$$\n" +
            " /$$__  $$ /$$__  $$| $$__  $$| $$$ | $$      |_  $$_/|__  $$__//$$__  $$ /$$__  $$|  $$   /$$//$$__  $$ /$$__  $$|  $$   /$$/\n" +
            "| $$  \\__/| $$  \\__/| $$  \\ $$| $$$$| $$        | $$     | $$  | $$  \\__/| $$  \\ $$ \\  $$ /$$/| $$  \\__/| $$  \\ $$ \\  $$ /$$/ \n" +
            "| $$      |  $$$$$$ | $$  | $$| $$ $$ $$ /$$$$$$| $$     | $$  |  $$$$$$ | $$$$$$$$  \\  $$$$/ |  $$$$$$ | $$$$$$$$  \\  $$$$/  \n" +
            "| $$       \\____  $$| $$  | $$| $$  $$$$|______/| $$     | $$   \\____  $$| $$__  $$   \\  $$/   \\____  $$| $$__  $$   \\  $$/   \n" +
            "| $$    $$ /$$  \\ $$| $$  | $$| $$\\  $$$        | $$     | $$   /$$  \\ $$| $$  | $$    | $$    /$$  \\ $$| $$  | $$    | $$    \n" +
            "|  $$$$$$/|  $$$$$$/| $$$$$$$/| $$ \\  $$       /$$$$$$   | $$  |  $$$$$$/| $$  | $$    | $$   |  $$$$$$/| $$  | $$    | $$    \n" +
            " \\______/  \\______/ |_______/ |__/  \\__/      |______/   |__/   \\______/ |__/  |__/    |__/    \\______/ |__/  |__/    |__/    \n";

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
        printStream.println(BANNER);
    }
}

SpringApplication 中配置banner

SpringApplication springApplication = new SpringApplication(SpringBootDemoApplication.class);
springApplication.setBanner(new CustomBanner());

如果在类路径中存在banner.txt 在会优先使用banner.txt

禁用banner

Spring Boot 提供了spring.main.banner-mode 配置,OFF-关闭banner打印,CONSOLE-使用System.out打印banner,log文件不会记录,LOG-打印到log文件

另外同样可以用springApplication.setBannerMode(Banner.Mode.OFF);方式设置banner

加载打印原理

在之前的《Spring Boot 框架整体启动流程详解》中,我们看到有一步是

//打印banner
Banner printedBanner = printBanner(environment);

这一步就是加载打印banner的核心。

private Banner printBanner(ConfigurableEnvironment environment) {
//如果bannerMode是关闭的,就会不打印banner
	if (this.bannerMode == Banner.Mode.OFF) {
		return null;
	}
	//获取资源加载器,如果当前没有就使用默认的资源加载器
	ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
			: new DefaultResourceLoader(null);
//创建一个SpringBoot应用程序的banner打印类,如果通过setBanner设置,this.banner会有值
	SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
	//如果bannerMode 是打印到log文件
	if (this.bannerMode == Mode.LOG) {
		return bannerPrinter.print(environment, this.mainApplicationClass, logger);
	}
	//默认打印到控制台
	return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

先看下如何打印到log文件:

Banner print(Environment environment, Class<?> sourceClass, Log logger) {
//获取Banner对象
	Banner banner = getBanner(environment);
	try {
	//使用log 的info 级别打印
		logger.info(createStringFromBanner(banner, environment, sourceClass));
	}
	catch (UnsupportedEncodingException ex) {
		logger.warn("Failed to create String for banner", ex);
	}
	//创建一个打印banner的装饰器bean,允许后期再次使用
	return new PrintedBanner(banner, sourceClass);
}

如何获取的Banner对象:

private Banner getBanner(Environment environment) {
//获取txt文本banner
	Banner textBanner = getTextBanner(environment);
	if (textBanner != null) {
		return textBanner;
	}
	//fallbackBanner 为前期通过setBanner设置的自定义banner
	//可见如果两者同时设置,优先使用的txt文本banner
	if (this.fallbackBanner != null) {
		return this.fallbackBanner;
	}
	//都没有,返回一个默认的SpringBootBanner
	return DEFAULT_BANNER;
}

如果获取txt文本banner:

private Banner getTextBanner(Environment environment) {
//从环境变量中获取spring.banner.location指定的banner地址,如果没有,使用banner.txt
	String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
	//获取指定的资源对象
	Resource resource = this.resourceLoader.getResource(location);
	try {
	//资源存在,并且资源路径中不包含liquibase-core
		if (resource.exists() && !resource.getURL().toExternalForm().contains("liquibase-core")) {
		//创建一个从源打印的banner对象,实现了Banner接口
			return new ResourceBanner(resource);
		}
	}
	catch (IOException ex) {
		// Ignore
	}
	return null;
}

获取到文本banner的ResourceBanner资源对象后,回到print(Environment environment, Class<?> sourceClass, Log logger) 这个方法中,接下来就是要把获取到banner对象打印出来,createStringFromBanner将获取到banner对象,调用其中的printBanner方法,把输出流转为UTF-8的字符串输出到log文件中。

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("spring.banner.charset", "UTF-8");
	return baos.toString(charset);
}

ResourceBanner的printBanner方法:

public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
	try {
	//从流读取banner.txt 字符串,使用spring.banner.charset编码,或者UTF-8
		String banner = StreamUtils.copyToString(this.resource.getInputStream(),
				environment.getProperty("spring.banner.charset", Charset.class, StandardCharsets.UTF_8));
				//获取用于解析占位符的所有属性源
		for (PropertyResolver resolver : getPropertyResolvers(environment, sourceClass)) {
		//解析banner,使用PropertyPlaceholderHelper工具类解析
			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);
	}
}

bannerPrinter.print(environment, this.mainApplicationClass, System.out);
打印到控制台的逻辑也是一样的,只是直接输出到控制台

Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
	Banner banner = getBanner(environment);
	banner.printBanner(environment, sourceClass, out);
	return new PrintedBanner(banner, sourceClass);
}

总结

通过图来总结一下整个流程
在这里插入图片描述

作者其他要推荐的文章,欢迎来学习:
Prometheus 系列文章

  1. Prometheus 的介绍和安装
  2. 直观感受PromQL及其数据类型
  3. PromQL之选择器和运算符
  4. PromQL之函数
  5. Prometheus 告警机制介绍及命令解读
  6. Prometheus 告警模块配置深度解析
  7. Prometheus 配置身份认证
  8. Prometheus 动态拉取监控服务
  9. Prometheus 监控云Mysql和自建Mysql

Grafana 系列文章,版本:OOS v9.3.1

  1. Grafana 的介绍和安装
  2. Grafana监控大屏配置参数介绍(一)
  3. Grafana监控大屏配置参数介绍(二)
  4. Grafana监控大屏可视化图表
  5. Grafana 查询数据和转换数据
  6. Grafana 告警模块介绍
  7. Grafana 告警接入飞书通知

Spring Boot Admin 系列

  1. Spring Boot Admin 参考指南
  2. SpringBoot Admin服务离线、不显示健康信息的问题
  3. Spring Boot Admin2 @EnableAdminServer的加载
  4. Spring Boot Admin2 AdminServerAutoConfiguration详解
  5. Spring Boot Admin2 实例状态监控详解
  6. Spring Boot Admin2 自定义JVM监控通知
  7. Spring Boot Admin2 自定义异常监控
  8. Spring Boot Admin 监控指标接入Grafana可视化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/32801.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes API Server源码学习(二):OpenAPI、API Resource的装载、HTTP Server具体是怎么跑起来的?

本文基于Kubernetes v1.22.4版本进行源码学习 6、OpenAPI 1&#xff09;、OpenAPI的作用 OpenAPI是由Swagger发展而来的一个规范&#xff0c;一种形式化描述Restful Service的语言&#xff0c;便于使用者理解和使用一个Service。通过OpenAPI规范可以描述一个服务&#xff1a;…

2024考研408-计算机组成原理第四章-指令系统学习笔记

文章目录 前言一、指令系统现代计算机的结构1.1、指令格式1.1.1、指令的定义1.1.2、指令格式1.1.3、指令—按照地址码数量分类①零地址指令②一地址指令&#xff08;1个操作数、2个操作数情况&#xff09;③二地址指令④三地址指令⑤四地址指令 1.1.4、指令-按照指令长度分类1.…

基于Java高校教师科研信息展示网站设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

[Hadoop] 期末答辩问题准备

0.相关概念 1.什么是NameNode&#xff1f; NameNode是整个文件系统的管理节点&#xff0c;它维护着整个文件系统的文件目录树&#xff0c;文件/目录的元信息和每个文件对应的数据块列表。并接收用户的操作请求。 2.SecondaryNameNode的主要作用&#xff1f; SecondaryNameN…

学习Angular的编程之旅

目录 1、简介 2、特点 2.1 横跨多种平台 2.2 速度与性能 2.3 美妙的工具 3、Angular 应用&#xff1a;知识要点 3.1 组件 3.2 模板 3.3 依赖注入 4、与其他框架的对比 1、简介 Angular 是一个应用设计框架与开发平台&#xff0c;旨在创建高效而精致的单页面应用。 A…

别再用查询count,判断数据是否存在了

目录 一、目前多数人的写法 二、优化方案 三、总结 大家在实际的开发过程中&#xff0c;会根据某些条件&#xff0c;从数据库表中查询出是否存在符合该条件的数据。无论是刚入行的程序员小白&#xff0c;还是久经沙场多年的程序员老白&#xff0c;都是一如既往的SELECT count(*…

【探索 Kubernetes|作业管理篇 系列 11】控制器的核心功能

前言 大家好&#xff0c;我是秋意零。 上一篇结束了 Pod 对象的内容。 今天要探讨的内容是 “控制器”&#xff0c;它是 Kubernetes 编排最核心的功能。理解了 “控制器”&#xff0c;你就能理解 Deployment、StatefulSet、DaemontSet、Job、CroJob 控制器对象。 最近搞了一…

数据结构之复杂度分析

1、大 O 复杂度表示法 算法的执行效率&#xff0c;粗略地讲&#xff0c;就是算法代码执行的时间 这里有段非常简单的代码&#xff0c;求 1,2,3…n 的累加和。看如何来估算一下这段代码的执行时间 int cal(int n) {int sum 0;int i 1;for (; i < n; i) {sum sum i;}ret…

pg报错attempted to delete invisible tuple

问题描述 postgresql数据库执行delete报错&#xff1a;attempted to delete invisible tuple&#xff0c;执行同样条件的select不报错 delete from lzltab1; select count(*) from lzltab1;执行全表删除和全表查询的结果&#xff1a; M# delete from lzltab1; ERROR: 5500…

【Unity之IMGUI】—位置信息类和控件基类的封装

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a; ⭐…

从0开始,手写MySQL事务

说在前面&#xff1a;从0开始&#xff0c;手写MySQL的学习价值 尼恩曾经指导过的一个7年经验小伙&#xff0c;凭借精通Mysql, 搞定月薪40K。 从0开始&#xff0c;手写一个MySQL的学习价值在于&#xff1a; 可以深入地理解MySQL的内部机制和原理&#xff0c;Mysql可谓是面试的…

大数据Doris(五十):Export导出原理

文章目录 Export导出原理 一、原理 二、查询计划拆分 三、查询计划执行 Export导出原理 Doris Export、Select Into Outfile、MySQL dump三种方式数据导出。用户可以根据自己的需求导出数据。此外数据还可以以文件形式通过Borker备份到远端存储系统中&#xff0c;之后可以…

idea打的包字符集为GBK

1.最近对接一个打印机厂家的机器&#xff0c;他们对与打印数据要求字符集是UTF-8的&#xff0c;做完程序在自己idea上运行是是能够打印的&#xff0c;但是打包后&#xff0c;就不能够打印了。然后问了设备方是否是他们机器的原因&#xff0c;后面他们问了我这报错码&#xff0c…

【单片机】STM32单片机的各个定时器的定时中断程序,标准库

文章目录 定时器1_定时中断定时器2_定时中断定时器3_定时中断定时器4_定时中断定时器5_定时中断 高级定时器和普通定时器的区别&#xff08;https://zhuanlan.zhihu.com/p/557896041&#xff09;&#xff1a; 定时器1_定时中断 TIM1是高级定时器&#xff0c;使用的时钟总线是R…

Methodot低代码开发教程——玩转表格增删改查分页

目录 1、背景介绍 2、连接数据源 2.1 新增数据源 2.2 填写数据源信息 3、表格数据的展示 3.1 新增查询&#xff0c;编写查询语句 3.2 使用表格组件 3.3 同步数据源与表格列名 4、表格的数据新增 4.1 新增查询&#xff0c;编写新增语句 4.2 表格配置新增一行&#xff0…

探索嵌入式开发领域:单片机、ARM、Android底层的紧密联系

作为一个曾编写ARM教程和参与Android产品开发的专家&#xff0c;我发现单片机、ARM、嵌入式开发和Android底层开发之间存在紧密的联系。对于那些希望在嵌入式开发领域发展的人来说&#xff0c;了解这些领域的知识至关重要。为了帮助你更好地学习这些内容&#xff0c;我总结了一…

Git指南 - 刚提的commit 怎么找不到了(游离分支)?

在有一次使用git时&#xff0c;我提交commit后&#xff0c;并未push&#xff0c;然后直接切到了当前分支的某个tag&#xff0c;最后我想切回来的时候&#xff0c;竟然找不到我刚才提交commit的节点了… 关联篇 Git指南 - 你该掌握的那些基础认知和首次配置Git指南 - 项目实战中…

青大数据结构【2018】【综合应用】

关键字&#xff1a; 二叉排序树、先序中序排列、平均查找长度ASL、快速排序、堆排序 &#xff08;3&#xff09; 采用堆排序&#xff1b; 因为快速排序在基本有序&#xff08;逆序&#xff09;的情况下&#xff0c;达到最坏的时间复杂度O(n2)。

如何支持研发对CSDN个性化推荐系统重构

目录 大地图工具构建数据治理保持发布重视测试小结引用 一个以内容服务为主的软件&#xff0c;它的推荐系统在数据侧对软件产生着举足轻重的作用。数据的三个方面决定了这个内容软件的档次。 数据的质量好坏数据和用户需求的相关性好坏数据的层次体系好坏 通常&#xff0c;我…

如何使用ChatGPT处理excel

用ChatGPT处理excel&#xff0c;我们需要用到的主要工具是ChatGPT和vba代码。 VBA代码是一种用于Excel&#xff0c;Word&#xff0c;PPT的Microsoft Office软件的编程语言。 可以让用户通过编写一些简单的指令和操作&#xff0c;实现自动化、自定义和数据处理等功能。 就像你编…