SpringBoot之Banner源码深度分解
Banner:抽象接口,定义了Banner的打印方式。其中 SpringBootBanner:实现Banner接口,用于打印“Spring Boot”的图案。 ImageBanner:实现Banner接口,用于将图片文件(如banner.gif、banner.jpg等)转换为ASCII字符图案。 ResourceBanner:实现Banner接口,用于处理文本文件(如banner.txt)的资源文件。 Banners:S..A..BannerPrinter 的私有静态类,封装多个Banner的集合。 PrintedBanner:S..A..BannerPrinter 的私有静态类,封装资源和对应的Banner。 SpringApplicationBannerPrinter:核心类,负责与SpringApplication对接,实现Banner的打印逻辑。
发布日期:2025-05-05 12:30:18
浏览次数:4
分类:精选文章
本文共 8539 字,大约阅读时间需要 28 分钟。
Spring Boot Banner 架构原理
##Banner 的基本作用 Banner 更多地作为一种人性化的标志,例如企业的标志、某个知名产品的标志、不同环境的标志等。在Spring Boot中,Banner的设计理念强调“大道至简”,即将Banner的非功能需求和部分功能需求封装好,让用户只需按照最傻瓜的操作步骤使用即可。
每一个应用都应该有自己的Banner,独一无二的设计使其更具个性化。
常规使用方法
Spring Boot 提供了默认的配置方式,无需修改任何代码,只需在 resources 目录下添加 banner.txt 文件即可。
banner.txt 文件格式
文件内容如下:
.___ _ __ _ _ /\\ / ___'__ __ _ _(_)_ __ __ _ \ \ \\( ( )\___ | '_ | '_| | '_ \\/ _` | \ \ \\ \\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\\__, | / / / /=========|_|==============|___/=/_/_/_/:: Hello World :: ${application.formatted-version} 支持的变量参数
${AnsiColor.BRIGHT_RED}:设置控制台中输出内容的颜色${application.version}:获取MANIFEST.MF文件中的版本号${application.formatted-version}:格式化后的版本信息${spring-boot.version}:Spring Boot的版本号${spring-boot.formatted-version}:格式化后的Spring Boot版本号${application.title}:应用标题
类组织关系解析:Spring Boot JAR
所有与Banner相关的类都位于 spring-boot 包中,主要包括以下几个关键类:
Mode 枚举控制打印模式:LOG、CONSOLE、OFF。SpringApplication 类
private Banner printBanner(ConfigurableEnvironment environment) { if (this.bannerMode == Banner.Mode.OFF) { return null; } ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(getClassLoader()); 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);}public void setBanner(Banner banner) { this.banner = banner;}public void setBannerMode(Banner.Mode bannerMode) { this.bannerMode = bannerMode;} 关键点
setBanner(Banner)方法允许用户自定义Banner实现类,支持扩展。setBannerMode(Banner.Mode)方法控制Banner的打印模式,默认为CONSOLE。
SpringApplicationBannerPrinter 类
static final String BANNER_LOCATION_PROPERTY = "banner.location";static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location";static final String DEFAULT_BANNER_LOCATION = "banner.txt";static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };private static final Banner DEFAULT_BANNER = new SpringBootBanner();private final ResourceLoader resourceLoader;private final Banner fallbackBanner;SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) { this.resourceLoader = resourceLoader; this.fallbackBanner = fallbackBanner;}public Banner print(Environment environment, Class sourceClass) { Banner banner = getBanner(environment, this.fallbackBanner); try { logger.info(createStringFromBanner(banner, environment, sourceClass)); } catch (UnsupportedEncodingException ex) { logger.warn("Failed to create String for banner", ex); } return new PrintedBanner(banner, sourceClass);}public Banner print(Environment environment, PrintStream out) { Banner banner = getBanner(environment, this.fallbackBanner); banner.printBanner(environment, sourceClass, out); return new PrintedBanner(banner, sourceClass);}private Banner getBanner(Environment environment, Banner definedBanner) { 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;}private Banner getTextBanner(Environment environment) { String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION); Resource resource = this.resourceLoader.getResource(location); if (resource.exists()) { return new ResourceBanner(resource); } return null;}private Banner getImageBanner(Environment environment) { String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY); 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;}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("banner.charset", "UTF-8"); return baos.toString(charset);}private static class Banners implements Banner { private final List banners = new ArrayList<>(); public void addIfNotNull(Banner banner) { if (banner != null) { this.banners.add(banner); } } public boolean hasAtLeastOneBanner() { return !this.banners.isEmpty(); } @Override public void printBanner(Environment environment, Class sourceClass, PrintStream out) { for (Banner banner : this.banners) { banner.printBanner(environment, sourceClass, out); } }}private static class PrintedBanner implements Banner { private final Banner banner; private final Class sourceClass; PrintedBanner(Banner banner, Class sourceClass) { this.banner = banner; this.sourceClass = sourceClass; } @Override public void printBanner(Environment environment, Class sourceClass, PrintStream out) { sourceClass = (sourceClass == null ? this.sourceClass : sourceClass); this.banner.printBanner(environment, sourceClass, out); }} 关键点
- ImageBanner 和 ResourceBanner:两者可以同时存在,ImageBanner 会优先打印。
- Banners 和 PrintedBanner:封装多个Banner 的集合类,体现了工具类设计思想。
Banner 接口
public interface Banner { void printBanner(Environment environment, Class sourceClass, PrintStream out); enum Mode { OFF, CONSOLE, LOG }} 关键点
- @FunctionalInterface:允许使用Lambda表达式实现。
- Mode 枚举:定义了三种打印模式。
SpringBootBanner 类
/** * 默认的Banner实现类,用于打印“Spring Boot”的图案。 */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 + ")"); String padding = ""; while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) { padding += " "; } printStream.println(AnsiOutput.toString( AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding, AnsiStyle.FAINT, version)); printStream.println(); }} 关键点
- SpringBootBanner:默认实现,用于打印Spring Boot的图案。
- 版本信息:通过Spring Boot Version获取版本号,并附加到banner中。
ImageBanner 和 ResourceBanner
- ImageBanner:处理图片文件,根据配置项 banner.image.location 加载图片文件,支持 gif、jpg、png 等格式。
- ResourceBanner:处理文本文件,根据配置项 banner.location 加载 banner.txt 文件。
PrintedBanner 和 Banners 类
- PrintedBanner:封装Banner 实例和源类,用于存储和管理多个Banner。
- Banners:封装多个Banner 的集合,支持动态添加和打印。
总结
Spring Boot 的 Banner 架构设计体现了面向对象和面向切面编程的思想,通过封装和抽象将非功能需求和部分功能需求集中管理。特别值得一提的是 PrintedBanner 和 Banners 类的设计,它们通过私有静态类的方式实现了工具类的功能,充分发挥了OOP思想的优势。
技术的发展往往遵循一定的规律,从面向过程编程到面向对象编程,再到面向切面编程、面向服务编程,这些设计思想不断演化,体现了技术生产力的提升。
发表评论
最新留言
第一次来,支持一个
[***.219.124.196]2026年06月17日 02时04分12秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
php上传文件找不到临时文件夹
2023-02-28
PHP下curl用法分析
2023-02-28
php与web服务器关系
2023-02-28
redis事务操作
2023-02-28
PHP中array_merge和array相加的区别分析
2023-02-28
PHP中dirname(__FILE__)的意思
2023-02-28
PHP中extract()函数的妙用
2023-02-28
PHP中implode()和explode()
2023-02-28
PHP中serialize和json序列化与反序列化的区别
2023-02-28
Redis事务处理
2023-02-28
php中使用ajax进行前后端json数据交互
2023-02-28
Redis事务和锁操作
2023-02-28
PHP中如何得到数组的长度
2023-02-28
php中引入文件几种方式的区别
2023-02-28
PHP中把stdClass Object转array的几个方法
2023-02-28
PHP中替换换行符
2023-02-28
PHP中有关正则表达式的函数集锦
2023-02-28
Redis 集群搭建详细指南
2023-02-28
php中的cookie用法
2023-02-28
php中的session用法
2023-02-28