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