浏览代码

优化多服务配置合并逻辑;优化服务接口降级逻辑;

Woody 6 天之前
父节点
当前提交
3aaa7f5a32
共有 18 个文件被更改,包括 385 次插入518 次删除
  1. 2 2
      framework-base/src/main/java/com/chelvc/framework/base/config/HttpClientConfigurer.java
  2. 62 3
      framework-base/src/main/java/com/chelvc/framework/base/config/MultipleServerConfigurer.java
  3. 27 325
      framework-base/src/main/java/com/chelvc/framework/base/context/ApplicationContextHolder.java
  4. 228 15
      framework-base/src/main/java/com/chelvc/framework/base/util/SpringUtils.java
  5. 1 0
      framework-base/src/main/resources/META-INF/spring.factories
  6. 4 3
      framework-common/src/main/java/com/chelvc/framework/common/util/JacksonUtils.java
  7. 1 1
      framework-database/src/main/java/com/chelvc/framework/database/config/MybatisConfigurer.java
  8. 0 37
      framework-database/src/main/java/com/chelvc/framework/database/context/DatabaseContextHolder.java
  9. 0 34
      framework-database/src/main/java/com/chelvc/framework/database/context/EntityAddEvent.java
  10. 0 34
      framework-database/src/main/java/com/chelvc/framework/database/context/EntityDeleteEvent.java
  11. 0 38
      framework-database/src/main/java/com/chelvc/framework/database/context/EntityUpdateEvent.java
  12. 19 1
      framework-feign-circuitbreaker/src/main/java/com/chelvc/framework/feign/circuitbreaker/CircuitBreakerInterceptor.java
  13. 9 4
      framework-feign-circuitbreaker/src/main/java/com/chelvc/framework/feign/circuitbreaker/FeignCircuitBreakerInitializer.java
  14. 2 2
      framework-feign/src/main/java/com/chelvc/framework/feign/config/FeignConfigurer.java
  15. 14 8
      framework-nacos/src/main/java/com/chelvc/framework/nacos/config/MultipleNacosConfigurer.java
  16. 6 3
      framework-redis/src/main/java/com/chelvc/framework/redis/config/RedisMQListenerRegistry.java
  17. 7 5
      framework-rocketmq/src/main/java/com/chelvc/framework/rocketmq/config/RocketMQListenerRegistry.java
  18. 3 3
      framework-security/src/main/java/com/chelvc/framework/security/context/SecurityContextHolder.java

+ 2 - 2
framework-base/src/main/java/com/chelvc/framework/base/config/HttpClientConfigurer.java

@@ -2,7 +2,7 @@ package com.chelvc.framework.base.config;
 
 import java.util.concurrent.TimeUnit;
 
-import com.chelvc.framework.base.context.ApplicationContextHolder;
+import com.chelvc.framework.base.util.SpringUtils;
 import lombok.RequiredArgsConstructor;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
@@ -60,7 +60,7 @@ public class HttpClientConfigurer {
         factory.setReadTimeout(this.properties.getSocketTimeout());
         factory.setConnectionRequestTimeout(this.properties.getConnectionRequestTimeout());
         RestTemplate template = new RestTemplate(factory);
-        template.setMessageConverters(ApplicationContextHolder.converters(template.getMessageConverters()));
+        template.setMessageConverters(SpringUtils.customizeConverters(template.getMessageConverters()));
         return template;
     }
 }

+ 62 - 3
framework-base/src/main/java/com/chelvc/framework/base/config/MultipleServerConfigurer.java

@@ -2,12 +2,22 @@ package com.chelvc.framework.base.config;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Properties;
 import java.util.function.Predicate;
 
 import com.chelvc.framework.base.context.ApplicationContextHolder;
+import com.chelvc.framework.base.util.SpringUtils;
+import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
 import com.google.common.collect.Maps;
+import lombok.NonNull;
+import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
+import org.springframework.boot.env.EnvironmentPostProcessor;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.PropertiesPropertySource;
+import org.springframework.core.env.PropertySource;
 import org.springframework.core.io.Resource;
 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
 
@@ -17,19 +27,68 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
  * @author Woody
  * @date 2024/4/3
  */
-public class MultipleServerConfigurer implements WebMvcRegistrations {
+public class MultipleServerConfigurer implements WebMvcRegistrations, EnvironmentPostProcessor {
+    /**
+     * 判断是否启用多服务配置
+     *
+     * @param application Spring应用实例
+     * @return true/false
+     */
+    private boolean isEnabled(@NonNull SpringApplication application) {
+        Class<?> main = application.getMainApplicationClass();
+        if (main != null) {
+            Class<?>[] imports = ObjectUtils.ifNull(main.getAnnotation(Import.class), Import::value);
+            if (ObjectUtils.notEmpty(imports)) {
+                for (Class<?> clazz : imports) {
+                    if (clazz == MultipleServerConfigurer.class) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
     @Override
     public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
-        List<Resource> resources = ApplicationContextHolder.getApplicationResources();
+        List<Resource> resources = SpringUtils.getPropertyResources();
         Map<String, Predicate<Class<?>>> prefixes = Maps.newHashMapWithExpectedSize(resources.size());
         for (Resource resource : resources) {
             String server = ApplicationContextHolder.getApplicationName(resource);
             if (StringUtils.notEmpty(server)) {
-                prefixes.put(server, clazz -> ApplicationContextHolder.isResourceClass(resource, clazz));
+                prefixes.put(server, clazz -> SpringUtils.isResourceClass(resource, clazz));
             }
         }
         RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();
         mapping.setPathPrefixes(prefixes);
         return mapping;
     }
+
+    @Override
+    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
+        if (!this.isEnabled(application)) {
+            return;
+        }
+
+        Properties properties = null;
+        Class<?> main = application.getMainApplicationClass();
+        List<Resource> resources = SpringUtils.getPropertyResources(environment);
+        for (Resource resource : resources) {
+            // 排除当前应用配置
+            if (main != null && SpringUtils.isResourceClass(resource, main)) {
+                continue;
+            }
+
+            // 合并其他服务配置
+            if (properties == null) {
+                properties = SpringUtils.getResourceProperties(resource);
+            } else {
+                properties.putAll(SpringUtils.getResourceProperties(resource));
+            }
+        }
+        if (ObjectUtils.notEmpty(properties)) {
+            PropertySource<?> source = new PropertiesPropertySource("multipleServerProperties", properties);
+            environment.getPropertySources().addLast(source);
+        }
+    }
 }

+ 27 - 325
framework-base/src/main/java/com/chelvc/framework/base/context/ApplicationContextHolder.java

@@ -1,11 +1,7 @@
 package com.chelvc.framework.base.context;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.lang.ref.WeakReference;
-import java.nio.charset.StandardCharsets;
 import java.time.Duration;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
@@ -18,10 +14,6 @@ import java.util.Properties;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import com.chelvc.framework.base.config.MultipleServerConfigurer;
 import com.chelvc.framework.base.util.SpringUtils;
@@ -35,41 +27,22 @@ import com.chelvc.framework.common.util.JacksonUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
 import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.aop.framework.AopProxyUtils;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
-import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.EnvironmentAware;
-import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.i18n.LocaleContextHolder;
-import org.springframework.context.support.GenericApplicationContext;
 import org.springframework.core.Ordered;
-import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.core.annotation.Order;
 import org.springframework.core.env.Environment;
 import org.springframework.core.io.Resource;
-import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
-import org.springframework.core.io.support.ResourcePatternResolver;
-import org.springframework.http.MediaType;
-import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.converter.StringHttpMessageConverter;
-import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
-import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
-import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter;
-import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
 import org.springframework.stereotype.Component;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RestController;
 
 /**
  * 应用上下文工具类
@@ -81,11 +54,6 @@ import org.springframework.web.bind.annotation.RestController;
 @Component
 @Order(Ordered.HIGHEST_PRECEDENCE)
 public class ApplicationContextHolder implements EnvironmentAware, ApplicationContextAware, PropertyRefreshListener {
-    /**
-     * 服务端口属性
-     */
-    public static final String SERVER_PORT_PROPERTY = "server.port";
-
     /**
      * 应用名称属性
      */
@@ -96,30 +64,6 @@ public class ApplicationContextHolder implements EnvironmentAware, ApplicationCo
      */
     public static final String APPLICATION_PROFILE_PROPERTY = "spring.profiles.active";
 
-    /**
-     * bootstrap.yml文件搜索路径
-     */
-    public static final String BOOTSTRAP_YML_PATH =
-            ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/bootstrap.yml";
-
-    /**
-     * bootstrap.properties文件搜索路径
-     */
-    public static final String BOOTSTRAP_PROPERTIES_PATH =
-            ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/bootstrap.properties";
-
-    /**
-     * application.yml文件搜索路径
-     */
-    public static final String APPLICATION_YML_PATH =
-            ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/application.yml";
-
-    /**
-     * application.properties文件搜索路径
-     */
-    public static final String APPLICATION_PROPERTIES_PATH =
-            ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/application.properties";
-
     /**
      * 属性名/对象实例映射表
      */
@@ -152,42 +96,6 @@ public class ApplicationContextHolder implements EnvironmentAware, ApplicationCo
         }
     }
 
-    /**
-     * 获取服务端口
-     *
-     * @return 服务端口
-     */
-    public static Integer getPort() {
-        Environment environment = getEnvironment(false);
-        return environment == null ? null : getPort(environment);
-    }
-
-    /**
-     * 根据 *.yml、*.properties文件资源获取服务端口
-     *
-     * @param resource *.yml、*.properties文件资源对象实例
-     * @return 服务端口
-     */
-    public static Integer getPort(@NonNull Resource resource) {
-        Properties properties = getResourceProperties(resource);
-        if (properties == null) {
-            return null;
-        }
-        String property = properties.getProperty(SERVER_PORT_PROPERTY);
-        return StringUtils.ifEmpty(property, Integer::parseInt);
-    }
-
-    /**
-     * 获取服务端口
-     *
-     * @param environment 应用环境
-     * @return 服务端口
-     */
-    public static Integer getPort(@NonNull Environment environment) {
-        String property = environment.getProperty(SERVER_PORT_PROPERTY);
-        return StringUtils.ifEmpty(property, Integer::parseInt);
-    }
-
     /**
      * 获取环境标识
      *
@@ -205,7 +113,7 @@ public class ApplicationContextHolder implements EnvironmentAware, ApplicationCo
      * @return 环境标识
      */
     public static String getProfile(@NonNull Resource resource) {
-        Properties properties = getResourceProperties(resource);
+        Properties properties = SpringUtils.getResourceProperties(resource);
         return properties == null ? null : properties.getProperty(APPLICATION_PROFILE_PROPERTY);
     }
 
@@ -250,56 +158,56 @@ public class ApplicationContextHolder implements EnvironmentAware, ApplicationCo
     }
 
     /**
-     * 获取应用环境对象
+     * 获取应用名称
      *
-     * @return 应用环境对象
+     * @return 应用名称
      */
-    public static Environment getEnvironment() {
-        return getEnvironment(true);
+    public static String getApplicationName() {
+        Environment environment = getEnvironment(false);
+        return environment == null ? null : getApplicationName(environment);
     }
 
     /**
-     * 获取应用环境对象
+     * 根据 *.yml、*.properties文件资源获取应用名称
      *
-     * @param required 是否必须
-     * @return 应用环境对象
+     * @param resource *.yml、*.properties文件资源对象实例
+     * @return 应用名称
      */
-    public static Environment getEnvironment(boolean required) {
-        if (required) {
-            return AssertUtils.nonnull(ENVIRONMENT, () -> "Application environment has not been initialized");
-        }
-        return ENVIRONMENT;
+    public static String getApplicationName(@NonNull Resource resource) {
+        Properties properties = SpringUtils.getResourceProperties(resource);
+        return properties == null ? null : properties.getProperty(APPLICATION_NAME_PROPERTY);
     }
 
     /**
      * 获取应用名称
      *
+     * @param environment 应用环境
      * @return 应用名称
      */
-    public static String getApplicationName() {
-        Environment environment = getEnvironment(false);
-        return environment == null ? null : getApplicationName(environment);
+    public static String getApplicationName(@NonNull Environment environment) {
+        return environment.getProperty(APPLICATION_NAME_PROPERTY);
     }
 
     /**
-     * 获取应用名称
+     * 获取应用环境对象
      *
-     * @param environment 应用环境
-     * @return 应用名称
+     * @return 应用环境对象
      */
-    public static String getApplicationName(@NonNull Environment environment) {
-        return environment.getProperty(APPLICATION_NAME_PROPERTY);
+    public static Environment getEnvironment() {
+        return getEnvironment(true);
     }
 
     /**
-     * 根据 *.yml、*.properties文件资源获取应用名称
+     * 获取应用环境对象
      *
-     * @param resource *.yml、*.properties文件资源对象实例
-     * @return 应用名称
+     * @param required 是否必须
+     * @return 应用环境对象
      */
-    public static String getApplicationName(@NonNull Resource resource) {
-        Properties properties = getResourceProperties(resource);
-        return properties == null ? null : properties.getProperty(APPLICATION_NAME_PROPERTY);
+    public static Environment getEnvironment(boolean required) {
+        if (required) {
+            return AssertUtils.nonnull(ENVIRONMENT, () -> "Application environment has not been initialized");
+        }
+        return ENVIRONMENT;
     }
 
     /**
@@ -324,212 +232,6 @@ public class ApplicationContextHolder implements EnvironmentAware, ApplicationCo
         return APPLICATION_CONTEXT;
     }
 
-    /**
-     * 获取所有application.yml文件资源
-     *
-     * @return 资源对象数组
-     */
-    public static List<Resource> getApplicationResources() {
-        List<Resource> resources = Lists.newLinkedList();
-        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
-
-        // bootstrap.yml配置文件
-        try {
-            resources.addAll(Arrays.asList(resourcePatternResolver.getResources(BOOTSTRAP_YML_PATH)));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-
-        // 查找application.yml配置文件
-        try {
-            resources.addAll(Arrays.asList(resourcePatternResolver.getResources(APPLICATION_YML_PATH)));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-
-        // bootstrap.properties配置文件
-        try {
-            resources.addAll(Arrays.asList(resourcePatternResolver.getResources(BOOTSTRAP_PROPERTIES_PATH)));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-
-        // 查找application.properties配置文件
-        try {
-            resources.addAll(Arrays.asList(resourcePatternResolver.getResources(APPLICATION_PROPERTIES_PATH)));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        return resources;
-    }
-
-    /**
-     * 判断类对象是否属于指定资源
-     *
-     * @param resource 资源对象
-     * @param clazz    类对象
-     * @return true/false
-     */
-    public static boolean isResourceClass(@NonNull Resource resource, @NonNull Class<?> clazz) {
-        String file = clazz.getProtectionDomain().getCodeSource().getLocation().getPath();
-        try {
-            return resource.getURL().getPath().contains(file);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * 获取资源文件 .yml、.properties属性
-     *
-     * @param resource *.yml、*.properties文件资源对象实例
-     * @return 应用名称
-     */
-    public static Properties getResourceProperties(@NonNull Resource resource) {
-        String filename = resource.getFilename();
-        if (StringUtils.isEmpty(filename) || filename.endsWith(".yml")) {
-            // yml
-            YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
-            yaml.setResources(resource);
-            return yaml.getObject();
-        }
-        // properties
-        Properties properties = new Properties();
-        try (InputStream input = resource.getInputStream()) {
-            properties.load(input);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        return properties;
-    }
-
-    /**
-     * 查找接口对象实例
-     *
-     * @return 接口对象实例列表
-     */
-    public static List<Object> lookupControllers() {
-        return lookupControllers(getApplicationContext());
-    }
-
-    /**
-     * 查找接口对象实例
-     *
-     * @param applicationContext 应用上下文
-     * @return 接口对象实例列表
-     */
-    public static List<Object> lookupControllers(@NonNull ApplicationContext applicationContext) {
-        return Stream.of(applicationContext.getBeansWithAnnotation(Controller.class).values(),
-                applicationContext.getBeansWithAnnotation(RestController.class).values())
-                .flatMap(Collection::stream).distinct().collect(Collectors.toList());
-    }
-
-    /**
-     * 查找类对象所属资源
-     *
-     * @param clazz     类对象
-     * @param resources 资源对象集合
-     * @return 资源对象
-     */
-    public static Resource lookupClassResource(@NonNull Class<?> clazz, @NonNull Collection<Resource> resources) {
-        if (ObjectUtils.isEmpty(resources)) {
-            return null;
-        }
-        return resources.stream().filter(resource -> isResourceClass(resource, clazz)).findAny().orElse(null);
-    }
-
-    /**
-     * 查找应用中的类对象
-     *
-     * @param condition 类对象条件判断
-     * @return 类对象列表
-     */
-    public static List<Class<?>> lookupApplicationClasses(@NonNull Predicate<Class<?>> condition) {
-        return lookupApplicationClasses(getApplicationContext(), condition);
-    }
-
-    /**
-     * 查找应用中的类对象
-     *
-     * @param applicationContext 应用上下文对象
-     * @param condition          类对象条件判断
-     * @return 类对象列表
-     */
-    public static List<Class<?>> lookupApplicationClasses(@NonNull ApplicationContext applicationContext,
-                                                          @NonNull Predicate<Class<?>> condition) {
-        Set<String> packages = Sets.newHashSet();
-        applicationContext.getBeansWithAnnotation(SpringBootApplication.class).forEach((key, value) -> {
-            Class<?> clazz = value.getClass();
-
-            // 加载@ComponentScan包扫描路径配置
-            ComponentScan componentScan = AnnotationUtils.findAnnotation(clazz, ComponentScan.class);
-            if (componentScan != null) {
-                packages.addAll(Arrays.asList(componentScan.basePackages()));
-            }
-
-            // 加载@SpringBootApplication包扫描路径配置
-            SpringBootApplication springBootApplication =
-                    AnnotationUtils.findAnnotation(clazz, SpringBootApplication.class);
-            if (springBootApplication != null) {
-                packages.addAll(Arrays.asList(springBootApplication.scanBasePackages()));
-            }
-        });
-
-        // 根据包扫描路径查找类对象
-        return packages.stream().flatMap(pack -> SpringUtils.lookupClasses(pack, condition).stream())
-                .distinct().collect(Collectors.toList());
-    }
-
-    /**
-     * 注册对象实例
-     *
-     * @param name     对象名称
-     * @param type     对象类型
-     * @param supplier 对象实例提供函数
-     * @param <T>      对象类型泛型
-     * @return 对象实例
-     */
-    public static <T> T register(@NonNull String name, @NonNull Class<T> type, @NonNull Supplier<T> supplier) {
-        ApplicationContext applicationContext = getApplicationContext();
-        ((GenericApplicationContext) getApplicationContext()).registerBean(name, type, supplier);
-        return applicationContext.getBean(name, type);
-    }
-
-    /**
-     * 自定义消息转换处理器
-     *
-     * @param converters 消息转换处理器列表
-     * @return 消息转换处理器列表
-     */
-    public static List<HttpMessageConverter<?>> converters(List<HttpMessageConverter<?>> converters) {
-        if (ObjectUtils.isEmpty(converters)) {
-            return Collections.emptyList();
-        }
-        return converters.stream().map(converter -> {
-            Class<?> clazz = converter.getClass();
-            if (clazz == StringHttpMessageConverter.class) {
-                converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
-            } else if (converter instanceof AbstractJackson2HttpMessageConverter) {
-                ObjectMapper mapper = JacksonUtils.getDefaultSerializer();
-                List<MediaType> types = Lists.newArrayList(converter.getSupportedMediaTypes());
-                if (!types.contains(MediaType.TEXT_PLAIN)) {
-                    types.add(MediaType.TEXT_PLAIN);
-                }
-                if (clazz == MappingJackson2HttpMessageConverter.class) {
-                    converter = new MappingJackson2HttpMessageConverter(mapper);
-                } else if (clazz == MappingJackson2XmlHttpMessageConverter.class) {
-                    converter = new MappingJackson2XmlHttpMessageConverter(mapper);
-                } else if (clazz == MappingJackson2CborHttpMessageConverter.class) {
-                    converter = new MappingJackson2CborHttpMessageConverter(mapper);
-                } else if (clazz == MappingJackson2SmileHttpMessageConverter.class) {
-                    converter = new MappingJackson2SmileHttpMessageConverter(mapper);
-                }
-                ((AbstractJackson2HttpMessageConverter) converter).setSupportedMediaTypes(types);
-            }
-            return converter;
-        }).collect(Collectors.toList());
-    }
-
     /**
      * 基于@Order注解对类对象排序
      *

+ 228 - 15
framework-base/src/main/java/com/chelvc/framework/base/util/SpringUtils.java

@@ -2,26 +2,49 @@ package com.chelvc.framework.base.util;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import com.chelvc.framework.common.util.JacksonUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.env.Environment;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 import org.springframework.core.io.support.ResourcePatternResolver;
 import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
 import org.springframework.core.type.classreading.MetadataReader;
 import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
+import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter;
+import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
 import org.springframework.stereotype.Controller;
 import org.springframework.util.AntPathMatcher;
 import org.springframework.util.ClassUtils;
@@ -257,39 +280,150 @@ public final class SpringUtils {
     }
 
     /**
-     * 查找工程内部的对象类型
+     * 获取系统资源
+     *
+     * @param locations 资源位置数组
+     * @return 资源对象列表
+     */
+    public static List<Resource> getResources(@NonNull String... locations) {
+        List<Resource> resources = Lists.newLinkedList();
+        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
+        for (String location : locations) {
+            try {
+                resources.addAll(Arrays.asList(resourcePatternResolver.getResources(location)));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return resources.isEmpty() ? Collections.emptyList() : resources;
+    }
+
+    /**
+     * 判断类对象是否属于指定资源
+     *
+     * @param resource 资源对象
+     * @param clazz    类对象
+     * @return true/false
+     */
+    public static boolean isResourceClass(@NonNull Resource resource, @NonNull Class<?> clazz) {
+        String file = clazz.getProtectionDomain().getCodeSource().getLocation().getPath();
+        try {
+            return resource.getURL().getPath().contains(file);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 获取类对象所属资源
+     *
+     * @param clazz     类对象
+     * @param resources 资源对象集合
+     * @return 资源对象
+     */
+    public static Resource getClassResource(@NonNull Class<?> clazz, @NonNull Collection<Resource> resources) {
+        if (ObjectUtils.isEmpty(resources)) {
+            return null;
+        }
+        return resources.stream().filter(resource -> isResourceClass(resource, clazz)).findAny().orElse(null);
+    }
+
+    /**
+     * 获取资源配置属性
+     *
+     * @param resource *.yml、*.properties文件资源对象实例
+     * @return 属性配置对象
+     */
+    public static Properties getResourceProperties(@NonNull Resource resource) {
+        String filename = resource.getFilename();
+        if (StringUtils.isEmpty(filename) || filename.endsWith(".yml")) {
+            // yml
+            YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
+            yaml.setResources(resource);
+            return yaml.getObject();
+        }
+        // properties
+        Properties properties = new Properties();
+        try (InputStream input = resource.getInputStream()) {
+            properties.load(input);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        return properties;
+    }
+
+    /**
+     * 获取配置资源
+     *
+     * @return 资源对象列表
+     */
+    public static List<Resource> getPropertyResources() {
+        return getResources(
+                "classpath*:/bootstrap.yml", "classpath*:/config/bootstrap.yml",
+                "classpath*:/bootstrap.properties", "classpath*:/config/bootstrap.properties",
+                "classpath*:/application.yml", "classpath*:/config/application.yml",
+                "classpath*:/application.properties", "classpath*:/config/application.properties"
+        );
+    }
+
+    /**
+     * 获取配置资源
+     *
+     * @param environment 应用环境
+     * @return 资源对象列表
+     */
+    public static List<Resource> getPropertyResources(@NonNull Environment environment) {
+        String[] profiles = environment.getActiveProfiles();
+        if (ObjectUtils.isEmpty(profiles)) {
+            return getResources(
+                    "classpath*:/bootstrap.yml", "classpath*:/config/bootstrap.yml",
+                    "classpath*:/bootstrap.properties", "classpath*:/config/bootstrap.properties"
+            );
+        }
+
+        int n = 0;
+        String[] locations = new String[profiles.length * 4 + 4];
+        locations[n++] = "classpath*:/application.yml";
+        locations[n++] = "classpath*:/config/application.yml";
+        locations[n++] = "classpath*:/application.properties";
+        locations[n++] = "classpath*:/config/application.properties";
+        for (String profile : profiles) {
+            locations[n++] = "classpath*:/application-" + profile + ".yml";
+            locations[n++] = "classpath*:/config/application-" + profile + ".yml";
+            locations[n++] = "classpath*:/application-" + profile + ".properties";
+            locations[n++] = "classpath*:/config/application-" + profile + ".properties";
+        }
+        return getResources(locations);
+    }
+
+    /**
+     * 获取类对象
      *
      * @param pack 包路径
      * @return 类对象列表
      */
-    public static List<Class<?>> lookupClasses(String pack) {
-        return lookupClasses(pack, clazz -> true);
+    public static List<Class<?>> getClasses(String pack) {
+        return getClasses(pack, clazz -> true);
     }
 
     /**
-     * 查找工程内部的对象类型
+     * 获取类对象
      *
      * @param pack      包路径
      * @param condition 类对象条件判断
      * @return 类对象列表
      */
-    public static List<Class<?>> lookupClasses(String pack, @NonNull Predicate<Class<?>> condition) {
+    public static List<Class<?>> getClasses(String pack, @NonNull Predicate<Class<?>> condition) {
+        // 获取class资源文件
         String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                 (pack == null || (pack = pack.trim()).isEmpty() ? "**/*.class" :
                         (ClassUtils.convertClassNameToResourcePath(pack) + "/**/*.class"));
-        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
-
-        // 获取对象资源文件
-        Resource[] resources;
-        try {
-            resources = resourcePatternResolver.getResources(pattern);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        List<Resource> resources = getResources(pattern);
 
         // 将资源文件转换为类对象
         Stream.Builder<Class<?>> classes = Stream.builder();
-        MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
+        MetadataReaderFactory readerFactory =
+                new CachingMetadataReaderFactory(new PathMatchingResourcePatternResolver());
         ClassLoader classLoader = Objects.requireNonNull(ClassUtils.getDefaultClassLoader());
         for (Resource resource : resources) {
             // 加载类对象
@@ -306,4 +440,83 @@ public final class SpringUtils {
         }
         return classes.build().distinct().collect(Collectors.toList());
     }
+
+    /**
+     * 获取类对象
+     *
+     * @param applicationContext 应用上下文对象
+     * @param condition          类对象条件判断
+     * @return 类对象列表
+     */
+    public static List<Class<?>> getClasses(@NonNull ApplicationContext applicationContext,
+                                            @NonNull Predicate<Class<?>> condition) {
+        Set<String> packages = Sets.newHashSet();
+        applicationContext.getBeansWithAnnotation(SpringBootApplication.class).forEach((key, value) -> {
+            Class<?> clazz = value.getClass();
+
+            // 加载@ComponentScan包扫描路径配置
+            ComponentScan scan = AnnotationUtils.findAnnotation(clazz, ComponentScan.class);
+            if (scan != null) {
+                packages.addAll(Arrays.asList(scan.basePackages()));
+            }
+
+            // 加载@SpringBootApplication包扫描路径配置
+            SpringBootApplication application = AnnotationUtils.findAnnotation(clazz, SpringBootApplication.class);
+            if (application != null) {
+                packages.addAll(Arrays.asList(application.scanBasePackages()));
+            }
+        });
+
+        // 根据包扫描路径查找类对象
+        return packages.stream().flatMap(pack -> getClasses(pack, condition).stream())
+                .distinct().collect(Collectors.toList());
+    }
+
+    /**
+     * 获取接口对象实例
+     *
+     * @param applicationContext 应用上下文
+     * @return 接口对象实例列表
+     */
+    public static List<Object> getControllers(@NonNull ApplicationContext applicationContext) {
+        return Stream.of(applicationContext.getBeansWithAnnotation(Controller.class).values(),
+                applicationContext.getBeansWithAnnotation(RestController.class).values())
+                .flatMap(Collection::stream).distinct().collect(Collectors.toList());
+    }
+
+    /**
+     * 自定义消息转换处理器
+     *
+     * @param converters 消息转换处理器列表
+     * @return 消息转换处理器列表
+     */
+    public static List<HttpMessageConverter<?>> customizeConverters(List<HttpMessageConverter<?>> converters) {
+        if (ObjectUtils.isEmpty(converters)) {
+            return Collections.emptyList();
+        }
+
+        return converters.stream().map(converter -> {
+            Class<?> clazz = converter.getClass();
+            if (clazz == StringHttpMessageConverter.class) {
+                converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
+            } else if (converter instanceof AbstractJackson2HttpMessageConverter) {
+                ObjectMapper mapper = JacksonUtils.getDefaultSerializer();
+                List<MediaType> types = Lists.newArrayList(converter.getSupportedMediaTypes());
+                if (!types.contains(MediaType.TEXT_PLAIN)) {
+                    types.add(MediaType.TEXT_PLAIN);
+                }
+                if (clazz == MappingJackson2HttpMessageConverter.class) {
+                    converter = new MappingJackson2HttpMessageConverter(mapper);
+                } else if (clazz == MappingJackson2XmlHttpMessageConverter.class) {
+                    converter = new MappingJackson2XmlHttpMessageConverter(mapper);
+                } else if (clazz == MappingJackson2CborHttpMessageConverter.class) {
+                    converter = new MappingJackson2CborHttpMessageConverter(mapper);
+                } else if (clazz == MappingJackson2SmileHttpMessageConverter.class) {
+                    converter = new MappingJackson2SmileHttpMessageConverter(mapper);
+                }
+                ((AbstractJackson2HttpMessageConverter) converter).setSupportedMediaTypes(types);
+            }
+            return converter;
+        }).collect(Collectors.toList());
+    }
 }

+ 1 - 0
framework-base/src/main/resources/META-INF/spring.factories

@@ -0,0 +1 @@
+org.springframework.boot.env.EnvironmentPostProcessor=com.chelvc.framework.base.config.MultipleServerConfigurer

+ 4 - 3
framework-common/src/main/java/com/chelvc/framework/common/util/JacksonUtils.java

@@ -481,11 +481,11 @@ public final class JacksonUtils {
     }
 
     /**
-     * 构建序列化处理器配置模块
+     * 构建序列化处理器数字模块
      *
      * @return 模块实例
      */
-    private static SimpleModule generateConfigureModule() {
+    private static SimpleModule generateNumberModule() {
         SimpleModule module = new SimpleModule();
         module.addDeserializer(long.class, new JsonDeserializer<Long>() {
             @Override
@@ -639,7 +639,7 @@ public final class JacksonUtils {
         if (NUMBER_MODULE == null) {
             synchronized (SimpleModule.class) {
                 if (NUMBER_MODULE == null) {
-                    NUMBER_MODULE = generateConfigureModule();
+                    NUMBER_MODULE = generateNumberModule();
                 }
             }
         }
@@ -673,6 +673,7 @@ public final class JacksonUtils {
                 .configure(SerializationFeature.INDENT_OUTPUT, false)
                 .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
                 .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
+                .configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true)
                 .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                 .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
         mapper.registerModule(initializeNumberModule());

+ 1 - 1
framework-database/src/main/java/com/chelvc/framework/database/config/MybatisConfigurer.java

@@ -195,7 +195,7 @@ public abstract class MybatisConfigurer {
     private static List<MybatisConfigurer> initializeInterceptorInstances(Configuration configuration)
             throws Exception {
         // 查找当前包目录下MybatisConfigureInterceptor实现
-        List<Class<?>> classes = SpringUtils.lookupClasses(
+        List<Class<?>> classes = SpringUtils.getClasses(
                 MybatisConfigurer.class.getPackage().getName(),
                 clazz -> clazz != MybatisConfigurer.class && MybatisConfigurer.class.isAssignableFrom(clazz)
         );

+ 0 - 37
framework-database/src/main/java/com/chelvc/framework/database/context/DatabaseContextHolder.java

@@ -1,6 +1,5 @@
 package com.chelvc.framework.database.context;
 
-import java.lang.reflect.Modifier;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -13,7 +12,6 @@ import java.util.function.Supplier;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.chelvc.framework.base.context.ApplicationContextHolder;
-import com.chelvc.framework.base.util.SpringUtils;
 import com.chelvc.framework.common.function.Executor;
 import com.chelvc.framework.common.function.Handler;
 import com.chelvc.framework.common.function.Provider;
@@ -21,10 +19,8 @@ import com.chelvc.framework.common.model.Pagination;
 import com.chelvc.framework.common.model.Paging;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.database.crypto.DatabaseCipherHandler;
-import com.chelvc.framework.database.entity.Entity;
 import lombok.NonNull;
 import org.mybatis.spring.SqlSessionTemplate;
-import org.springframework.context.ApplicationContext;
 import org.springframework.dao.DuplicateKeyException;
 
 /**
@@ -222,39 +218,6 @@ public final class DatabaseContextHolder {
         }
     }
 
-    /**
-     * 根据包名查找数据模型对象
-     *
-     * @return 数据模型对象列表
-     */
-    public static List<Class<?>> lookupEntityClasses() {
-        return ApplicationContextHolder.lookupApplicationClasses(clazz -> Entity.class.isAssignableFrom(clazz)
-                && !Modifier.isInterface(clazz.getModifiers()) && !Modifier.isAbstract(clazz.getModifiers()));
-    }
-
-    /**
-     * 根据包名查找数据模型对象
-     *
-     * @param pack 包路径
-     * @return 数据模型对象列表
-     */
-    public static List<Class<?>> lookupEntityClasses(String pack) {
-        return SpringUtils.lookupClasses(pack, clazz -> Entity.class.isAssignableFrom(clazz)
-                && !Modifier.isInterface(clazz.getModifiers()) && !Modifier.isAbstract(clazz.getModifiers()));
-    }
-
-    /**
-     * 根据包名查找数据模型对象
-     *
-     * @param applicationContext 应用上下文
-     * @return 数据模型对象列表
-     */
-    public static List<Class<?>> lookupEntityClasses(@NonNull ApplicationContext applicationContext) {
-        return ApplicationContextHolder.lookupApplicationClasses(applicationContext,
-                clazz -> Entity.class.isAssignableFrom(clazz) && !Modifier.isInterface(clazz.getModifiers())
-                        && !Modifier.isAbstract(clazz.getModifiers()));
-    }
-
     /**
      * 将分页请求参数转换成MyBatis Plus分页参数
      *

+ 0 - 34
framework-database/src/main/java/com/chelvc/framework/database/context/EntityAddEvent.java

@@ -1,34 +0,0 @@
-package com.chelvc.framework.database.context;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import com.chelvc.framework.database.entity.Entity;
-import lombok.NonNull;
-
-/**
- * 实体增加事件
- *
- * @author Woody
- * @date 2024/6/30
- */
-public class EntityAddEvent extends EntityEvent {
-    public EntityAddEvent(@NonNull Entity<?>... entities) {
-        this(Arrays.asList(entities));
-    }
-
-    public EntityAddEvent(@NonNull List<Entity<?>> entities) {
-        super(Collections.unmodifiableList(entities));
-    }
-
-    /**
-     * 获取增加实体
-     *
-     * @return 实体对象实例列表
-     */
-    @SuppressWarnings("unchecked")
-    public List<Entity<?>> getEntities() {
-        return (List<Entity<?>>) this.source;
-    }
-}

+ 0 - 34
framework-database/src/main/java/com/chelvc/framework/database/context/EntityDeleteEvent.java

@@ -1,34 +0,0 @@
-package com.chelvc.framework.database.context;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import com.chelvc.framework.database.entity.Entity;
-import lombok.NonNull;
-
-/**
- * 实体删除事件
- *
- * @author Woody
- * @date 2024/6/30
- */
-public class EntityDeleteEvent extends EntityEvent {
-    public EntityDeleteEvent(@NonNull Entity<?>... entities) {
-        this(Arrays.asList(entities));
-    }
-
-    public EntityDeleteEvent(@NonNull List<Entity<?>> entities) {
-        super(Collections.unmodifiableList(entities));
-    }
-
-    /**
-     * 获取删除实体
-     *
-     * @return 实体对象实例列表
-     */
-    @SuppressWarnings("unchecked")
-    public List<Entity<?>> getEntities() {
-        return (List<Entity<?>>) this.source;
-    }
-}

+ 0 - 38
framework-database/src/main/java/com/chelvc/framework/database/context/EntityUpdateEvent.java

@@ -1,38 +0,0 @@
-package com.chelvc.framework.database.context;
-
-import java.util.Collections;
-import java.util.List;
-
-import com.chelvc.framework.database.entity.Entity;
-import lombok.NonNull;
-import org.apache.commons.lang3.tuple.Pair;
-
-/**
- * 实体修改事件
- *
- * @author Woody
- * @date 2024/6/30
- */
-public class EntityUpdateEvent extends EntityEvent {
-    public EntityUpdateEvent(@NonNull Entity<?> entity) {
-        this(entity, entity);
-    }
-
-    public EntityUpdateEvent(@NonNull Entity<?> before, @NonNull Entity<?> after) {
-        this(Collections.singletonList(Pair.of(before, after)));
-    }
-
-    public EntityUpdateEvent(@NonNull List<Pair<Entity<?>, Entity<?>>> entities) {
-        super(Collections.unmodifiableList(entities));
-    }
-
-    /**
-     * 获取修改实体
-     *
-     * @return 实体对象实例列表
-     */
-    @SuppressWarnings("unchecked")
-    public List<Pair<Entity<?>, Entity<?>>> getEntities() {
-        return (List<Pair<Entity<?>, Entity<?>>>) this.source;
-    }
-}

+ 19 - 1
framework-feign-circuitbreaker/src/main/java/com/chelvc/framework/feign/circuitbreaker/CircuitBreakerInterceptor.java

@@ -1,8 +1,13 @@
 package com.chelvc.framework.feign.circuitbreaker;
 
 import com.chelvc.framework.base.context.ApplicationContextHolder;
+import com.chelvc.framework.common.exception.FrameworkException;
 import io.github.resilience4j.circuitbreaker.CircuitBreaker;
 import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerAspectExt;
+import io.github.resilience4j.fallback.DefaultFallbackDecorator;
+import io.github.resilience4j.fallback.FallbackMethod;
+import io.github.resilience4j.timelimiter.configure.IllegalReturnTypeException;
+import io.vavr.CheckedFunction0;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.springframework.stereotype.Component;
 
@@ -13,12 +18,25 @@ import org.springframework.stereotype.Component;
  * @date 2025/4/15
  */
 @Component
-public class CircuitBreakerInterceptor implements CircuitBreakerAspectExt {
+public class CircuitBreakerInterceptor extends DefaultFallbackDecorator implements CircuitBreakerAspectExt {
     @Override
     public boolean canHandleReturnType(Class returnType) {
         return true;
     }
 
+    @Override
+    public CheckedFunction0<Object> decorate(FallbackMethod fallbackMethod, CheckedFunction0<Object> supplier) {
+        return () -> {
+            try {
+                return supplier.apply();
+            } catch (IllegalReturnTypeException | FrameworkException e) {
+                throw e;
+            } catch (Throwable t) {
+                return fallbackMethod.fallback(t);
+            }
+        };
+    }
+
     @Override
     public Object handle(ProceedingJoinPoint point, CircuitBreaker circuitBreaker, String method) throws Throwable {
         boolean enabled = ApplicationContextHolder.getProperty("circuit.breaker.enabled", boolean.class, true);

+ 9 - 4
framework-feign-circuitbreaker/src/main/java/com/chelvc/framework/feign/circuitbreaker/FeignCircuitBreakerInitializer.java

@@ -44,9 +44,10 @@ public class FeignCircuitBreakerInitializer implements SpringApplicationRunListe
 
     public FeignCircuitBreakerInitializer(SpringApplication application, String[] args) {
         Class<?> main = application.getMainApplicationClass();
-        EnableFeignCircuitBreaker configuration = main.getAnnotation(EnableFeignCircuitBreaker.class);
-        this.enabled = configuration != null && main.isAnnotationPresent(EnableFeignClients.class);
+        this.enabled = main != null && main.isAnnotationPresent(EnableFeignClients.class)
+                && main.isAnnotationPresent(EnableFeignCircuitBreaker.class);
         if (this.enabled && !INITIALIZED) {
+            EnableFeignCircuitBreaker configuration = main.getAnnotation(EnableFeignCircuitBreaker.class);
             SERVERS = ObjectUtils.ifEmpty(configuration.servers(), Sets::newHashSet, Collections::emptySet);
         }
     }
@@ -84,10 +85,14 @@ public class FeignCircuitBreakerInitializer implements SpringApplicationRunListe
     private static String type2return(Class<?> type) {
         if (type == void.class) {
             return StringUtils.EMPTY;
+        } else if (type == long.class) {
+            return "return 0L;";
+        } else if (type == float.class) {
+            return "return 0.0F;";
+        } else if (type == double.class) {
+            return "return 0.00D;";
         } else if (type == boolean.class) {
             return "return false;";
-        } else if (type == Boolean.class) {
-            return "return java.lang.Boolean.FALSE;";
         } else if (type.isPrimitive()) {
             return "return 0;";
         } else if (type.isArray()) {

+ 2 - 2
framework-feign/src/main/java/com/chelvc/framework/feign/config/FeignConfigurer.java

@@ -1,6 +1,6 @@
 package com.chelvc.framework.feign.config;
 
-import com.chelvc.framework.base.context.ApplicationContextHolder;
+import com.chelvc.framework.base.util.SpringUtils;
 import com.chelvc.framework.feign.encoder.CustomizeEncoder;
 import com.chelvc.framework.feign.encoder.CustomizeQueryEncoder;
 import com.chelvc.framework.feign.interceptor.FeignFailureInterceptor;
@@ -44,7 +44,7 @@ public class FeignConfigurer {
     private ObjectFactory<HttpMessageConverters> converters(HttpMessageConverters converters) {
         return new ObjectFactory<HttpMessageConverters>() {
             private final HttpMessageConverters instance =
-                    new HttpMessageConverters(ApplicationContextHolder.converters(converters.getConverters()));
+                    new HttpMessageConverters(SpringUtils.customizeConverters(converters.getConverters()));
 
             @Override
             public HttpMessageConverters getObject() throws BeansException {

+ 14 - 8
framework-nacos/src/main/java/com/chelvc/framework/nacos/config/MultipleNacosConfigurer.java

@@ -1,15 +1,21 @@
 package com.chelvc.framework.nacos.config;
 
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
 import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
 import com.alibaba.cloud.nacos.registry.NacosRegistration;
 import com.alibaba.cloud.nacos.registry.NacosServiceRegistry;
 import com.chelvc.framework.base.context.ApplicationContextHolder;
+import com.chelvc.framework.base.util.SpringUtils;
 import com.chelvc.framework.common.util.StringUtils;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.context.event.ApplicationStartedEvent;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationListener;
+import org.springframework.core.env.Environment;
 import org.springframework.core.io.Resource;
 
 /**
@@ -28,15 +34,15 @@ public class MultipleNacosConfigurer implements ApplicationListener<ApplicationS
     @Override
     public void onApplicationEvent(ApplicationStartedEvent event) {
         ApplicationContext applicationContext = event.getApplicationContext();
-        String applicationName = ApplicationContextHolder.getApplicationName(applicationContext.getEnvironment());
-        for (Resource resource : ApplicationContextHolder.getApplicationResources()) {
-            // 将打包的服务注册到nacos(排除当前服务)
-            String service = ApplicationContextHolder.getApplicationName(resource);
-            if (StringUtils.isEmpty(service) || service.equals(applicationName)) {
-                continue;
-            }
+        Environment environment = applicationContext.getEnvironment();
+        List<Resource> resources = SpringUtils.getPropertyResources(environment);
+        String application = ApplicationContextHolder.getApplicationName(environment);
+        Set<String> services = resources.stream().map(ApplicationContextHolder::getApplicationName)
+                .filter(service -> StringUtils.notEmpty(service) && !service.equals(application))
+                .collect(Collectors.toSet());
+        services.forEach(service -> {
             this.properties.setService(service);
             this.registry.register(new NacosRegistration(null, this.properties, applicationContext));
-        }
+        });
     }
 }

+ 6 - 3
framework-redis/src/main/java/com/chelvc/framework/redis/config/RedisMQListenerRegistry.java

@@ -15,6 +15,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.aop.framework.AopProxyUtils;
 import org.springframework.boot.context.event.ApplicationStartedEvent;
 import org.springframework.context.ApplicationListener;
+import org.springframework.context.support.GenericApplicationContext;
 
 /**
  * Redis消息监听器注册器
@@ -32,9 +33,11 @@ public class RedisMQListenerRegistry implements ApplicationListener<ApplicationS
      */
     @SuppressWarnings("unchecked")
     protected <T> RedisMQListenerContainer<T> createListenerContainer() {
-        return ApplicationContextHolder.register(
-                StringUtils.uuid(), RedisMQListenerContainer.class, DefaultRedisMQListenerContainer::new
-        );
+        String name = StringUtils.uuid();
+        GenericApplicationContext applicationContext =
+                (GenericApplicationContext) ApplicationContextHolder.getApplicationContext();
+        applicationContext.registerBean(name, RedisMQListenerContainer.class, DefaultRedisMQListenerContainer::new);
+        return applicationContext.getBean(name, RedisMQListenerContainer.class);
     }
 
     /**

+ 7 - 5
framework-rocketmq/src/main/java/com/chelvc/framework/rocketmq/config/RocketMQListenerRegistry.java

@@ -18,6 +18,7 @@ import org.apache.rocketmq.client.apis.ClientServiceProvider;
 import org.springframework.aop.framework.AopProxyUtils;
 import org.springframework.boot.context.event.ApplicationStartedEvent;
 import org.springframework.context.ApplicationListener;
+import org.springframework.context.support.GenericApplicationContext;
 
 /**
  * RocketMQ消息监听器注册器
@@ -45,11 +46,12 @@ public class RocketMQListenerRegistry implements ApplicationListener<Application
      */
     @SuppressWarnings("unchecked")
     protected <T> RocketMQListenerContainer<T> createListenerContainer(boolean multiple) {
-        return ApplicationContextHolder.register(
-                StringUtils.uuid(),
-                RocketMQListenerContainer.class,
-                () -> multiple ? new MultipleRocketMQListenerContainer<>() : new SingleRocketMQListenerContainer<>()
-        );
+        String name = StringUtils.uuid();
+        GenericApplicationContext applicationContext =
+                (GenericApplicationContext) ApplicationContextHolder.getApplicationContext();
+        applicationContext.registerBean(name, RocketMQListenerContainer.class,
+                () -> multiple ? new MultipleRocketMQListenerContainer<>() : new SingleRocketMQListenerContainer<>());
+        return applicationContext.getBean(name, RocketMQListenerContainer.class);
     }
 
     /**

+ 3 - 3
framework-security/src/main/java/com/chelvc/framework/security/context/SecurityContextHolder.java

@@ -189,14 +189,14 @@ public final class SecurityContextHolder {
         Set<String> ignores = Sets.newHashSet();
         boolean multiple = ApplicationContextHolder.isMultipleServer(applicationContext);
         List<Resource> resources = multiple ?
-                ApplicationContextHolder.getApplicationResources() : Collections.emptyList();
-        ApplicationContextHolder.lookupControllers(applicationContext).forEach(controller -> {
+                SpringUtils.getPropertyResources(applicationContext.getEnvironment()) : Collections.emptyList();
+        SpringUtils.getControllers(applicationContext).forEach(controller -> {
             Class<?> clazz = AopProxyUtils.ultimateTargetClass(controller);
 
             // 如果启用多服务MVC配置则将服务名作为接口地址前缀
             String prefix = null;
             if (multiple) {
-                Resource resource = ApplicationContextHolder.lookupClassResource(clazz, resources);
+                Resource resource = SpringUtils.getClassResource(clazz, resources);
                 prefix = resource == null ? null : ApplicationContextHolder.getApplicationName(resource);
             }