启动输出

This commit is contained in:
yuejiajun 2025-09-29 17:38:00 +08:00
parent 088dc1bf09
commit 46cb97b675
10 changed files with 368 additions and 32 deletions

View File

@ -0,0 +1,61 @@
package com.example.demo.common.base;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.env.Environment;
import org.springframework.context.ApplicationContext;
/**
* 启动输出接口
* 用于在SpringBoot启动后执行输出操作的Bean需要实现此接口
*
* @author 岳佳君 (2025年09月29日 16:45:39)
* @version 1.0.0
*/
public interface BaseStartupOutput extends CommandLineRunner {
/**
* 获取执行优先级数值越小优先级越高
*
* @return 执行优先级
*/
default int getOrder() {
return 0;
}
/**
* 是否启用该输出
*
* @return 是否启用
*/
default boolean isEnabled() {
return true;
}
/**
* 输出名称用于日志标识
*
* @return 输出名称
*/
default String getOutputName() {
return this.getClass().getSimpleName();
}
/**
* 启动输出执行方法
*
* @param environment 环境配置
* @param context 应用上下文
*/
void outputOnStartup(Environment environment, ApplicationContext context);
/**
* CommandLineRunner接口的默认实现
*
* @param args 命令行参数
*/
@Override
default void run(String... args) {
// 由StartupOutputExecutor统一管理执行
}
}

View File

@ -0,0 +1,26 @@
package com.example.demo.common.base.defaults;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
/**
* 启动默认输出
*
* @author 岳佳君 (2025年9月29日 17:26:23)
* @version 1.0.0
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class StartupOutput implements com.example.demo.common.base.BaseStartupOutput {
@Override
public void outputOnStartup(Environment environment, ApplicationContext context) {
// 获取当前激活的环境如devtestprod
String[] activeProfiles = environment.getActiveProfiles();
String activeProfile = activeProfiles.length > 0 ? activeProfiles[0] : "default";
log.info("当前环境: {}", activeProfile);
}
}

View File

@ -1,32 +0,0 @@
package com.example.demo.common.config.springdoc;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class OpenApiStarterOutput implements CommandLineRunner {
private final ServerProperties serverProperties;
private final Environment environment;
@Override
public void run(String... args) throws Exception {
String pathInYaml = "server.servlet.context-path";
String portInYaml = "server.port";
String port = environment.getProperty(portInYaml, "8080");
String path = environment.getProperty(pathInYaml, "");
// 获取当前激活的环境如devtestprod
String[] activeProfiles = environment.getActiveProfiles();
String activeProfile = activeProfiles.length > 0 ? activeProfiles[0] : "default";
// 输出包含环境信息和动态端口的API文档地址
System.out.printf("当前环境: %sAPI文档地址: http://localhost:%s%s/doc.html#/%n",
activeProfile, port, path);
}
}

View File

@ -0,0 +1,83 @@
package com.example.demo.common.executor;
import com.example.demo.common.base.BaseStartupOutput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* 启动输出执行器
* 统一管理所有BaseStartupOutput实现类的执行
*
* @author 岳佳君 (2025年9月29日 17:18:32)
* @version 1.0.0
*/
@Slf4j
@Component
public class StartupOutputExecutor implements ApplicationRunner {
private final List<BaseStartupOutput> outputs;
private final Environment environment;
private final ApplicationContext context;
/**
* 构造函数
*
* @param outputs 所有BaseStartupOutput实现类可选依赖
* @param environment 环境配置
* @param context 应用上下文
*/
public StartupOutputExecutor(List<BaseStartupOutput> outputs,
Environment environment,
ApplicationContext context) {
this.outputs = outputs != null ? outputs : Collections.emptyList();
this.environment = environment;
this.context = context;
}
/**
* 执行所有启动输出
*
* @param args 应用参数
*/
@Override
public void run(ApplicationArguments args) {
if (outputs.isEmpty()) {
log.debug("未发现需要执行的启动输出");
return;
}
log.debug("开始执行启动输出,共发现 {} 个输出组件", outputs.size());
// 按优先级排序并执行
outputs.stream()
.filter(BaseStartupOutput::isEnabled)
.sorted(Comparator.comparingInt(BaseStartupOutput::getOrder))
.forEach(this::executeOutput);
log.debug("启动输出执行完成");
}
/**
* 执行单个输出组件
*
* @param output 输出组件
*/
private void executeOutput(BaseStartupOutput output) {
String outputName = output.getOutputName();
try {
log.trace("执行启动输出: {}", outputName);
output.outputOnStartup(environment, context);
log.trace("启动输出执行成功: {}", outputName);
} catch (Exception e) {
log.warn("启动输出执行失败: {}", outputName, e);
}
}
}

View File

@ -0,0 +1,51 @@
package com.example.demo.common.output;
import com.example.demo.common.base.BaseStartupOutput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* API文档地址输出
* 在启动后输出API文档访问地址
*
* @author 岳佳君 (2025年09月29日 16:45:00)
* @version 1.0.0
*/
@Slf4j
@Component
public class ApiDocOutput implements BaseStartupOutput {
/**
* 获取执行优先级
*
* @return 优先级数值越小优先级越高
*/
@Override
public int getOrder() {
return 10;
}
/**
* 启动输出执行方法
*
* @param environment 环境配置
* @param context 应用上下文
*/
@Override
public void outputOnStartup(Environment environment, ApplicationContext context) {
String port = environment.getProperty("server.port", "8080");
String path = environment.getProperty("server.servlet.context-path", "");
String apiDocUrl = "http://localhost:" + port + path + "/doc.html";
String swaggerUrl = "http://localhost:" + port + path + "/swagger-ui.html";
System.out.println("\n📚 API文档地址:");
System.out.println(" • Knife4j文档: " + apiDocUrl);
log.trace("API文档地址输出完成 - Knife4j: {}", apiDocUrl);
// System.out.println(" • Swagger文档: " + swaggerUrl);
// log.trace("API文档地址输出完成 - Knife4j: {}, Swagger: {}", apiDocUrl, swaggerUrl);
}
}

View File

@ -0,0 +1,67 @@
package com.example.demo.common.output;
import com.example.demo.common.base.BaseStartupOutput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* 应用配置信息输出
* 在启动后输出应用配置信息
*
* @author 岳佳君 (2025年09月29日 16:45:00)
* @version 1.0.0
*/
@Slf4j
@Component
public class AppConfigOutput implements BaseStartupOutput {
/**
* 获取执行优先级
*
* @return 优先级数值越小优先级越高
*/
@Override
public int getOrder() {
return 1;
}
/**
* 启动输出执行方法
*
* @param environment 环境配置
* @param context 应用上下文
*/
@Override
public void outputOnStartup(Environment environment, ApplicationContext context) {
System.out.println("\n🚀 XML解析器服务启动成功");
System.out.println("📋 应用配置信息:");
// 服务器配置
String port = environment.getProperty("server.port", "8080");
String contextPath = environment.getProperty("server.servlet.context-path", "");
String baseUrl = "http://localhost:" + port + contextPath;
System.out.println(" • 服务端口: " + port);
System.out.println(" • 上下文路径: " + (contextPath.isEmpty() ? "/" : contextPath));
System.out.println(" • 访问地址: " + baseUrl);
// 激活的Profile
String[] activeProfiles = environment.getActiveProfiles();
if (activeProfiles.length > 0) {
System.out.println(" • 激活配置: " + String.join(", ", activeProfiles));
} else {
System.out.println(" • 激活配置: default");
}
// 应用信息
String appName = environment.getProperty("spring.application.name", "xml-to-txt-service");
String appVersion = environment.getProperty("app.version", "1.0.0");
System.out.println(" • 应用名称: " + appName);
System.out.println(" • 应用版本: " + appVersion);
log.trace("应用配置信息输出完成 - 端口: {}, 上下文: {}", port, contextPath);
}
}

View File

@ -0,0 +1,80 @@
package com.example.demo.common.output;
import com.example.demo.common.base.BaseStartupOutput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 数据库连接状态输出
* 在启动后输出数据库连接状态
*
* @author 岳佳君 (2025年09月29日 16:45:00)
* @version 1.0.0
*/
@Slf4j
@Component
public class DatabaseStatusOutput implements BaseStartupOutput {
/**
* 获取执行优先级
*
* @return 优先级数值越小优先级越高
*/
@Override
public int getOrder() {
return 5;
}
/**
* 启动输出执行方法
*
* @param environment 环境配置
* @param context 应用上下文
*/
@Override
public void outputOnStartup(Environment environment, ApplicationContext context) {
System.out.println("\n🗄 数据库连接状态:");
// 检查SQLite数据库连接
checkDataSource("SQLite", "sqlite", context);
// 检查H2数据库连接
checkDataSource("H2", "h2", context);
}
/**
* 检查数据源连接状态
*
* @param dbName 数据库名称
* @param beanName Bean名称
* @param context 应用上下文
*/
private void checkDataSource(String dbName, String beanName, ApplicationContext context) {
try {
DataSource dataSource = context.getBean(beanName + "DataSource", DataSource.class);
if (dataSource != null) {
try (Connection connection = dataSource.getConnection()) {
if (connection.isValid(5)) {
System.out.println("" + dbName + "数据库: 连接正常");
log.trace("{}数据库连接正常", dbName);
} else {
System.out.println("" + dbName + "数据库: 连接无效");
log.trace("{}数据库连接无效", dbName);
}
}
} else {
System.out.println(" ⚠️ " + dbName + "数据库: 未配置");
log.info("{}数据库未配置", dbName);
}
} catch (Exception e) {
System.out.println("" + dbName + "数据库: 连接失败 - " + e.getMessage());
log.warn("{}数据库连接失败: {}", dbName, e.getMessage());
}
}
}

View File

View File

View File