SpringBoot 自定义Banner

SpringBoot自定义Banner

SpringBoot项目启动的时候会输出一个Spring的图标。

image-20240121135746569

思考🤔

  • 它是在哪个环节的时候输出的呢?
  • 是否可以自定义图标呢?

源码分析

通常我们SpringBoot项目是通过SpringBoot.run(Appcation.class)启动的。

run()

public ConfigurableApplicationContext run(String... args) {
		try {
            // 关键是这两行 
            // 第一行是初始化环境信息也就是会将我们配置文件中的内容,或者是System.getProperties()中的信息进行封装
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            // 通过环节信息来构建Banner并输出对于的内容
			Banner printedBanner = printBanner(environment);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		return context;
}

printBanner()

private Banner.Mode bannerMode = Banner.Mode.CONSOLE;


private Banner printBanner(ConfigurableEnvironment environment) {
    	// 判断横幅的模式 是否为禁用 ,默认是输出到控制台
		if (this.bannerMode == Banner.Mode.OFF) {
			return null;
		}
		ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
				: new DefaultResourceLoader(null);
    	// 创建一个SpringApplicationBannerPrinter对象
		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.Mode

enum Mode {
		/**
		 * 禁用横幅的打印
		 */
		OFF,

		/**
		 * 将横幅输出到System.out
		 */
		CONSOLE,

		/**
		 * 将横幅输出到日志文件中
		 */
		LOG

	}

SpringApplicationBannerPrinter.print()

Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
    	// 根据环节信息创建一个Banner
		Banner banner = getBanner(environment);
    	// 实际输出Banner的地方
		banner.printBanner(environment, sourceClass, out);
		return new PrintedBanner(banner, sourceClass);
}

getBanner()

// 	private static final Banner DEFAULT_BANNER = new SpringBootBanner();
private Banner getBanner(Environment environment) {
		Banners banners = new Banners();
    	// 判断是否存在image的横幅
		banners.addIfNotNull(getImageBanner(environment));
    	// 判断是否存在文本的横幅
		banners.addIfNotNull(getTextBanner(environment));
		if (banners.hasAtLeastOneBanner()) {
			return banners;
		}
		if (this.fallbackBanner != null) {
			return this.fallbackBanner;
		}
    	// 这里如果在配置中没有得到Banner的话,则会使用SpringBoot默认的
		return DEFAULT_BANNER;
}
// 从当前环境中取出spring.banner.image.location 
// 判断这个路径下是否存在文件,存在则创建一个ImageBanner
// 不存在这尝试读取类路径下的banner.gif, banner.jpg", banner.png 
// 都不存在则返回一个NULL
private Banner getImageBanner(Environment environment) {
		String location = environment.getProperty("spring.banner.image.location");
		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;
}

// 从当前环境中取出spring.banner.location配置的值 默认是banner.txt 可以自定义。通过这个路径来创建一个ResourceBanner
private Banner getTextBanner(Environment environment) {
		String location = environment.getProperty("spring.banner.location", "banner.txt");
		Resource resource = this.resourceLoader.getResource(location);
		try {
			if (resource.exists() && !resource.getURL().toExternalForm().contains("liquibase-core")) {
				return new ResourceBanner(resource);
			}
		}
		catch (IOException ex) {
			// Ignore
		}
		return null;
	}

SpringBootBanner

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

}

这里可以看见BANNER是一个字符串数组,实际打印的也就是这个数组的值。也就是我们的Spring图标以及一些版本信息。

小结

从源码中分析得出,在不配置spring.banner.locationspring.banner.image.location的情况下,SpringBoot默认会使用SpringBootBanner来进行输出。输出的则是Spring的图标。

当我们想自定义图标的话,则可以使用spring.banner.location来指定一个横幅文件的位置,或者直接在类路径下创建一个banner.txt,并把横幅的内容写入到里面。

实践

创建一个banner.txt,并写入程序员万岁!;

输出效果

image-20240121145819663

感觉很low😂。如何让他变的跟Spring那个图标一样好看呢。

Spring Boot自定义启动Banner在线生成工具

image-20240121145834309

替换banner.tet内容,效果。

image-20240121145909209

补充

纯字体的网站

  • (推荐)https://www.bootschool.net/ascii

  • http://patorjk.com/software/taag/

  • http://www.network-science.de/ascii/

图片风

  • (推荐)https://www.bootschool.net/ascii-art/animals

  • https://www.degraeve.com/img2txt.php

内置变量

  • spring-boot.version:SpringBoot的版本号
  • spring-boot.formatted-version:带v的版本号

image-20240121150844817

总结

https://www.bootschool.net/ascii-art/animals

  • https://www.degraeve.com/img2txt.php

内置变量

  • spring-boot.version:SpringBoot的版本号
  • spring-boot.formatted-version:带v的版本号

[外链图片转存中…(img-kzLyUrAu-1705820973870)]

总结

直接在类路径下创建banner.txt来替换SpringBoot默认的输出最为方便,想要美化的话,可以通过一些工具来进行生成。

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

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

相关文章

【算法竞赛C++STL基础】栈,链表,队列,优先队列,map,set以及迭代器的用法

文章目录 1&#xff0c;前知——模板函数的实现2, hash 表1&#xff0c;定义2,ASCII码表3&#xff0c;咉射关系 3&#xff0c;迭代器4&#xff0c;STL关系1,stl 的基础关系2,stl 的分类1,相关分类2,相关简介顺序容器关联容器适配容器 3. STL 的相关函数的学习3.1 STL函数中都含…

buctoj——2024寒假集训 进阶训练赛 (五)

问题 A: 约瑟夫问题 题目描述 N个人围成一圈&#xff0c;从第一个人开始报数&#xff0c;数到M的人出圈&#xff1b;再由下一个人开始报数&#xff0c;数到M的人出圈&#xff1b;……输出依次出圈的人的编号。N&#xff0c;M由键盘输入。 输入 一行&#xff0c;包含两个正整数…

Linux 入门命令大全汇总 + Linux 集锦大全 【20240122】

文章目录 Linux 入门命令大全汇总Linux 集锦大全更多信息 Linux 入门命令大全汇总 别有一番风趣的alias 刚刚好合适的 apropos 命令 迷你计算器 bc 可看黄道吉日的 cal 全文可查看&#xff1a; Linux入门命令大全全文 Linux 集锦大全 linux终端中最漂亮的几款字体介绍及…

golang学习笔记——http.Handle和http.HandleFunc的区别与type func巧妙运用

文章目录 http.Handle和http.HandleFunc的区别http.Handle分析type func巧妙运用 http.HandleFunc分析总结参考资料 http.Handle和http.HandleFunc的区别 http.Handle和http.HandleFunc的区别体现了Go语言接口的巧妙运用 下面代码启动了一个 http 服务器&#xff0c;监听 808…

深入理解C语言(2):字符、字符串与内存函数

文章主题&#xff1a;字符、字符串与内存函数&#x1f30f;所属专栏&#xff1a;深入理解C语言&#x1f4d4;作者简介&#xff1a;更新有关深入理解C语言知识的博主一枚&#xff0c;记录分享自己对C语言的深入解读。&#x1f606;个人主页&#xff1a;[₽]的个人主页&#x1f3…

jdk17新特性——Switch表达式增强

目录 一、Switch表达式增强示例一1.1、传统的方式 case中变量赋值示例1.2、jdk17 case中变量赋值示例 二、Switch表达式增强示例二2.1、传统的方式 case中值匹配多个示例2.2、jdk17 case中值匹配多个示例 三、Switch表达式增强示例三3.1、传统的方式 case中需要多行业务代码示例…

React-Native项目矢量图标库(react-native-vector-icons)

系列文章目录 React-Native环境搭建&#xff08;IOS&#xff09;React-Native项目 — 关于IOS知识储备React-Native项目工程搭建&#xff08;开发模板搭建&#xff09;React-Native项目矢量图标库&#xff08;react-native-vector-icons&#xff09; 目录 系列文章目录前言一、…

斐波那契查找

斐波那契查找 概述步骤代码示例输出结果 概述 斐波那契查找是一种基于斐波那契数列的查找算法&#xff0c;用于在有序数组中查找目标元素的位置。与二分查找类似&#xff0c;斐波那契查找也是一种分治算法&#xff0c;它通过比较目标值与数组的中间元素来确定下一步的查找范围…

C++版QT:鼠标事件

鼠标常用的事件可以说有一下几种&#xff1a;鼠标按下、鼠标移动、鼠标移动、鼠标双击和鼠标滚轮事件。 当你想使用他们&#xff0c;需要包含头文件&#xff1a;#include <QMouseEvent> 需要对鼠标事件进行处理时&#xff0c;通常要重新实现以下几个鼠标事件处理函数&a…

设备对象(DEVICE_OBJECT)

设备对象(DEVICE_OBJECT) 每个驱动程序会创建一个或多个设备对象&#xff0c;用DEVICE_OBJECT数据结构表示。每个设备对象都会有一个指针指向下一个设备对象&#xff0c;因此就形成一个设备链。设备对象链的第一个设备是由DRIVER_OBJECT结构体中指明的。设备对象保存设…

UI自动化中的option选项配置

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

《WebKit 技术内幕》学习之七(2): 渲染基础

2 网页层次和RenderLayer树 2.1 层次和RenderLayer对象 前面章节介绍了网页的层次结构&#xff0c;也就是说网页是可以分层的&#xff0c;这有两点原因&#xff0c;一是为了方便网页开发者开发网页并设置网页的层次&#xff0c;二是为了WebKit处理上的便利&#xff0c;也就是…

C++中命名空间、缺省参数、函数重载

目录 1.命名空间 2.缺省参数 3.函数重载 1.命名空间 在C中定义命名空间我们需要用到namespace关键字&#xff0c;后面跟上命名空间的名字&#xff0c;结构框架有点类似结构体&#xff08;如图所示&#xff09; 上面的代码我一一进行讲解&#xff1a; 1.我们先来说第三行和main函…

开源堡垒机JumpServer本地安装并配置公网访问地址

文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机&#xff0c;是符合 4A 规范的专业运维安全审计系统。JumpS…

基于CLIP4Clip的DRL的WTI模块实现

关于DRL的WTI模块&#xff1a; Weighted Token-wise Interaction&#xff1a; 直觉上&#xff0c;并非所有的单词和视频帧都同等重要。我们提供一种自适应方法&#xff0c;来调整每个标记的权重大小&#xff1a; 注&#xff1a;其中两个f函数都是MLP和softmax构成。 WTI的算…

使用STM32的SPI接口实现与外部传感器的数据交互

一、引言 外部传感器是嵌入式系统中常用的外设&#xff0c;用于检测环境参数、采集数据等。通过STM32微控制器的SPI接口&#xff0c;可以与外部传感器进行数据交互&#xff0c;从而实现数据的采集和控制。本文将介绍如何使用STM32的SPI接口实现与外部传感器的数据交互&#xff…

云计算任务调度仿真05

今天再分享一个新的调度框架deeprm 本项目基于hongzimao/deeprm&#xff0c;原作者还著有论文Resource Management with Deep Reinforcement Learning 。 这个框架研究的也蛮多的&#xff0c;我在一篇博士论文中也看到了基于此的研究工作&#xff0c;但是论文题目忘记了。 运…

【C++】入门(一)

前言&#xff1a; 本篇博客将带大家认识C&#xff0c;熟悉基本语法 文章目录 认识CC的诞生与发展C 在行业中的运用 一、命名空间1.1 命名空间的定义1.2 命名空间的使用1.3 命名空间的访问 二、C输入&输出输出操作符 <<输入操作符 >>换行符和刷新输出缓冲区关键…

如何在CentOS8使用宝塔面板本地部署Typecho个人网站并实现公网访问【内网穿透】

文章目录 前言1. 安装环境2. 下载Typecho3. 创建站点4. 访问Typecho5. 安装cpolar6. 远程访问Typecho7. 固定远程访问地址8. 配置typecho 前言 Typecho是由type和echo两个词合成的&#xff0c;来自于开发团队的头脑风暴。Typecho基于PHP5开发&#xff0c;支持多种数据库&#…

vmware 安装Rocky-9.3系统

安装系统截图 安装完成&#xff0c;启动 查看版本和内核 开启远程登陆授权 1、编辑配置文件 #提升权限&#xff0c;输入su,并输入密码 su #编辑ssh文件开启root远程登陆 vi /etc/ssh/sshd_config找到以下内容&#xff1a;#PermitRootLogin prohibit-password 添加&#xff1a…