diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java index 20dbbb746..1f1952d86 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java @@ -1,15 +1,16 @@ package com.ruoyi.common.utils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.springframework.util.StringUtils; + import java.lang.management.ManagementFactory; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; +import java.time.*; import java.util.Date; -import org.apache.commons.lang3.time.DateFormatUtils; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; /** * 时间工具类 @@ -33,6 +34,8 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + // 缓存SimpleDateFormat实例的Map,其中每个格式字符串对应一个ThreadLocal + private static final Map> dateFormatMap = new HashMap<>(); /** * 获取当前Date型日期 * @@ -73,11 +76,39 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils return parseDateToStr(YYYY_MM_DD, date); } - public static final String parseDateToStr(final String format, final Date date) - { - return new SimpleDateFormat(format).format(date); + + /** + * 原代码每次new消耗性能 修改后提升性能并保证线程安全 + * 将给定日期格式化为指定格式的字符串 + * @param format 日期格式字符串,例如 "yyyy-MM-dd" + * @param date 需要格式化的日期对象 + * @return 格式化后的日期字符串 + * @throws IllegalArgumentException 如果格式字符串或日期对象为空时抛出 + */ + public static String parseDateToStr(final String format, final Date date) { + if (!StringUtils.hasLength(format)) { + throw new IllegalArgumentException("Format must not be null or empty"); + } + if (Objects.isNull(date)) { + throw new IllegalArgumentException("Date must not be null"); + } + return getDateFormat(format).format(date); } + /** + * 获取与指定格式关联的SimpleDateFormat实例 + * 如果Map中不存在该格式的实例,则创建一个新的实例并存入Map + * @param format 日期格式字符串 + * @return 与指定格式关联的SimpleDateFormat实例 + */ + private static SimpleDateFormat getDateFormat(final String format) { + return dateFormatMap + .computeIfAbsent(format + , k -> ThreadLocal.withInitial(() -> new SimpleDateFormat(k))) + .get(); + } + + public static final Date dateTime(final String format, final String ts) { try diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java index c5699ad49..0ab6e236e 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java @@ -1,5 +1,6 @@ package com.ruoyi.common.utils.spring; +import com.ruoyi.common.utils.StringUtils; import org.springframework.aop.framework.AopContext; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -8,31 +9,93 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; -import com.ruoyi.common.utils.StringUtils; + +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.function.Supplier; /** * spring工具类 方便在非spring管理环境中获取bean - * + * + * 该工具类通过实现BeanFactoryPostProcessor和ApplicationContextAware接口, + * 在Spring容器初始化时保存ApplicationContext和BeanFactory的引用,以便在非Spring管理的环境中获取bean * @author ruoyi */ @Component public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware { - /** Spring应用上下文环境 */ - private static ConfigurableListableBeanFactory beanFactory; + /** Spring应用上下文环境*/ - private static ApplicationContext applicationContext; + /** + * 用于存储ConfigurableListableBeanFactory的AtomicReference + */ + private static final AtomicReference> beanFactoryRef = new AtomicReference<>(); + /** + * 用于存储ApplicationContext的AtomicReference + */ + private static final AtomicReference> applicationContextRef = new AtomicReference<>(); + + private static final String UNINITIALIZED_EXCEPTION = " is not initialized yet."; + + private SpringUtils(){} + + /** + * 在BeanFactory后处理器中设置beanFactory引用 + * + * @param beanFactory ConfigurableListableBeanFactory实例 + * @throws BeansException 如果beanFactory设置过程中出现异常 + */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - SpringUtils.beanFactory = beanFactory; + beanFactoryRef.set(Optional.of(beanFactory)); } + /** + * 设置ApplicationContext引用 + * + * @param applicationContext ApplicationContext实例 + * @throws BeansException 如果applicationContext设置过程中出现异常 + */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - SpringUtils.applicationContext = applicationContext; + applicationContextRef.set(Optional.of(applicationContext)); + } + + /** + * 该方法接受一个参数,并返回一个异常 Supplier + * + * @param clz 异常所属类 + * @param suffix 异常信息后缀 + * @return 返回RuntimeException的Supplier + */ + private static Supplier exceptionSupplier(Class clz, String suffix) { + return () -> new IllegalStateException(generateErrorMessage(clz, suffix)); + } + + /** + * 该方法接受一个参数,并返回一个异常 Supplier + * + * @param clz 异常所属类 + * @param suffix 异常信息后缀 + * @return 返回拼接好的异常信息 类名+异常信息后缀 + */ + private static String generateErrorMessage(Class clz,String suffix) { + Function errorMessageFunction = errorMessageGenerator(suffix); + return errorMessageFunction.apply(clz.getSimpleName()); + } + + /** + * 该方法接受一个参数,返回拼接功能的Function + * + * @param suffix 异常信息后缀 + * @return 返回拼接功能的Function + */ + private static Function errorMessageGenerator(String suffix) { + return parameter -> parameter + suffix; } /** @@ -46,7 +109,9 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC @SuppressWarnings("unchecked") public static T getBean(String name) throws BeansException { - return (T) beanFactory.getBean(name); + return (T) beanFactoryRef.get() + .orElseThrow(exceptionSupplier(ConfigurableListableBeanFactory.class, UNINITIALIZED_EXCEPTION)) + .getBean(name); } /** @@ -59,8 +124,9 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC */ public static T getBean(Class clz) throws BeansException { - T result = (T) beanFactory.getBean(clz); - return result; + return beanFactoryRef.get() + .orElseThrow(exceptionSupplier(ConfigurableListableBeanFactory.class, UNINITIALIZED_EXCEPTION)) + .getBean(clz); } /** @@ -71,7 +137,9 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC */ public static boolean containsBean(String name) { - return beanFactory.containsBean(name); + return beanFactoryRef.get() + .orElseThrow(exceptionSupplier(ConfigurableListableBeanFactory.class,UNINITIALIZED_EXCEPTION)) + .containsBean(name); } /** @@ -84,7 +152,9 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC */ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { - return beanFactory.isSingleton(name); + return beanFactoryRef.get() + .orElseThrow(exceptionSupplier(ConfigurableListableBeanFactory.class,UNINITIALIZED_EXCEPTION)) + .isSingleton(name); } /** @@ -95,7 +165,9 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC */ public static Class getType(String name) throws NoSuchBeanDefinitionException { - return beanFactory.getType(name); + return beanFactoryRef.get() + .orElseThrow(exceptionSupplier(ConfigurableListableBeanFactory.class,UNINITIALIZED_EXCEPTION)) + .getType(name); } /** @@ -108,12 +180,14 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC */ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { - return beanFactory.getAliases(name); + return beanFactoryRef.get() + .orElseThrow(exceptionSupplier(ConfigurableListableBeanFactory.class,UNINITIALIZED_EXCEPTION)) + .getAliases(name); } /** * 获取aop代理对象 - * + * * @param invoker * @return */ @@ -130,7 +204,11 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC */ public static String[] getActiveProfiles() { - return applicationContext.getEnvironment().getActiveProfiles(); + return applicationContextRef + .get() + .orElseThrow(exceptionSupplier(ApplicationContext.class, UNINITIALIZED_EXCEPTION)) + .getEnvironment() + .getActiveProfiles(); } /** @@ -153,7 +231,11 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC */ public static String getRequiredProperty(String key) { - return applicationContext.getEnvironment().getRequiredProperty(key); + return applicationContextRef + .get() + .orElseThrow(exceptionSupplier(ApplicationContext.class, UNINITIALIZED_EXCEPTION)) + .getEnvironment() + .getRequiredProperty(key); } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java index f72b8051b..c7799584e 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -1,6 +1,7 @@ package com.ruoyi.framework.aspectj; -import java.util.Objects; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.config.datasource.DynamicDataSourceContextHolder; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -11,9 +12,8 @@ import org.slf4j.LoggerFactory; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; -import com.ruoyi.common.annotation.DataSource; -import com.ruoyi.common.config.datasource.DynamicDataSourceContextHolder; -import com.ruoyi.common.utils.StringUtils; + +import java.util.Objects; /** * 多数据源处理 @@ -34,24 +34,27 @@ public class DataSourceAspect } + // 增加错误捕捉与日志记录 @Around("dsPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { DataSource dataSource = getDataSource(point); - if (StringUtils.isNotNull(dataSource)) + if (Objects.nonNull(dataSource)) { DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); } - - try - { + try { return point.proceed(); - } - finally - { - // 销毁数据源 在执行方法之后 + } catch (Throwable throwable) { + logger.error("Error during data source switch: {}", throwable.getMessage()); + throw throwable; + } finally { DynamicDataSourceContextHolder.clearDataSourceType(); + if(logger.isInfoEnabled()){ + logger.info("Cleared data source setting after method execution"); + } + } }