javascript
SpringBoot源码解析(十一)自定义banner
上篇文章結(jié)束了prepareEnvironment方法的分析,本篇繼續(xù)SpringApplication的run方法往下走,看一個比較簡單的點——banner打印
所謂banner就是SpringBoot應(yīng)用啟動的時候打印在控制臺的一個logo
涉及到的代碼為下面這行printBanner
在這之前,先簡單說下上面的這行configureIgnoreBeanInfo
它將環(huán)境配置中的spring.beaninfo.ignore屬性設(shè)置到系統(tǒng)變量中,如果沒有配置,就默認為true
這個屬性主要和內(nèi)省機制有關(guān),這里不詳細展開,很多工具類比如BeanUtils底層都依賴了內(nèi)省,spring.beaninfo.ignore設(shè)置為true,會忽略自定義的BeanInfo
至于為什么SpringBoot把這個屬性默認設(shè)置為true,我個人的理解是很多底層的機制都依賴了內(nèi)省,而自定義BeanInfo很可能干擾使用了內(nèi)省機制的相關(guān)功能,所以SpringBoot不推薦使用,實際工作中也極少會做BeanInfo層面的定制
接下來進入正題,點開printBanner方法
private Banner printBanner(ConfigurableEnvironment environment) {if (this.bannerMode == Mode.OFF) {return null;} else {ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader(this.getClassLoader());SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter((ResourceLoader)resourceLoader, this.banner);return this.bannerMode == Mode.LOG ? bannerPrinter.print(environment, this.mainApplicationClass, logger) : bannerPrinter.print(environment, this.mainApplicationClass, System.out);}}bannerMode是banner打印的模式,取值有三個
public static enum Mode {OFF,CONSOLE,LOG; }OFF就是不打印banner,CONSOLE是打印到標(biāo)準(zhǔn)輸出流,會輸出到控制臺,LOG就是打印到日志文件
bannerMode在SpringApplication對象的構(gòu)造函數(shù)里初始化為CONSOLE
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {......this.bannerMode = Mode.CONSOLE;...... }所以默認就是只打印到控制臺的,但是一般我們項目都會集成一些日志框架,這些框架也可以把標(biāo)準(zhǔn)輸出流的內(nèi)容重定向到日志文件中
如果沒有設(shè)置為關(guān)閉,就進入printBanner方法的else分支
...... ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader(this.getClassLoader()); SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter((ResourceLoader)resourceLoader, this.banner); return this.bannerMode == Mode.LOG ? bannerPrinter.print(environment, this.mainApplicationClass, logger) : bannerPrinter.print(environment, this.mainApplicationClass, System.out); ......第一行定義了一個ResourceLoader,由于SpringApplication對象的resourceLoader并沒有初始化,所以new了一個DefaultResourceLoader,這個東西我們已經(jīng)見了很多次了
接下來新建了一個SpringApplicationBannerPrinter,入?yún)⑹莿倓?chuàng)建的DefaultResourceLoader和SpringApplication對象的banner屬性,這個屬性同樣沒有初始化,所以此時也是null
SpringApplicationBannerPrinter內(nèi)部封裝了將banner打印到不同渠道的方法,先看下它的成員變量
class SpringApplicationBannerPrinter {static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";static final String DEFAULT_BANNER_LOCATION = "banner.txt";static final String[] IMAGE_EXTENSION = new String[]{"gif", "jpg", "png"};private static final Banner DEFAULT_BANNER = new SpringBootBanner();private final ResourceLoader resourceLoader;private final Banner fallbackBanner;banner可以是圖片形式,也可以是文本形式,可供我們自定義配置,前兩個成員變量就是自定義文本banner和圖片banner的默認配置名,后續(xù)尋找banner的時候,先看有沒有自定義,如果沒有,再找默認的banner文件
第三個變量為默認的文本banner文件,即classpath下的banner.txt
第四個變量為默認的圖片banner的后綴名,圖片banner可以支持多種格式,即會到classpath下找圖片banner.gif / jpg / png
最后根據(jù)bannerMode是LOG還是CONSOLE,調(diào)用SpringApplicationBannerPrinter對象不同的print方法,這兩個print方法內(nèi)部實現(xiàn)大致相同,只不過打印的目的地分別為log文件以及標(biāo)準(zhǔn)輸出流System.out,我們這里就按照默認情況,看下打印到System.out的代碼
public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {Banner banner = this.getBanner(environment);banner.printBanner(environment, sourceClass, out);return new SpringApplicationBannerPrinter.PrintedBanner(banner, sourceClass);}第一行先獲取Banner對象
private Banner getBanner(Environment environment) {SpringApplicationBannerPrinter.Banners banners = new SpringApplicationBannerPrinter.Banners();banners.addIfNotNull(this.getImageBanner(environment));banners.addIfNotNull(this.getTextBanner(environment));if (banners.hasAtLeastOneBanner()) {return banners;} else {return this.fallbackBanner != null ? this.fallbackBanner : DEFAULT_BANNER;}}Banner對象就是對banner內(nèi)容的封裝,文本類型的banner會封裝到ResourceBanner中,圖片類型則封裝到ImageBanner,而SpringBootBanner是默認的banner,我們下面會見到
先嘗試獲取圖片類型的banner
private Banner getImageBanner(Environment environment) {String location = environment.getProperty("spring.banner.image.location");if (StringUtils.hasLength(location)) {Resource resource = this.resourceLoader.getResource(location);return resource.exists() ? new ImageBanner(resource) : null;} else {String[] var3 = IMAGE_EXTENSION;int var4 = var3.length;for(int var5 = 0; var5 < var4; ++var5) {String ext = var3[var5];Resource resource = this.resourceLoader.getResource("banner." + ext);if (resource.exists()) {return new ImageBanner(resource);}}return null;}}到environment中查找配置spring.banner.image.location(SpringBoot中很多地方都是這樣,雖然上面定義了靜態(tài)變量,但是下面很多代碼并沒有引用),如果沒有指定,就到classpath下看有沒有默認的banner圖片,也就是上面提到的在classpath下找圖片banner.gif / jpg / png
接下來再找txt類型的banner
private Banner getTextBanner(Environment environment) {String location = environment.getProperty("spring.banner.location", "banner.txt");Resource resource = this.resourceLoader.getResource(location);return resource.exists() ? new ResourceBanner(resource) : null;}先到environment看是否有配置spring.banner.location,如果沒有,再默認找classpath下的banner.txt
如果圖片類型和文本類型的banner都沒有找到,就取fallbackBanner
return this.fallbackBanner != null ? this.fallbackBanner : DEFAULT_BANNER;這個fallbackBanner傳進來的是SpringApplication對象的banner屬性,它并沒有初始化,所以是null的,也就是說最終的fallbackBanner賦值為DEFAULT_BANNER,它是SpringApplicationBannerPrinter的靜態(tài)變量,初始化為SpringBootBanner
class SpringApplicationBannerPrinter {......private static final Banner DEFAULT_BANNER = new SpringBootBanner();......看下這個SpringBootBanner 的實現(xiàn)
class SpringBootBanner implements Banner {private static final String[] BANNER = new String[]{"", " . ____ _ __ _ _", " /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\", " \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )", " ' |____| .__|_| |_|_| |_\\__, | / / / /", " =========|_|==============|___/=/_/_/_/"};private static final String SPRING_BOOT = " :: Spring Boot :: ";private static final int STRAP_LINE_SIZE = 42;SpringBootBanner() {}第一行String類型的數(shù)組,其實就是我們文章開頭看到的SpringBoot默認的banner,它是以字符串的形式寫死在代碼里的
獲取到banner后,調(diào)用它的print方法,最終通過System.out.println()將banner內(nèi)容寫到控制臺
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {try {String banner = StreamUtils.copyToString(this.resource.getInputStream(), (Charset)environment.getProperty("spring.banner.charset", Charset.class, StandardCharsets.UTF_8));PropertyResolver resolver;for(Iterator var5 = this.getPropertyResolvers(environment, sourceClass).iterator(); var5.hasNext(); banner = resolver.resolvePlaceholders(banner)) {resolver = (PropertyResolver)var5.next();}out.println(banner);} catch (Exception var7) {logger.warn("Banner not printable: " + this.resource + " (" + var7.getClass() + ": '" + var7.getMessage() + "')", var7);}}很多公司都會在SpringBoot基礎(chǔ)上做一層封裝,然后加上自己公司的banner,這里附上一個自定義banner的網(wǎng)站
自定義banner
(https://patorjk.com/software/taag/#p=display&f=Doom&t=mybanner)
比如取名為MyBanner,然后將內(nèi)同拷貝到classpath下的banner.txt
啟動項目,默認banner已經(jīng)被替換了
總結(jié)
以上是生活随笔為你收集整理的SpringBoot源码解析(十一)自定义banner的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一位中科院自动化研究所博士毕业论文的致谢
- 下一篇: Spring Boot项目自定义启动Ba