SpringBoot启动Banner设置及源码分析

本文为原创,转载请标明出处!

我们都知道SpringBoot在启动时会打印一个Banner,就是一个SpringBoot的标志,如下:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.5.RELEASE)

那么这个标志是怎么打印出来的,如何修改呢?今天就从源码级别进行相关解析和修改处理。

1、源码分析

启动SpringBoot的时候,常用的方式是使用SpringApplication.run(xxx.class, args);

当然还有几种其他的启动方式(后面在说明修改Banner时会涉及),但无论哪种启动方式,最后都会运行SpringApplication的run方法,下面是这个run方法的源码:

	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment); // 此处打印
	......

上面加注释的那部分就是其打印Banner的代码,我们再打开这个printBanner()看下源码,如下:

	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);
	}

其中涉及到了一个BannerMode的概念,这个是一个枚举,描述的是打印Banner的模式:

	enum Mode {

		/**
		 * Disable printing of the banner.
		 */
		OFF,

		/**
		 * Print the banner to System.out.
		 */
		CONSOLE,

		/**
		 * Print the banner to the log file.
		 */
		LOG

	}

源码已经注释的非常清晰,有三种模式,此处不再赘述。

书接上文,我们发现绕过一些判断的方法,最后都是执行的bannerPrinter.print方法,这个bannerPrinter是一个类:SpringApplicationBannerPrinter,我们打开这部分源码继续分析:

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

	private Banner getBanner(Environment environment) {
		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;
	}

处理逻辑其实很简单,就是首先获取当前环境的Banner(Banner其实是一个接口),然后执行这个banner的printBanner方法。SpringBoot在启动的时候使用的是DEFAULT_BANNER,DEFAULT_BANNER对应的类是:

private static final Banner DEFAULT_BANNER = new SpringBootBanner();

到这里我们已经很清楚了,其实SpringBoot在启动时调用的是SpringBootBanner这个类,下面分析一下这个类。

首先是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();
	}

}

哈哈,是不是很开心的看到了SpringBoot启动时打印的图标?

从上面我们可以很清晰的看出:

1)这个类实现了Banner这个接口;

2)它打印了Banner信息;

在这里我们其实也学习到了另外的一个知识:那就是获取版本号的处理。

String version = SpringBootVersion.getVersion();

我们看下SpringBootVersion的源码,我们发现其实它还是调用的JDK提供的package来获取的:

public final class SpringBootVersion {

	private SpringBootVersion() {
	}

	/**
	 * Return the full version string of the present Spring Boot codebase, or {@code null}
	 * if it cannot be determined.
	 * @return the version of Spring Boot or {@code null}
	 * @see Package#getImplementationVersion()
	 */
	public static String getVersion() {
		Package pkg = SpringBootVersion.class.getPackage();
		return (pkg != null) ? pkg.getImplementationVersion() : null;
	}

}

2、修改Banner

下面我们自己定义一个图标进行打印:

首先定义一个类:MyBanner,该类需要实现Banner接口,我们参考SpringBootBanner的处理方式,源码如下:

public class MyBanner implements Banner {

    private static final String[] BANNER = {
            "-------------------------",
            "| My Name is SpringBoot |",
            "-------------------------"};

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
        for (String line : BANNER) {
            printStream.println(line);
        }
        printStream.println();
    }
}

下面我们修改SpringBoot的启动方式,前面我们已经提到了SpringBoot最基本的启动方式:

SpringApplication.run(BootStart.class, args);

还有两种常用的启动方式:

        SpringApplication springApplication = new SpringApplication(BootStart.class);
        springApplication.run(args);

new SpringApplicationBuilder().sources(BootStart.class).run(args);

而我们修改Banner必须依赖于下面的这两种方式。

1)第一种修改,源码如下:

        SpringApplication springApplication = new SpringApplication(BootStart.class);
        springApplication.setBanner(new MyBanner()); // 设置为自定义的Banner
        springApplication.run(args);

2)第二种修改,源码如下:

new SpringApplicationBuilder().banner(new MyBanner()).sources(BootStart.class).run(args);

通过上述两种修改都可以达到我们想要的效果:

为什么可以达到这个效果呢?答案还是在原来我们看到的SpringApplicationBannerPrinter获取Banner的方法中,源码:

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

	private Banner getBanner(Environment environment) {
		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;
	}

这里面有一个fallbackBanner,而这个fallbackBanner恰好就是我们通过setBanner方法设置的,由下面几段代码可以清晰的看出,其实这些代码原先都看过:

	public void setBanner(Banner banner) {
		this.banner = banner;
	}
	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); // 就是这个banner
		if (this.bannerMode == Mode.LOG) {
			return bannerPrinter.print(environment, this.mainApplicationClass, logger);
		}
		return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
	}
	SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) {
		this.resourceLoader = resourceLoader;
		this.fallbackBanner = fallbackBanner; // 就是setBanner中的那个Banner
	}

这样就一目了然了~~~~~~

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值