SpringBoot自定义的启动字符画

本文介绍了如何通过在SpringBoot项目中创建banner.txt文件来替换默认的启动画面,并推荐了使用JHipster插件生成专业ASCII艺术的方法。详细展示了AnsiColor和AnsiBackground枚举,以及如何配置输出控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

spring boot项目启动时会打印spring boot的ANSI字符画,可以进行自定义。
如何自定义:
实现方式非常简单,我们只需要在Spring Boot工程的/src/main/resources目录下创建一个banner.txt文件,然后将ASCII字符画复制进去,就能替换默认的banner了。

如何生成字符画:
正好刚接触jhipster,发现有一个 generator-jhipster-banner插件,可以生成banner.
npm install -g generator-jhipster-banner
yo jhipster-banner.
npm install -g yo

相关源码如下:

public final class Ansi8BitColor implements AnsiElement {

	private final String prefix;

	private final int code;

	/**
	 * Create a new {@link Ansi8BitColor} instance.
	 * @param prefix the prefix escape chars
	 * @param code color code (must be 0-255)
	 * @throws IllegalArgumentException if color code is not between 0 and 255.
	 */
	private Ansi8BitColor(String prefix, int code) {
		Assert.isTrue(code >= 0 && code <= 255, "Code must be between 0 and 255");
		this.prefix = prefix;
		this.code = code;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null || getClass() != obj.getClass()) {
			return false;
		}
		Ansi8BitColor other = (Ansi8BitColor) obj;
		return this.prefix.equals(other.prefix) && this.code == other.code;
	}

	@Override
	public int hashCode() {
		return this.prefix.hashCode() * 31 + this.code;
	}

	@Override
	public String toString() {
		return this.prefix + this.code;
	}

	/**
	 * Return a foreground ANSI color code instance for the given code.
	 * @param code the color code
	 * @return an ANSI color code instance
	 */
	public static Ansi8BitColor foreground(int code) {
		return new Ansi8BitColor("38;5;", code);
	}

	/**
	 * Return a background ANSI color code instance for the given code.
	 * @param code the color code
	 * @return an ANSI color code instance
	 */
	public static Ansi8BitColor background(int code) {
		return new Ansi8BitColor("48;5;", code);
	}

/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.ansi;

/**
 * {@link AnsiElement Ansi} background colors.
 *
 * @author Phillip Webb
 * @author Geoffrey Chandler
 * @since 1.3.0
 */
public enum AnsiBackground implements AnsiElement {

	DEFAULT("49"),

	BLACK("40"),

	RED("41"),

	GREEN("42"),

	YELLOW("43"),

	BLUE("44"),

	MAGENTA("45"),

	CYAN("46"),

	WHITE("47"),

	BRIGHT_BLACK("100"),

	BRIGHT_RED("101"),

	BRIGHT_GREEN("102"),

	BRIGHT_YELLOW("103"),

	BRIGHT_BLUE("104"),

	BRIGHT_MAGENTA("105"),

	BRIGHT_CYAN("106"),

	BRIGHT_WHITE("107");

	private String code;

	AnsiBackground(String code) {
		this.code = code;
	}

	@Override
	public String toString() {
		return this.code;
	}

}

对应对颜色的枚举:

public enum AnsiColor implements AnsiElement {

	DEFAULT("39"),

	BLACK("30"),

	RED("31"),

	GREEN("32"),

	YELLOW("33"),

	BLUE("34"),

	MAGENTA("35"),

	CYAN("36"),

	WHITE("37"),

	BRIGHT_BLACK("90"),

	BRIGHT_RED("91"),

	BRIGHT_GREEN("92"),

	BRIGHT_YELLOW("93"),

	BRIGHT_BLUE("94"),

	BRIGHT_MAGENTA("95"),

	BRIGHT_CYAN("96"),

	BRIGHT_WHITE("97");

	private final String code;

	AnsiColor(String code) {
		this.code = code;
	}

	@Override
	public String toString() {
		return this.code;
	}

}

对应的输出:

public abstract class AnsiOutput {

	private static final String ENCODE_JOIN = ";";

	private static Enabled enabled = Enabled.DETECT;

	private static Boolean consoleAvailable;

	private static Boolean ansiCapable;

	private static final String OPERATING_SYSTEM_NAME = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);

	private static final String ENCODE_START = "\033[";

	private static final String ENCODE_END = "m";

	private static final String RESET = "0;" + AnsiColor.DEFAULT;

	/**
	 * Sets if ANSI output is enabled.
	 * @param enabled if ANSI is enabled, disabled or detected
	 */
	public static void setEnabled(Enabled enabled) {
		Assert.notNull(enabled, "Enabled must not be null");
		AnsiOutput.enabled = enabled;
	}

	/**
	 * Returns if ANSI output is enabled
	 * @return if ANSI enabled, disabled or detected
	 */
	public static Enabled getEnabled() {
		return AnsiOutput.enabled;
	}

	/**
	 * Sets if the System.console() is known to be available.
	 * @param consoleAvailable if the console is known to be available or {@code null} to
	 * use standard detection logic.
	 */
	public static void setConsoleAvailable(Boolean consoleAvailable) {
		AnsiOutput.consoleAvailable = consoleAvailable;
	}

	/**
	 * Encode a single {@link AnsiElement} if output is enabled.
	 * @param element the element to encode
	 * @return the encoded element or an empty string
	 */
	public static String encode(AnsiElement element) {
		if (isEnabled()) {
			return ENCODE_START + element + ENCODE_END;
		}
		return "";
	}

	/**
	 * Create a new ANSI string from the specified elements. Any {@link AnsiElement}s will
	 * be encoded as required.
	 * @param elements the elements to encode
	 * @return a string of the encoded elements
	 */
	public static String toString(Object... elements) {
		StringBuilder sb = new StringBuilder();
		if (isEnabled()) {
			buildEnabled(sb, elements);
		}
		else {
			buildDisabled(sb, elements);
		}
		return sb.toString();
	}

	private static void buildEnabled(StringBuilder sb, Object[] elements) {
		boolean writingAnsi = false;
		boolean containsEncoding = false;
		for (Object element : elements) {
			if (element instanceof AnsiElement) {
				containsEncoding = true;
				if (!writingAnsi) {
					sb.append(ENCODE_START);
					writingAnsi = true;
				}
				else {
					sb.append(ENCODE_JOIN);
				}
			}
			else {
				if (writingAnsi) {
					sb.append(ENCODE_END);
					writingAnsi = false;
				}
			}
			sb.append(element);
		}
		if (containsEncoding) {
			sb.append(writingAnsi ? ENCODE_JOIN : ENCODE_START);
			sb.append(RESET);
			sb.append(ENCODE_END);
		}
	}

	private static void buildDisabled(StringBuilder sb, Object[] elements) {
		for (Object element : elements) {
			if (!(element instanceof AnsiElement) && element != null) {
				sb.append(element);
			}
		}
	}

	private static boolean isEnabled() {
		if (enabled == Enabled.DETECT) {
			if (ansiCapable == null) {
				ansiCapable = detectIfAnsiCapable();
			}
			return ansiCapable;
		}
		return enabled == Enabled.ALWAYS;
	}

	private static boolean detectIfAnsiCapable() {
		try {
			if (Boolean.FALSE.equals(consoleAvailable)) {
				return false;
			}
			if ((consoleAvailable == null) && (System.console() == null)) {
				return false;
			}
			return !(OPERATING_SYSTEM_NAME.contains("win"));
		}
		catch (Throwable ex) {
			return false;
		}
	}

	/**
	 * Possible values to pass to {@link AnsiOutput#setEnabled}. Determines when to output
	 * ANSI escape sequences for coloring application output.
	 */
	public enum Enabled {

		/**
		 * Try to detect whether ANSI coloring capabilities are available. The default
		 * value for {@link AnsiOutput}.
		 */
		DETECT,

		/**
		 * Enable ANSI-colored output.
		 */
		ALWAYS,

		/**
		 * Disable ANSI-colored output.
		 */
		NEVER

	}

public class AnsiPropertySource extends PropertySource<AnsiElement> {

	private static final Iterable<Mapping> MAPPINGS;

	static {
		List<Mapping> mappings = new ArrayList<>();
		mappings.add(new EnumMapping<>("AnsiStyle.", AnsiStyle.class));
		mappings.add(new EnumMapping<>("AnsiColor.", AnsiColor.class));
		mappings.add(new Ansi8BitColorMapping("AnsiColor.", Ansi8BitColor::foreground));
		mappings.add(new EnumMapping<>("AnsiBackground.", AnsiBackground.class));
		mappings.add(new Ansi8BitColorMapping("AnsiBackground.", Ansi8BitColor::background));
		mappings.add(new EnumMapping<>("Ansi.", AnsiStyle.class));
		mappings.add(new EnumMapping<>("Ansi.", AnsiColor.class));
		mappings.add(new EnumMapping<>("Ansi.BG_", AnsiBackground.class));
		MAPPINGS = Collections.unmodifiableList(mappings);
	}

	private final boolean encode;

	/**
	 * Create a new {@link AnsiPropertySource} instance.
	 * @param name the name of the property source
	 * @param encode if the output should be encoded
	 */
	public AnsiPropertySource(String name, boolean encode) {
		super(name);
		this.encode = encode;
	}

	@Override
	public Object getProperty(String name) {
		if (StringUtils.hasLength(name)) {
			for (Mapping mapping : MAPPINGS) {
				String prefix = mapping.getPrefix();
				if (name.startsWith(prefix)) {
					String postfix = name.substring(prefix.length());
					AnsiElement element = mapping.getElement(postfix);
					if (element != null) {
						return (this.encode) ? AnsiOutput.encode(element) : element;
					}
				}
			}
		}
		return null;
	}

	/**
	 * Mapping between a name and the pseudo property source.
	 */
	private abstract static class Mapping {

		private final String prefix;

		Mapping(String prefix) {
			this.prefix = prefix;
		}

		String getPrefix() {
			return this.prefix;
		}

		abstract AnsiElement getElement(String postfix);

	}

	/**
	 * {@link Mapping} for {@link AnsiElement} enums.
	 */
	private static class EnumMapping<E extends Enum<E> & AnsiElement> extends Mapping {

		private final Set<E> enums;

		EnumMapping(String prefix, Class<E> enumType) {
			super(prefix);
			this.enums = EnumSet.allOf(enumType);
		}

		@Override
		AnsiElement getElement(String postfix) {
			for (Enum<?> candidate : this.enums) {
				if (candidate.name().equals(postfix)) {
					return (AnsiElement) candidate;
				}
			}
			return null;
		}

	}

	/**
	 * {@link Mapping} for {@link Ansi8BitColor}.
	 */
	private static class Ansi8BitColorMapping extends Mapping {

		private final IntFunction<Ansi8BitColor> factory;

		Ansi8BitColorMapping(String prefix, IntFunction<Ansi8BitColor> factory) {
			super(prefix);
			this.factory = factory;
		}

		@Override
		AnsiElement getElement(String postfix) {
			if (containsOnlyDigits(postfix)) {
				try {
					return this.factory.apply(Integer.parseInt(postfix));
				}
				catch (IllegalArgumentException ex) {
				}
			}
			return null;
		}

		private boolean containsOnlyDigits(String postfix) {
			for (int i = 0; i < postfix.length(); i++) {
				if (!Character.isDigit(postfix.charAt(i))) {
					return false;
				}
			}
			return !postfix.isEmpty();
		}

	}

}

Ansistyle:样式:

public enum AnsiStyle implements AnsiElement {

	NORMAL("0"),

	BOLD("1"),

	FAINT("2"),

	ITALIC("3"),

	UNDERLINE("4");

	private final String code;

	AnsiStyle(String code) {
		this.code = code;
	}

	@Override
	public String toString() {
		return this.code;
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迅捷的软件产品制作专家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值