玩转 Spring Boot Banner:从默认 Logo 到炫酷启动画面
你是否厌倦了每次启动Spring Boot应用时那个千篇一律的"SPRING" ASCII艺术字?
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.5)
今天教你如何打造独一无二的启动Banner,让应用启动更具个性!
一、Spring Boot Banner基础
1. 什么是 Banner ?
Banner是Spring Boot应用启动时在控制台显示的艺术化文本。它不仅仅是装饰,还能传递重要信息:
- 应用标识:显示应用名称、版本
- 环境信息:区分开发、测试、生产环境
- 启动状态:展示启动进度和状态
- 团队文化:体现团队的技术文化
2. Banner的显示时机
Banner在应用启动的早期阶段显示,具体在SpringApplication的printBanner方法中:
// SpringApplication.java
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
Banner printBanner = getBanner(environment);
if (this.bannerMode == Banner.Mode.CONSOLE) {
printBanner.printBanner(environment, this.mainApplicationClass, System.out);
}
return printBanner;
}
二、Banner 定制方式大全
Spring Boot提供了多种Banner定制方式,按优先级从高到低:
方式1:文本文件Banner(最常用)
在src/main/resources目录下创建banner.txt文件:
_______ _______ _______ _______ _______ _______ _______
| || || || || || || |
| _ || ___|| ___||_ _|| ___|| _____||_ _|
| | | || | __ | |___ | | | |___ | |_____ | |
| |_| || || || ___| | | | ___||_____ | | |
| || |_| || |___ | | | |___ _____| | | |
|_______||_______||_______| |___| |_______||_______| |___|
:: 订单服务 :: (v${application.version})
环境: ${spring.profiles.active:default}
启动时间: ${application.startup-date}
作者: 技术研发部
实际运行效果:
_______ _______ _______ _______ _______ _______ _______
| || || || || || || |
| _ || ___|| ___||_ _|| ___|| _____||_ _|
| | | || | __ | |___ | | | |___ | |_____ | |
| |_| || || || ___| | | | ___||_____ | | |
| || |_| || |___ | | | |___ _____| | | |
|_______||_______||_______| |___| |_______||_______| |___|
:: 订单服务 :: (v1.0.0)
环境: dev
启动时间: 2024-05-20 10:00:00
作者: 技术研发部
支持的内置变量:
${application.version}:应用版本(来自pom.xml或build.gradle)${application.formatted-version}:格式化版本${spring-boot.version}:Spring Boot版本${spring-boot.formatted-version}:格式化Spring Boot版本${application.title}:应用名称
方式2:图片 Banner(图形化展示)
Spring Boot支持将图片转换为ASCII艺术字。支持的格式:jpg、png、gif
步骤1:在src/main/resources放置图片banner.png
步骤2:配置图片转换参数:
# application.yml
spring:
banner:
image:
location: classpath:banner.png # 图片位置
width: 76 # 宽度(字符)
height: 20 # 高度(字符)
pixel-mode: block # 像素模式:block, text, default
invert: false # 是否反色
margin: 2 # 边距
实际效果:图片会被转换为ASCII字符在控制台显示。
方式3:编程式 Banner(最灵活)
通过实现Banner接口,完全控制Banner的生成:
// CustomBanner.java
package com.example.demo.banner;
import org.springframework.boot.Banner;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.io.PrintStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Component
public class CustomBanner implements Banner {
private static final String[] BANNER = {
"╔══════════════════════════════════════════════════════════════╗",
"║ ║",
"║ ███████╗██████╗ ██╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ║",
"║ ██╔════╝██╔══██╗██║████╗ ██║██╔════╝ ██╔═══██╗██╔══██╗ ║",
"║ ███████╗██████╔╝██║██╔██╗ ██║██║ ██║ ██║██████╔╝ ║",
"║ ╚════██║██╔═══╝ ██║██║╚██╗██║██║ ██║ ██║██╔══██╗ ║",
"║ ███████║██║ ██║██║ ╚████║╚██████╗ ╚██████╔╝██║ ██║ ║",
"║ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ║",
"║ ║",
"╚══════════════════════════════════════════════════════════════╝"
};
@Override
public void printBanner(Environment environment,
Class<?> sourceClass,
PrintStream out) {
// 打印Banner主体
for (String line : BANNER) {
out.println(line);
}
// 打印动态信息
String appName = environment.getProperty("spring.application.name", "Unknown");
String version = environment.getProperty("application.version", "1.0.0");
String activeProfiles = String.join(",", environment.getActiveProfiles());
if (activeProfiles.isEmpty()) {
activeProfiles = "default";
}
String now = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 使用ANSI颜色
String reset = "\u001B[0m";
String cyan = "\u001B[36m";
String green = "\u001B[32m";
String yellow = "\u001B[33m";
out.println();
out.println(cyan + " ✨ " + appName + " v" + version + reset);
out.println(green + " 🌍 环境: " + activeProfiles + reset);
out.println(yellow + " 🕐 启动: " + now + reset);
out.println(cyan + " 🚀 Spring Boot " +
environment.getProperty("spring-boot.version", "3.2.5") + reset);
// 打印系统信息
Runtime runtime = Runtime.getRuntime();
out.println(green + " 💻 内存: " +
(runtime.totalMemory() / 1024 / 1024) + "MB" + reset);
out.println();
}
}
运行效果:
╔══════════════════════════════════════════════════════════════╗
║ ║
║ ███████╗██████╗ ██╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ║
║ ██╔════╝██╔══██╗██║████╗ ██║██╔════╝ ██╔═══██╗██╔══██╗ ║
║ ███████╗██████╔╝██║██╔██╗ ██║██║ ██║ ██║██████╔╝ ║
║ ╚════██║██╔═══╝ ██║██║╚██╗██║██║ ██║ ██║██╔══██╗ ║
║ ███████║██║ ██║██║ ╚████║╚██████╗ ╚██████╔╝██║ ██║ ║
║ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ║
║ ║
╚══════════════════════════════════════════════════════════════╝
✨ order-service v1.0.0
🌍 环境: dev
🕐 启动: 2024-05-20 10:00:00
🚀 Spring Boot 3.2.5
💻 内存: 256MB
方式4:多环境Banner(企业级实践)
不同环境使用不同的Banner,便于快速识别:
src/main/resources/
├── banner-dev.txt # 开发环境
├── banner-test.txt # 测试环境
├── banner-prod.txt # 生产环境
└── banner.txt # 默认(可选)
banner-dev.txt(开发环境-绿色):
${Ansi.GREEN}
██████╗ ███████╗██╗ ██╗
██╔══██╗██╔════╝██║ ██║
██║ ██║█████╗ ██║ ██║
██║ ██║██╔══╝ ╚██╗ ██╔╝
██████╔╝███████╗ ╚████╔╝
╚═════╝ ╚══════╝ ╚═══╝
${Ansi.RESET}
:: 开发环境 :: 请谨慎操作!
应用: ${application.title}
版本: ${application.version}
时间: ${application.startup-date}
banner-prod.txt(生产环境-红色警告):
${Ansi.RED}${Ansi.BOLD}
╔══════════════════════════════════════════════════╗
║ 🚨 生产环境 🚨 ║
║ 所有操作将影响真实用户,请务必确认! ║
╚══════════════════════════════════════════════════╝
${Ansi.RESET}
应用: ${application.title}
版本: ${application.version}
时间: ${application.startup-date}
服务器: ${server.address}:${server.port}
三、高级定制技巧
1. 使用 ANSI 颜色和样式
Spring Banner支持ANSI转义序列,让Banner更加丰富多彩:
${AnsiColor.BRIGHT_CYAN}${AnsiStyle.BOLD}
_____ _ _ _
| __ (_) | | (_)
| |__) | _ __ __ _| |_ _ ___ _ __
| ___/ | '_ \ / _` | __| |/ _ \| '_ \
| | | | | | | (_| | |_| | (_) | | | |
|_| |_|_| |_|\__,_|\__|_|\___/|_| |_|
${AnsiStyle.NORMAL}${AnsiColor.DEFAULT}
${AnsiColor.GREEN}应用: ${application.title}${AnsiColor.DEFAULT}
${AnsiColor.YELLOW}版本: ${application.version}${AnsiColor.DEFAULT}
${AnsiColor.BLUE}时间: ${application.startup-date}${AnsiColor.DEFAULT}
${AnsiColor.MAGENTA}环境: ${spring.profiles.active:default}${AnsiColor.DEFAULT}
支持的ANSI属性:
- 颜色:
BLACK,RED,GREEN,YELLOW,BLUE,MAGENTA,CYAN,WHITE - 亮度:
BRIGHT_前缀(如BRIGHT_RED) - 样式:
BOLD,FAINT,ITALIC,UNDERLINE,BLINK_SLOW,BLINK_FAST,NORMAL
2. 动态 Banner:显示实时信息
创建动态Banner,显示实时系统状态:
// DynamicBanner.java
package com.example.demo.banner;
import org.springframework.boot.Banner;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Component
public class DynamicBanner implements Banner {
@Override
public void printBanner(Environment environment,
Class<?> sourceClass,
PrintStream out) {
try {
// 获取系统信息
String hostname = InetAddress.getLocalHost().getHostName();
String ip = InetAddress.getLocalHost().getHostAddress();
Runtime runtime = Runtime.getRuntime();
RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
List<String> jvmArgs = runtimeMxBean.getInputArguments();
// 获取应用信息
String appName = environment.getProperty("spring.application.name", "Unknown");
String version = environment.getProperty("application.version", "1.0.0");
String activeProfiles = String.join(",", environment.getActiveProfiles());
// ANSI颜色代码
final String RESET = "\u001B[0m";
final String CYAN = "\u001B[36m";
final String GREEN = "\u001B[32m";
final String YELLOW = "\u001B[33m";
final String BLUE = "\u001B[34m";
final String MAGENTA = "\u001B[35m";
final String BOLD = "\u001B[1m";
// 打印Banner框架
out.println(CYAN + "╔══════════════════════════════════════════════════════════════╗" + RESET);
out.println(CYAN + "║" + RESET + BOLD + " 📊 实时系统监控面板 " + RESET + CYAN + "║" + RESET);
out.println(CYAN + "╠══════════════════════════════════════════════════════════════╣" + RESET);
// 应用信息
out.println(CYAN + "║" + RESET + " 📦 " + GREEN + "应用: " + appName + " v" + version + RESET);
out.println(CYAN + "║" + RESET + " 🏷️ " + YELLOW + "环境: " + activeProfiles + RESET);
// 系统信息
out.println(CYAN + "║" + RESET + " 💻 " + BLUE + "主机: " + hostname + " (" + ip + ")" + RESET);
out.println(CYAN + "║" + RESET + " ⏱️ " + MAGENTA + "PID: " + getProcessId() + RESET);
out.println(CYAN + "║" + RESET + " 🧵 " + CYAN + "JVM: " +
runtimeMxBean.getVmName() + " " + runtimeMxBean.getVmVersion() + RESET);
// 内存信息
long totalMem = runtime.totalMemory() / 1024 / 1024;
long freeMem = runtime.freeMemory() / 1024 / 1024;
long usedMem = totalMem - freeMem;
out.println(CYAN + "║" + RESET + " 💾 " + GREEN + "内存: " + usedMem + "MB/" + totalMem + "MB" + RESET);
// 启动参数(只显示前3个)
if (!jvmArgs.isEmpty()) {
out.println(CYAN + "║" + RESET + " ⚙️ " + YELLOW + "JVM参数: " +
String.join(" ", jvmArgs.subList(0, Math.min(3, jvmArgs.size()))) +
(jvmArgs.size() > 3 ? " ..." : "") + RESET);
}
// 时间信息
String now = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
out.println(CYAN + "║" + RESET + " 🕐 " + BLUE + "时间: " + now + RESET);
out.println(CYAN + "╚══════════════════════════════════════════════════════════════╝" + RESET);
out.println();
} catch (UnknownHostException e) {
out.println("⚠️ 无法获取主机信息");
}
}
private String getProcessId() {
try {
String jvmName = ManagementFactory.getRuntimeMXBean().getName();
return jvmName.split("@")[0];
} catch (Exception e) {
return "未知";
}
}
}
运行效果:
╔══════════════════════════════════════════════════════════════╗
║ 📊 实时系统监控面板 ║
╠══════════════════════════════════════════════════════════════╣
║ 📦 应用: order-service v1.0.0
║ 🏷️ 环境: dev
║ 💻 主机: server-01 (192.168.1.100)
║ ⏱️ PID: 12345
║ 🧵 JVM: OpenJDK 64-Bit Server VM 17.0.8
║ 💾 内存: 128MB/256MB
║ ⚙️ JVM参数: -Xmx512m -Xms256m -Dspring.profiles.active=dev
║ 🕐 时间: 2024-05-20 10:00:00
╚══════════════════════════════════════════════════════════════╝
3. Banner生成工具
手动创建ASCII艺术很麻烦?使用在线工具生成:
- patorjk.com/taag - 最流行的ASCII艺术生成器
- www.kammerl.de/ascii/AsciiSignature.php - 多种字体风格
- www.ascii-art-generator.org - 图片转ASCII
Java代码生成Banner:
// BannerGenerator.java
package com.example.demo.banner;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class BannerGenerator {
/**
* 生成自定义ASCII艺术字
*/
public List<String> generateAsciiArt(String text, ArtStyle style) {
List<String> lines = new ArrayList<>();
switch (style) {
case BLOCK:
lines.add("╔══════════════════════════════════════╗");
lines.add("║ ║");
lines.add("║ " + text.toUpperCase() + " ║");
lines.add("║ ║");
lines.add("╚══════════════════════════════════════╝");
break;
case SIMPLE:
lines.add("┌──────────────────────────────────────┐");
lines.add("│ │");
lines.add("│ " + text + " │");
lines.add("│ │");
lines.add("└──────────────────────────────────────┘");
break;
case BOLD:
lines.add("██████╗ █████╗ ███╗ ██╗███╗ ██╗███████╗██████╗ ");
lines.add("██╔══██╗██╔══██╗████╗ ██║████╗ ██║██╔════╝██╔══██╗");
lines.add("██████╔╝███████║██╔██╗ ██║██╔██╗ ██║█████╗ ██████╔╝");
lines.add("██╔══██╗██╔══██║██║╚██╗██║██║╚██╗██║██╔══╝ ██╔══██╗");
lines.add("██████╔╝██║ ██║██║ ╚████║██║ ╚████║███████╗██║ ██║");
lines.add("╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝");
break;
}
return lines;
}
/**
* 生成包含系统信息的Banner
*/
public String generateSystemBanner(String appName, String version,
String profile, String startupTime) {
return String.format("""
╔══════════════════════════════════════════════════╗
║ %-20s ║
╠══════════════════════════════════════════════════╣
║ 应用: %-30s ║
║ 版本: %-30s ║
║ 环境: %-30s ║
║ 时间: %-30s ║
╚══════════════════════════════════════════════════╝
""",
"Spring Boot 3.2",
appName,
version,
profile,
startupTime
);
}
public enum ArtStyle {
BLOCK, SIMPLE, BOLD
}
}
四、企业级最佳实践
1. 统一 Banner 规范
在大型组织中,制定统一的Banner规范:
// 企业级Banner规范示例
public class CorporateBanner implements Banner {
// 公司标准颜色
private static final String COMPANY_COLOR = "\u001B[38;2;0;102;204m"; // RGB: #0066CC
private static final String WARNING_COLOR = "\u001B[38;2;255;102;102m"; // RGB: #FF6666
private static final String SUCCESS_COLOR = "\u001B[38;2;102;204;102m"; // RGB: #66CC66
private static final String RESET = "\u001B[0m";
@Override
public void printBanner(Environment environment,
Class<?> sourceClass,
PrintStream out) {
// 1. 公司Logo
printCompanyLogo(out);
// 2. 应用信息
printApplicationInfo(environment, out);
// 3. 安全警告(生产环境)
if (isProduction(environment)) {
printSecurityWarning(out);
}
// 4. 合规声明
printComplianceStatement(out);
out.println();
}
private void printCompanyLogo(PrintStream out) {
out.println(COMPANY_COLOR);
out.println(" ███████╗██████╗ ██╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ");
out.println(" ██╔════╝██╔══██╗██║████╗ ██║██╔════╝ ██╔═══██╗██╔══██╗");
out.println(" ███████╗██████╔╝██║██╔██╗ ██║██║ ██║ ██║██████╔╝");
out.println(" ╚════██║██╔═══╝ ██║██║╚██╗██║██║ ██║ ██║██╔══██╗");
out.println(" ███████║██║ ██║██║ ╚████║╚██████╗ ╚██████╔╝██║ ██║");
out.println(" ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝");
out.println(RESET);
}
private void printApplicationInfo(Environment environment, PrintStream out) {
String appName = environment.getProperty("spring.application.name", "N/A");
String version = environment.getProperty("application.version", "N/A");
String profile = String.join(",", environment.getActiveProfiles());
out.println(COMPANY_COLOR + " ═══════════════════════════════════════════════" + RESET);
out.println(" 应用名称: " + appName);
out.println(" 版本号: v" + version);
out.println(" 运行环境: " + profile);
out.println(" 启动时间: " + java.time.LocalDateTime.now());
out.println(COMPANY_COLOR + " ═══════════════════════════════════════════════" + RESET);
}
private boolean isProduction(Environment environment) {
return environment.acceptsProfiles(org.springframework.core.env.Profiles.of("prod"));
}
private void printSecurityWarning(PrintStream out) {
out.println();
out.println(WARNING_COLOR + " ⚠️ ⚠️ ⚠️ 生产环境警告 ⚠️ ⚠️ ⚠️" + RESET);
out.println(WARNING_COLOR + " 此系统为生产环境,所有操作将被审计" + RESET);
out.println(WARNING_COLOR + " 未经授权的访问将承担法律责任" + RESET);
out.println();
}
private void printComplianceStatement(PrintStream out) {
out.println(SUCCESS_COLOR + " ✅ 已通过ISO 27001信息安全认证" + RESET);
out.println(SUCCESS_COLOR + " ✅ 符合GDPR数据保护规范" + RESET);
}
}
2. Banner 配置管理
通过配置文件精细控制Banner:
# application.yml
spring:
banner:
# Banner模式: OFF, CONSOLE, LOG
mode: CONSOLE
# 图片Banner配置
image:
location: classpath:banner.png
width: 76
height: 20
pixel-mode: block
invert: false
margin: 2
# 字符集(用于文本Banner)
charset: UTF-8
# 应用信息(在Banner中引用)
application:
name: 订单服务
version: 1.0.0
startup-date: ${application.startup-date}
# 自定义Banner属性
custom:
banner:
company: "某某科技有限公司"
department: "技术研发部"
contact: "tech@example.com"
show-memory: true
show-threads: true
show-uptime: true
3. 性能考虑:Banner 对启动时间的影响
Banner处理在启动的早期阶段,复杂的Banner可能影响启动时间。优化建议:
// 性能优化的Banner
@Component
@ConditionalOnProperty(name = "spring.banner.enabled", havingValue = "true", matchIfMissing = true)
public class OptimizedBanner implements Banner {
private volatile String cachedBanner; // 缓存生成的Banner
@Override
public void printBanner(Environment environment,
Class<?> sourceClass,
PrintStream out) {
if (cachedBanner == null) {
synchronized (this) {
if (cachedBanner == null) {
cachedBanner = generateBanner(environment);
}
}
}
out.print(cachedBanner); // 使用缓存,避免重复生成
}
private String generateBanner(Environment environment) {
long startTime = System.currentTimeMillis();
StringBuilder banner = new StringBuilder();
// 生成简单的Banner,避免复杂计算
banner.append("\n");
banner.append(" ").append(environment.getProperty("spring.application.name", "App"));
banner.append(" v").append(environment.getProperty("application.version", "1.0.0"));
banner.append("\n\n");
long duration = System.currentTimeMillis() - startTime;
if (duration > 100) {
LoggerFactory.getLogger(OptimizedBanner.class)
.warn("Banner生成耗时 {}ms,考虑简化", duration);
}
return banner.toString();
}
}
五、常见问题与解决方案
问题1:Banner 显示乱码
原因:控制台字符集不匹配
解决方案:
- 确保
banner.txt使用UTF-8编码 - 在IDEA中设置运行配置:
Help -> Edit Custom VM Options 添加: -Dfile.encoding=UTF-8 - 或者在
application.yml中指定:spring: banner: charset: UTF-8
问题2:Banner 不显示
原因:Banner被禁用或配置错误
排查步骤:
// Banner调试组件
@Component
@Slf4j
public class BannerDebugger {
@EventListener(ApplicationStartingEvent.class)
public void debugBannerConfig(ApplicationStartingEvent event) {
SpringApplication app = event.getSpringApplication();
log.info("Banner模式: {}", app.getBannerMode());
log.info("Banner自定义类: {}", app.getBanner());
// 检查Banner文件是否存在
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource[] possibleBanners = new Resource[] {
resourceLoader.getResource("classpath:banner.txt"),
resourceLoader.getResource("classpath:banner.png"),
resourceLoader.getResource("classpath:banner.jpg"),
resourceLoader.getResource("classpath:banner.gif")
};
for (Resource banner : possibleBanners) {
if (banner.exists()) {
log.info("找到Banner文件: {}", banner);
}
}
}
}
问题3:彩色 Banner 在日志文件中显示乱码
原因:ANSI颜色代码被写入日志文件
解决方案:
# application.yml
spring:
banner:
# 控制台显示彩色,日志文件不记录颜色代码
charset: UTF-8
logging:
pattern:
console: "%clr(%msg%n)" # 控制台显示颜色
file: "%msg%n" # 文件不记录颜色代码
六、额外的:Banner 管理系统
对于需要动态管理Banner的大型应用,可以创建Banner管理系统:
// Banner管理服务
@Service
@Slf4j
public class BannerService {
@Value("classpath:banners/")
private Resource bannersDirectory;
/**
* 获取所有可用的Banner
*/
public List<BannerInfo> getAvailableBanners() throws IOException {
List<BannerInfo> banners = new ArrayList<>();
Resource[] resources = getBannerResources();
for (Resource resource : resources) {
BannerInfo info = new BannerInfo();
info.setName(resource.getFilename());
info.setPath(resource.getURI().toString());
info.setType(detectBannerType(resource));
info.setPreview(generatePreview(resource));
banners.add(info);
}
return banners;
}
/**
* 动态切换Banner
*/
public void switchBanner(String bannerName) throws IOException {
Resource newBanner = getBannerResource(bannerName);
if (newBanner.exists()) {
// 在实际应用中,可能需要重启应用或动态加载
log.info("切换到Banner: {}", bannerName);
// 这里可以触发Banner重新加载逻辑
} else {
throw new FileNotFoundException("Banner不存在: " + bannerName);
}
}
/**
* 生成节日主题Banner
*/
public String generateFestivalBanner(String festival) {
Map<String, String> festivalThemes = Map.of(
"christmas", "🎄 圣诞快乐! 🎅",
"newyear", "🎉 新年快乐! 🥳",
"spring", "🌸 新春快乐! 🧧",
"nationalday", "🇨🇳 国庆快乐! 🎆"
);
String theme = festivalThemes.getOrDefault(festival.toLowerCase(), "🎊 节日快乐! 🎈");
return String.format("""
╔══════════════════════════════════════════════════╗
║ ║
║ %s ║
║ ║
║ %s 全体同仁敬上 ║
║ ║
╚══════════════════════════════════════════════════╝
""", theme, LocalDate.now().getYear());
}
private Resource[] getBannerResources() throws IOException {
if (bannersDirectory.exists()) {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
return resolver.getResources("classpath:banners/*");
}
return new Resource[0];
}
private BannerType detectBannerType(Resource resource) {
String filename = resource.getFilename().toLowerCase();
if (filename.endsWith(".txt")) return BannerType.TEXT;
if (filename.endsWith(".png")) return BannerType.IMAGE;
if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) return BannerType.IMAGE;
if (filename.endsWith(".gif")) return BannerType.ANIMATED;
return BannerType.UNKNOWN;
}
private String generatePreview(Resource resource) throws IOException {
// 生成Banner预览(前3行)
if (detectBannerType(resource) == BannerType.TEXT) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(resource.getInputStream()))) {
StringBuilder preview = new StringBuilder();
for (int i = 0; i < 3; i++) {
String line = reader.readLine();
if (line != null) {
preview.append(line).append("\n");
}
}
return preview.toString();
}
}
return "[图片/动画Banner]";
}
@Data
public static class BannerInfo {
private String name;
private String path;
private BannerType type;
private String preview;
}
public enum BannerType {
TEXT, IMAGE, ANIMATED, UNKNOWN
}
}
欢迎在评论区分享你创建的炫酷 Banner,期待ing ~
更多推荐




所有评论(0)