您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

SpringBoot启动控制台的banner是怎么回事

时间:2023-03-13 11:46:17  来源:今日头条  作者:马士兵教育CTO


 

前言

每次启动SpringBoot项目时,总是能看到控制台打印了一串字符,隐约能辨认出是“Spring”,不知大家是否也好奇过是怎么实现的,是直接打印固定的字符串,还是根据什么算法去生成的?于是闲暇无事,探究一番。


 

 

只想修改banner可以跳到文末查看
SpringBoot是怎么打印的 Banner默认实现类 SpringBootBanner

 

1、根据控制台打印的字符进行全局搜索,笔者选取:: Spring Boot ::进行搜索,定位到了org.springframework.boot.SpringBootBanner。

 

IDEA全局搜索:CTRL + SHIFT + R

 


 

2、进入SpringBootBanner类,先看下注释Default Banner implementation which writes the 'Spring' banner.,说了两个信息:1、当前类是SpringBoot Banner的默认实现;2、打印的字符是“Spring”。

3、往下看,SpringBootBanner实现了Banner接口。Banner包括printBanner方法和枚举Mode。
根据Mode中的注释和枚举值可以看出,Banner有三种状态:关闭、打印到控制台、打印到日志。具体使用场景留待后续分析。

Banner源码

4、往下看到类的属性BANNER和SPRING_BOOT,也能辨认出是控制台打印的那些字符。

类里面只有一个方法printBanner,负责打印Banner字符。逻辑比较清晰,第一部分逐行打印BANNER形成图案;第二部分打印SpringBoot版本号,总长度由STRAP_LINE_SIZE控制。

SpringBootBanner完整代码 Banner核心控制类 SpringApplicationBannerPrinter

1、上节找到了负责存储和打印Banner字符的类SpringBootBanner,现在向调用链上方继续寻找,通过CTRL + B或者全局搜索可以发现SpringBootBanner在SpringApplicationBannerPrinter类中作为类变量,大概能猜测出这个SpringApplicationBannerPrinter类是Banner打印的核心控制器。

2、进入SpringApplicationBannerPrinter类,照例先看注释Class used by SpringApplication to print the application banner.,意思是当前类被SpringApplication用来打印banner。

这个SpringApplication好像有点眼熟,名字和我们SpringBoot项目的启动类有点相似,翻翻启动类的代码,想起我们就是通过SpringApplication的run方法启动项目,banner打印调用也是由SpringApplication控制的,后续会详细分析。(占坑,后续SpringBoot启动流程也会出一篇博客去探讨一下)


 

回归正题,继续从类的属性开始看,根据名字猜测大概含义,留待后续验证:

 

  1. BANNER_LOCATION_PROPERTY:Spring配置,大概是banner文件的路径。
  2. BANNER_IMAGE_LOCATION_PROPERTY:Spring配置,banner图片的路径(存疑,控制台难道能打印图片?)。
  3. DEFAULT_BANNER_LOCATION = "banner.txt":取值是txt文件,猜测是banner文件的默认位置。
  4. String[] IMAGE_EXTENSION = { "gif", "jpg", "png" }:取值是常见图片的后缀,结合第二个属性猜测是用来对banner图片类型做限制。
  5. DEFAULT_BANNER = new SpringBootBanner():把上节分析的SpringBootBanner当做Banner默认实现类
  6. ResourceLoader resourceLoader:ResourceLoader简单来说是Spring加载资源的统一抽象,由实现类提供具体逻辑。
    在Spring中读取xml配置文件加载应用上下文的ClassPathXmlApplicationContext,就是ResourceLoader的子类。
  7. Banner fallbackBanner:翻译过来是回退banner,暂时猜不出作用,等待后续填坑。

     

     

 

3、往下看方法,只有两个非私有方法,都是print的重载方法,差别在于第三个参数,分别是Log logger和PrintStream out,代表这两个方法分别负责日志打印和控制台打印。

紧扣主题,先看负责控制台打印的方法。

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

代码很精简,第一行获取Banner类,第二行调用Banner的print方法打印banner图案,最后生成PrintedBanner并返回。

1. getBanner

getBanner源码

查看getBanner方法,首先创建Banners,底层就是Banner数组,由于存在控制台、日志两种打印方式,使用此类方便批量处理。

Banners源码

接着就是调用getImageBanner和getTextBanner方法获取Banner,如果Banner数组不为空则返回,否则检查fallbackBanner。

这个fallbackBanner光看名字看不出是什么,使用CTRL+B查看引用,发现是在SpringApplication#printBanner里注入进来的,如下图。


 

继续查找this.banner会发现,最终Banner只能通过SpringApplicationBuilder#banner注入。


 

SpringApplicationBuilder是通过Constructor(构造器)模式实现的SpringApplication构造器。
查看banner方法的注释,我们可以知道这里注入的Banner实例会在没有静态banner文件时使用
回过头来,fallbackBanner的坑填上了,它是在SpringApplicationBannerPrinter找不到txt文件或者图片作为banner素材的时候使用。

如果fallbackBanner也为空,则最终返回兜底方案-SpringBootBanner。

getBanner的结构分析完了,实际情况我们知道走的是兜底方案,也就是只要我们能让getImageBanner、getTextBanner或者fallbackBanner不为空,就能改变banner打印的图案。
带着这个想法,我们就去看看getImageBanner和getTextBanner是咋回事。

2、getImageBanner
查看源码,首先environment.getProperty读取配置spring.banner.image.location获取图片位置。

配置文件读取若为空则遍历图片后缀数组IMAGE_EXTENSION,采用"banner." + ext拼接方式得到图片相对路径,并尝试加载。加载成功后会生成ImageBanner并返回。

接收图片资源并处理打印的逻辑都封装在ImageBanner中,后续单独写一篇文章尝试分析图片打印逻辑。

按照我们的分析,只要在配置文件中添加spring.banner.image.location并赋值正确的图片路径,或者在resources目录下存放一张名字为“banner”、后缀是gif,jpg, png其中之一的图片,SpringApplicationBannerPrinter就会打印出来。
注: 为什么没加前缀classpath:也可以放在resources目录下,可以查看DefaultResourceLoader#getResource对于banner.jpg这种location的处理逻辑。

后续章节会有打印效果。

getImageBanner源码

3、getTextBanner
查看源码,同样是先从配置文件中读取banner文件的location并尝试加载资源,和getImageBanner不同的是,这里读取不到会使用默认值banner.txt。

加载资源后有一个Resource的限制条件!resource.getURL().toExternalForm().contAIns("liquibase-core"),这里不明白这个条件的含义,只查询到了Liquibase是一个用于跟踪、管理和应用数据库变化的开源工具。

资源校验通过后生成ResourceBanner并返回。

getTextBanner源码

接下来进入ResourceBanner看下打印细节。
printBanner结构比较简单,第一部分设置banner字符集,优先读取配置spring.banner.charset,无配置则默认设置为UTF-8。
第二部分去解析banner字符,比如将${xxx}占位符解析成实际的值。
第三部分就是调用流打印输出。

ResourceBanner#printBanner banner打印调用方-SpringApplication

上节看完SpringApplicationBannerPrinter,这节来寻找打印banner的调用方。

CTRL+B查看SpringApplicationBannerPrinter#print的引用,定位到了SpringApplication#printBanner。源码如下。

从整体结构来看,printBanner方法根据this.bannerMode取值不同,执行不同的打印策略:不打印、打印到日志、打印到控制台。

 

那么这个bannerMode是怎么设置的?查看初始化的代码,默认值是CONSOLE。 继续寻找,最终定位到了SpringApplicationBuilder#bannerMode,意味着bannerMode只能通过构造器进行注入。

 

继续寻找printBanner的调用方,定位到了SpringApplication#run(String...)。

上面有提到过,通常我们SpringBoot项目都是去调用SpringApplication#run(Class, String...)去启动项目,底层是通过new关键字创建SpringApplication对象,最后调用SpringApplication#run(String...)完成一系列的资源初始化。

所以这就可以解释大多数情况下,我们的SpringBoot项目启动时都会打印那个默认的“Spring”字符。

SpringApplication#printBanner源码

如何修改项目启动的banner 修改banner打印策略

经上分析,banner打印策略包括控制台日志不打印

1. 隐式
默认策略是控制台,只需大多数情况一样,项目启动类通过SpringApplication.run(DistinctAppUserServiceApplication.class, args);启动,无需指定。

2. 显式注入
通过SpringApplicationBuilder构造器显式注入banner打印策略。

@SpringBootApplicationpublic class DemoApplication { public static void main(String[] args) { new SpringApplicationBuilder(DemoApplication.class) // Banner.Mode.LOG 打印到日志 // Banner.Mode.OFF 不打印 .bannerMode(Banner.Mode.CONSOLE) .run(args); }}

打印效果
打印到控制台


 

打印到日志:INFO级别


 

修改banner内容 文本

方式一:在src/main/resources下新建banner.txt,里面放入想要打印的内容即可。

方式二:修改配置文件

spring: banner: location: file/bannerText.txt #文件位置 src/main/resources/file/bannerText.txt 图片

和文本方式相同,但是图片类型有限制,只能是以下三种gif,、jpg、png。
方式一:在src/main/resources下新建banner.png,里面放入想要打印的内容即可。

方式二:修改配置文件

spring: banner: image: location: file/bannerImage.png #文件位置 src/main/resources/file/bannerImage.png

打印效果



Tags:SpringBoot   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
详解基于SpringBoot的WebSocket应用开发
在现代Web应用中,实时交互和数据推送的需求日益增长。WebSocket协议作为一种全双工通信协议,允许服务端与客户端之间建立持久性的连接,实现实时、双向的数据传输,极大地提升了用...【详细内容】
2024-01-30  Search: SpringBoot  点击:(17)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19  Search: SpringBoot  点击:(86)  评论:(0)  加入收藏
SpringBoot3+Vue3 开发高并发秒杀抢购系统
开发高并发秒杀抢购系统:使用SpringBoot3+Vue3的实践之旅随着互联网技术的发展,电商行业对秒杀抢购系统的需求越来越高。为了满足这种高并发、高流量的场景,我们决定使用Spring...【详细内容】
2024-01-14  Search: SpringBoot  点击:(91)  评论:(0)  加入收藏
公司用了六年的 SpringBoot 项目部署方案,稳得一批!
本篇和大家分享的是springboot打包并结合shell脚本命令部署,重点在分享一个shell程序启动工具,希望能便利工作。 profiles指定不同环境的配置 maven-assembly-plugin打发布压...【详细内容】
2024-01-10  Search: SpringBoot  点击:(163)  评论:(0)  加入收藏
简易版的SpringBoot是如何实现的!!!
SpringBoot作为目前最流行的框架之一,同时是每个程序员必须掌握的知识,其提供了丰富的功能模块和开箱即用的特性,极大地提高了开发效率和降低了学习成本,使得开发人员能够更专注...【详细内容】
2023-12-29  Search: SpringBoot  点击:(135)  评论:(0)  加入收藏
用 SpringBoot+Redis 解决海量重复提交问题
前言 一:搭建redis的服务Api 二:自定义注解AutoIdempotent 三:token创建和检验 四:拦截器的配置 五:测试用例 六:总结前言:在实际的开发项目中,一个对外暴露的接口往往会面临很多...【详细内容】
2023-12-20  Search: SpringBoot  点击:(53)  评论:(0)  加入收藏
SpringBoot中如何优雅地个性化定制Jackson
当使用 JSON 格式时,Spring Boot 将使用ObjectMapper实例来序列化响应和反序列化请求。在本教程中,我们将了解配置序列化和反序列化选项的最常用方法。一、默认配置默认情况下...【详细内容】
2023-12-20  Search: SpringBoot  点击:(132)  评论:(0)  加入收藏
springboot-如何集成Validation进行参数校验
一、步骤概览 二、步骤说明1.引入依赖包在 pom.xml 文件中引入 validation 组件,它提供了在 Spring Boot 应用程序中进行参数校验的支持。<!-- WEB 程序依赖包 --><dependen...【详细内容】
2023-12-13  Search: SpringBoot  点击:(157)  评论:(0)  加入收藏
优雅的springboot参数校验,你学会了吗?
前言在后端的接口开发过程,实际上每一个接口都或多或少有不同规则的参数校验,有一些是基础校验,如非空校验、长度校验、大小校验、格式校验;也有一些校验是业务校验,如学号不能重...【详细内容】
2023-11-29  Search: SpringBoot  点击:(200)  评论:(0)  加入收藏
Springboot扩展点之BeanDefinitionRegistryPostProcessor,你学会了吗?
前言通过这篇文章来大家分享一下,另外一个Springboot的扩展点BeanDefinitionRegistryPostProcessor,一般称这类扩展点为容器级后置处理器,另外一类是Bean级的后置处理器;容器级...【详细内容】
2023-11-27  Search: SpringBoot  点击:(175)  评论:(0)  加入收藏
▌简易百科推荐
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  京东云开发者    Tags:Web Components   点击:(8)  评论:(0)  加入收藏
Kubernetes 集群 CPU 使用率只有 13% :这下大家该知道如何省钱了
作者 | THE STACK译者 | 刘雅梦策划 | Tina根据 CAST AI 对 4000 个 Kubernetes 集群的分析,Kubernetes 集群通常只使用 13% 的 CPU 和平均 20% 的内存,这表明存在严重的过度...【详细内容】
2024-03-08  InfoQ    Tags:Kubernetes   点击:(12)  评论:(0)  加入收藏
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  风舞凋零叶    Tags:Spring Security   点击:(54)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  贝格前端工场    Tags:框架   点击:(47)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  程序员wayn  微信公众号  Tags:Spring   点击:(39)  评论:(0)  加入收藏
开发者的Kubernetes懒人指南
你可以将本文作为开发者快速了解 Kubernetes 的指南。从基础知识到更高级的主题,如 Helm Chart,以及所有这些如何影响你作为开发者。译自Kubernetes for Lazy Developers。作...【详细内容】
2024-02-01  云云众生s  微信公众号  Tags:Kubernetes   点击:(50)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  大噬元兽  微信公众号  Tags:框架   点击:(68)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  HELLO程序员  微信公众号  Tags:Spring   点击:(86)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19   Java中文社群  微信公众号  Tags:SpringBoot   点击:(86)  评论:(0)  加入收藏
花 15 分钟把 Express.js 搞明白,全栈没有那么难
Express 是老牌的 Node.js 框架,以简单和轻量著称,几行代码就可以启动一个 HTTP 服务器。市面上主流的 Node.js 框架,如 Egg.js、Nest.js 等都与 Express 息息相关。Express 框...【详细内容】
2024-01-16  程序员成功  微信公众号  Tags:Express.js   点击:(88)  评论:(0)  加入收藏
站内最新
站内热门
站内头条