refactor: 重构实体类equals和hashCode方法 fix: 修复WebSocketService消息发送逻辑 style: 格式化代码和优化导入 docs: 更新JacksonConfig日期序列化格式 test: 添加CompanyFilePathTableCell测试类 chore: 清理无用代码和注释
262 lines
11 KiB
Java
262 lines
11 KiB
Java
package com.ecep.contract;
|
|
|
|
import java.time.Duration;
|
|
import java.time.Instant;
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDateTime;
|
|
import java.time.LocalTime;
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.util.Locale;
|
|
import java.util.Properties;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.beans.BeansException;
|
|
import org.springframework.boot.ConfigurableBootstrapContext;
|
|
import org.springframework.boot.SpringApplication;
|
|
import org.springframework.boot.SpringApplicationHook;
|
|
import org.springframework.boot.SpringApplicationRunListener;
|
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
|
import org.springframework.boot.context.metrics.buffering.StartupTimeline;
|
|
import org.springframework.cache.CacheManager;
|
|
import org.springframework.cache.annotation.EnableCaching;
|
|
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
|
import org.springframework.context.ConfigurableApplicationContext;
|
|
import org.springframework.context.annotation.Bean;
|
|
import org.springframework.context.event.ContextClosedEvent;
|
|
import org.springframework.context.event.EventListener;
|
|
import org.springframework.core.env.ConfigurableEnvironment;
|
|
import org.springframework.core.env.PropertiesPropertySource;
|
|
import org.springframework.core.env.PropertySource;
|
|
import org.springframework.core.metrics.StartupStep;
|
|
import org.springframework.scheduling.annotation.EnableAsync;
|
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
|
|
|
import com.ecep.contract.util.UITools;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
|
|
|
@SpringBootApplication(exclude = {
|
|
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.class,
|
|
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.class,
|
|
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.class,
|
|
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration.class
|
|
})
|
|
@EnableScheduling
|
|
@EnableAsync
|
|
@EnableCaching
|
|
public class SpringApp {
|
|
private static final Logger logger = LoggerFactory.getLogger(SpringApp.class);
|
|
|
|
public static SpringApplication application;
|
|
public static ConfigurableApplicationContext context;
|
|
|
|
public static <T> T getBean(Class<T> requiredType) throws BeansException {
|
|
return context.getBean(requiredType);
|
|
}
|
|
|
|
public static void launch(Properties properties, MessageHolder holder) {
|
|
application = new SpringApplication(SpringApp.class);
|
|
BufferingApplicationStartup startup = new BufferingApplicationStartup(2000);
|
|
application.setApplicationStartup(startup);
|
|
//
|
|
holder.debug("应用程序环境准备中...");
|
|
SpringApplication.withHook(new Hook(holder), () -> {
|
|
// 动态地注册或修改这些组件和配置
|
|
application.addBootstrapRegistryInitializer(registry -> {
|
|
//
|
|
System.out.println("registry = " + registry);
|
|
|
|
});
|
|
application.addListeners(event -> {
|
|
logger.debug("SpringApp.launch ApplicationListener, event:{}", event);
|
|
});
|
|
|
|
application.addInitializers(app -> {
|
|
logger.debug("SpringApp.launch ApplicationContextInitializer");
|
|
ConfigurableEnvironment environment = app.getEnvironment();
|
|
logger.debug("environment = {}", environment);
|
|
PropertySource<?> dynamicProperties = environment.getPropertySources().get("dynamicProperties");
|
|
if (dynamicProperties != null) {
|
|
logger.debug("dynamicProperties = {}", dynamicProperties);
|
|
}
|
|
environment.getPropertySources().addLast(new PropertiesPropertySource("dynamicProperties", properties));
|
|
// app.getBeanFactory().registerSingleton("dataSource", dataSource());
|
|
logger.debug("app = {}", app);
|
|
});
|
|
|
|
startup.start("");
|
|
context = application.run();
|
|
logger.debug("SpringApp.launch application.run().");
|
|
Duration between = Duration.between(startup.getBufferedTimeline().getStartTime(), Instant.now());
|
|
holder.info("应用程序环境加载完成... " + between);
|
|
});
|
|
CompletableFuture.runAsync(() -> {
|
|
// 在这里调用 startup 性能分析
|
|
analyzeStartupPerformance(startup);
|
|
}, Desktop.instance.getExecutorService());
|
|
}
|
|
|
|
/**
|
|
* 分析启动性能数据并输出到日志
|
|
*/
|
|
private static void analyzeStartupPerformance(BufferingApplicationStartup startup) {
|
|
// 获取所有记录的事件
|
|
StartupTimeline timeline = startup.getBufferedTimeline();
|
|
if (timeline == null || timeline.getEvents().isEmpty()) {
|
|
logger.warn("StartupTimeline 为空或没有事件!");
|
|
return;
|
|
}
|
|
logger.info("总共有 {} 个事件", timeline.getEvents().size());
|
|
|
|
// 找出与 Bean 初始化相关的步骤
|
|
timeline.getEvents().stream()
|
|
.filter(event -> event.getStartupStep().getName().startsWith("spring.beans."))
|
|
.sorted((a, b) -> Long.compare(b.getDuration().toMillis(), a.getDuration().toMillis()))
|
|
.limit(30)
|
|
.forEach(event -> {
|
|
String name = event.getStartupStep().getName();
|
|
long duration = event.getDuration().toMillis();
|
|
logger.info("Bean 初始化阶段: {} - 耗时: {} ms", name, duration);
|
|
|
|
for (StartupStep.Tag tag : event.getStartupStep().getTags()) {
|
|
if ("beanName".equals(tag.getKey())) {
|
|
logger.info(" └── Bean 名称: {}", tag.getValue());
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public static String getMessage(String code, Object[] args, Locale locale) {
|
|
return context.getMessage(code, args, locale);
|
|
}
|
|
|
|
public static void shutdown() {
|
|
System.out.println("SpringApp.shutdown");
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("shutdown");
|
|
}
|
|
if (context != null) {
|
|
if (context.isRunning()) {
|
|
context.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
public static boolean isRunning() {
|
|
return context != null && context.isRunning();
|
|
}
|
|
|
|
static class Hook implements SpringApplicationHook, SpringApplicationRunListener {
|
|
MessageHolder holder;
|
|
|
|
Hook(MessageHolder holder) {
|
|
this.holder = holder;
|
|
}
|
|
|
|
public void debug(String msg) {
|
|
holder.debug(msg);
|
|
}
|
|
|
|
@Override
|
|
public void starting(ConfigurableBootstrapContext bootstrapContext) {
|
|
logger.debug("Desktop.starting");
|
|
debug("Spring Application 启动中...");
|
|
}
|
|
|
|
@Override
|
|
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
|
|
ConfigurableEnvironment environment) {
|
|
logger.debug("Desktop.environmentPrepared");
|
|
debug("初始化 Environment 中,请稍后...");
|
|
}
|
|
|
|
@Override
|
|
public void contextPrepared(ConfigurableApplicationContext context) {
|
|
logger.debug("Desktop.contextPrepared");
|
|
debug("Spring Application Context 预处理中,请稍后...");
|
|
}
|
|
|
|
@Override
|
|
public void contextLoaded(ConfigurableApplicationContext context) {
|
|
logger.debug("Desktop.contextLoaded");
|
|
debug("Spring Application Context 初始化完毕,请稍后...");
|
|
}
|
|
|
|
@Override
|
|
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
|
|
logger.debug("Desktop.started");
|
|
debug("Spring Application 启动完毕.");
|
|
}
|
|
|
|
@Override
|
|
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
|
|
logger.debug("Desktop.ready");
|
|
debug("Spring Application ready.");
|
|
}
|
|
|
|
@Override
|
|
public void failed(ConfigurableApplicationContext context, Throwable exception) {
|
|
logger.error("Desktop.failed", exception);
|
|
holder.error("Spring Application 启动失败(" + exception.getMessage() + ").");
|
|
UITools.showExceptionAndWait("启动失败", exception);
|
|
}
|
|
|
|
@Override
|
|
public SpringApplicationRunListener getRunListener(SpringApplication springApplication) {
|
|
return this;
|
|
}
|
|
}
|
|
|
|
@EventListener
|
|
public void handleClosedEvent(ContextClosedEvent event) {
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("handleClosedEvent={}", event);
|
|
}
|
|
Desktop.shutdown();
|
|
}
|
|
|
|
@Bean
|
|
public CacheManager cacheManager() {
|
|
// return new ConcurrentMapCacheManager("myCache");
|
|
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
|
|
cacheManager.setAsyncCacheMode(true);
|
|
return cacheManager;
|
|
}
|
|
|
|
@Bean
|
|
public ObjectMapper objectMapper() {
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
|
// LocalDate
|
|
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE));
|
|
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE));
|
|
// LocalTime
|
|
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME));
|
|
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME));
|
|
// LocalDateTime
|
|
javaTimeModule.addSerializer(LocalDateTime.class,
|
|
new LocalDateTimeSerializer(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
|
|
javaTimeModule.addDeserializer(LocalDateTime.class,
|
|
new LocalDateTimeDeserializer(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
|
|
|
|
objectMapper.registerModule(javaTimeModule);
|
|
return objectMapper;
|
|
}
|
|
|
|
@Bean
|
|
public ScheduledExecutorService scheduledExecutorService() {
|
|
return Desktop.instance.getExecutorService();
|
|
}
|
|
|
|
}
|