你是否厌倦了每次启动Spring Boot应用时那个千篇一律的"SPRING" ASCII艺术字?

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.5)

今天教你如何打造独一无二的启动Banner,让应用启动更具个性!

一、Spring Boot Banner基础

1. 什么是 Banner ?

Banner是Spring Boot应用启动时在控制台显示的艺术化文本。它不仅仅是装饰,还能传递重要信息:

  1. 应用标识:显示应用名称、版本
  2. 环境信息:区分开发、测试、生产环境
  3. 启动状态:展示启动进度和状态
  4. 团队文化:体现团队的技术文化

2. Banner的显示时机

Banner在应用启动的早期阶段显示,具体在SpringApplicationprintBanner方法中:

// 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.xmlbuild.gradle
  • ${application.formatted-version}:格式化版本
  • ${spring-boot.version}:Spring Boot版本
  • ${spring-boot.formatted-version}:格式化Spring Boot版本
  • ${application.title}:应用名称

方式2:图片 Banner(图形化展示)

Spring Boot支持将图片转换为ASCII艺术字。支持的格式:jpgpnggif

步骤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艺术很麻烦?使用在线工具生成:

  1. patorjk.com/taag - 最流行的ASCII艺术生成器
  2. www.kammerl.de/ascii/AsciiSignature.php - 多种字体风格
  3. 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 显示乱码

原因:控制台字符集不匹配

解决方案

  1. 确保banner.txt使用UTF-8编码
  2. 在IDEA中设置运行配置:
    Help -> Edit Custom VM Options
    添加: -Dfile.encoding=UTF-8
    
  3. 或者在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 ~

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐