重构所有注解@CacheConfig的Service类,将IEntityService泛型从实体类改为VO类 实现实体与VO之间的转换逻辑,使用VO替代实体进行缓存以避免序列化问题 更新相关依赖组件和测试用例,确保功能完整性和系统兼容性 优化Redis缓存配置,清理旧缓存数据并验证新缓存策略有效性
273 lines
11 KiB
Java
273 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());
|
||
|
||
// 初始化MyProperties,从properties加载配置
|
||
try {
|
||
MyProperties myProperties = context.getBean(MyProperties.class);
|
||
myProperties.loadFromProperties(properties);
|
||
holder.info("MyProperties配置加载完成");
|
||
} catch (Exception e) {
|
||
logger.error("加载MyProperties配置失败", e);
|
||
holder.error("加载MyProperties配置失败: " + e.getMessage());
|
||
}
|
||
|
||
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();
|
||
}
|
||
|
||
}
|