From 70141f58d2081d73e85dbff250ebc3b5aa1d7b18 Mon Sep 17 00:00:00 2001 From: caochaojie <258920553@qq.com> Date: Thu, 22 Jan 2026 15:42:04 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat(profile):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E9=9D=99=E6=80=81=E7=B1=BB?= =?UTF-8?q?=E7=BB=91=E5=AE=9A=E5=8A=9F=E8=83=BD,=E5=8F=AF=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=E7=8E=AF=E5=A2=83=E5=8A=A0=E8=BD=BD=E7=89=B9=E5=AE=9A?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 ProfileStaticBinder 类实现配置到静态字段的自动绑定 - 新增 DbProps 测试类用于验证静态绑定功能 - 扩展 ProfileManager 支持特定环境配置文件加载 - 重构 Profile.load 方法提取为独立方法便于复用 - 新增 ResourcePatternResolverProfile.getFileURL 方法按文件名查找资源 --- .../game/widget/light/profile/Profile.java | 27 ++++---- .../widget/light/profile/ProfileManager.java | 33 ++++++++++ .../light/profile/ProfileStaticBinder.java | 64 +++++++++++++++++++ .../ResourcePatternResolverProfile.java | 60 ++++++++++++++--- .../ResourcePatternResolverProfileTest.java | 11 ++++ .../widget/light/profile/bean/DbProps.java | 10 +++ 6 files changed, 184 insertions(+), 21 deletions(-) create mode 100644 widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java create mode 100644 widget/light-profile/src/test/java/com/iohao/game/widget/light/profile/bean/DbProps.java diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java index d8ab178de..cf7d36641 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java @@ -149,18 +149,21 @@ public void load(Properties properties) { */ public void load(List urls) { // 需要加载的配置文件 - urls.forEach(url -> { - - try (InputStream inputStream = url.openStream()) { - Properties properties = new Properties(); - properties.load(inputStream); - - this.load(properties); - - } catch (IOException e) { - log.error(e.getMessage(), e); - } - }); + urls.forEach(this::load); } + /** + * 需要加载的配置文件 + * + */ + public void load(URL url) { + // 需要加载的配置文件 + try (InputStream inputStream = url.openStream()) { + Properties properties = new Properties(); + properties.load(inputStream); + this.load(properties); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } } diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java index e3e283a09..b314698a2 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java @@ -21,6 +21,7 @@ import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; +import org.springframework.core.convert.support.DefaultConversionService; import java.net.URL; import java.util.*; @@ -48,6 +49,7 @@ public final class ProfileManager { */ private final Map profileMap = new ConcurrentHashMap<>(); + private static final DefaultConversionService CONVERTER = new DefaultConversionService(); public Profile profile() { return profile(MAIN_CONFIG); @@ -115,5 +117,36 @@ public void loadMainProfile(String profileConfigName) { } + /** + * 加载特定环境的配置文件 + * + * @param env 环境名称 + * @param fileName 配置文件名 (不需要带 .props 后缀) + */ + public Profile loadEnvFile(String env, String fileName) { + if (StringUtils.isEmpty(env) || StringUtils.isEmpty(fileName)) { + log.warn("环境或文件名为空,跳过加载"); + return null; + } + URL fileURL = ResourcePatternResolverProfile.getFileURL(env, fileName); + Profile profile = profile(fileName); + profile.load(fileURL); + log.debug("配置内容 - size:{} - {}", profile.map.size(), profile.map); + return profile; + } + + public void loadEnvFileFillStaticClass(String env, String fileName, Class staticClass) { + if (StringUtils.isEmpty(env) || StringUtils.isEmpty(fileName)) { + log.warn("环境或文件名为空,跳过加载"); + return; + } + Profile profile = loadEnvFile(env, fileName); + if (Objects.isNull(profile)) { + log.warn("加载配置文件失败,跳过加载"); + return; + } + ProfileStaticBinder.bind(staticClass, profile.map); + } + } diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java new file mode 100644 index 000000000..fa3f93d0e --- /dev/null +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java @@ -0,0 +1,64 @@ +package com.iohao.game.widget.light.profile; + +import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Map; + +/** + * @Author: caochaojie + * @Date: 2026-01-2215:29 + */ +public class ProfileStaticBinder { + + // Spring 标准类型转换器:支持 String -> int/long/boolean/Enum/Array 等 + private static final DefaultConversionService CONVERTER = new DefaultConversionService(); + + public static void bind(Class targetClass, Map configMap) { + if (configMap == null || configMap.isEmpty()) { + return; + } + + // 获取类中定义的全部字段 + Field[] fields = targetClass.getDeclaredFields(); + + configMap.forEach((key, value) -> { + if (key == null || value == null) return; + + String configKey = key.toString(); + // 归一化处理:去掉下划线并转为小写 + String normalizedConfigKey = normalize(configKey); + + for (Field field : fields) { + int mod = field.getModifiers(); + + // 仅处理 public static 字段 + if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) { + String fieldName = field.getName(); + + // 匹配规则:原始名一致 OR 归一化后一致 + if (fieldName.equalsIgnoreCase(configKey) || normalize(fieldName).equals(normalizedConfigKey)) { + try { + field.setAccessible(true); + Object convertedValue = CONVERTER.convert(value, field.getType()); + field.set(null, convertedValue); + } catch (Exception e) { + System.err.printf("[Config] 字段 [%s] 赋值失败: %s%n", fieldName, e.getMessage()); + } + break; // 匹配到一个字段后跳出内层循环 + } + } + } + }); + } + + /** + * 归一化处理:移除所有下划线并转为小写 + * 例如: SERVER_ID -> serverid, serverId -> serverid, server_id -> serverid + */ + private static String normalize(String name) { + return name.replace("_", "").toLowerCase(); + } +} \ No newline at end of file diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ResourcePatternResolverProfile.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ResourcePatternResolverProfile.java index 1b04a60f2..65b98ba35 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ResourcePatternResolverProfile.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ResourcePatternResolverProfile.java @@ -34,17 +34,25 @@ */ @Slf4j class ResourcePatternResolverProfile { - /** 默认主目录 */ + /** + * 默认主目录 + */ @Setter private static String rootDir = "conf"; - /** 默认加载主目录-子目录 */ + /** + * 默认加载主目录-子目录 + */ @Setter private static String defaultDir = "common"; - /** 默认加载指定后缀文件 */ + /** + * 默认加载指定后缀文件 + */ @Setter private static String suffix = ".props"; - /** 需要加载的目录列表 */ + /** + * 需要加载的目录列表 + */ private final LinkedList dirNameList = new LinkedList<>(); /** @@ -68,15 +76,11 @@ public List toUrls() { List files = new LinkedList<>(); - String locationPatternTemplate = "classpath*:%s/%s/*%s"; PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { for (String dir : dirNameList) { - String locationPattern = String.format(locationPatternTemplate, rootDir, dir, suffix); - - Resource[] resources = resolver.getResources(locationPattern); - log.debug("locationPattern: {}", locationPattern); + Resource[] resources = getResourcesForDirectory(resolver, dir); for (Resource resource : resources) { URL url = resource.getURL(); files.add(url); @@ -89,4 +93,42 @@ public List toUrls() { return files; } + public static URL getFileURL(String env, String fileName) { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] resources; + try { + resources = getResourcesForDirectory(resolver, env); + } catch (IOException e) { + log.error("Error getting resources for directory: " + env, e); + return null; + } + + for (Resource resource : resources) { + try { + URL url = resource.getURL(); + if (url.getFile().contains(fileName)) { + return url; + } + } catch (IOException e) { + log.warn("Error getting URL for resource: " + resource.getDescription(), e); + // Continue to next resource + } + } + return null; + } + + /** + * 获取指定目录下的资源 + * + * @param resolver 资源解析器 + * @param directory 目录名 + * @return 资源数组 + */ + private static Resource[] getResourcesForDirectory(PathMatchingResourcePatternResolver resolver, String directory) throws IOException { + String locationPatternTemplate = "classpath*:%s/%s/*%s"; + String locationPattern = String.format(locationPatternTemplate, rootDir, directory, suffix); + log.debug("locationPattern: {}", locationPattern); + return resolver.getResources(locationPattern); + } + } diff --git a/widget/light-profile/src/test/java/com/iohao/game/widget/light/profile/ResourcePatternResolverProfileTest.java b/widget/light-profile/src/test/java/com/iohao/game/widget/light/profile/ResourcePatternResolverProfileTest.java index 0ac91f910..e56ee3fd4 100644 --- a/widget/light-profile/src/test/java/com/iohao/game/widget/light/profile/ResourcePatternResolverProfileTest.java +++ b/widget/light-profile/src/test/java/com/iohao/game/widget/light/profile/ResourcePatternResolverProfileTest.java @@ -18,6 +18,7 @@ */ package com.iohao.game.widget.light.profile; +import com.iohao.game.widget.light.profile.bean.DbProps; import lombok.extern.slf4j.Slf4j; import org.junit.Test; @@ -38,7 +39,17 @@ public void loadLocal() { List urls = config.toUrls(); log.info("local profile {}", urls.size()); + } + @Test + public void loadEnvFile() { + ProfileManager.loadEnvFile("local", "db.props"); + } + @Test + public void loadEnvFileStaticBinder() { + ProfileManager.loadEnvFileFillStaticClass("local", "db.props", DbProps.class); + log.info("DbProps.jdbcUser = {}", DbProps.JDBC_USER); + log.info("DbProps.jdbcPwd = {}", DbProps.JDBC_PWD); } } \ No newline at end of file diff --git a/widget/light-profile/src/test/java/com/iohao/game/widget/light/profile/bean/DbProps.java b/widget/light-profile/src/test/java/com/iohao/game/widget/light/profile/bean/DbProps.java new file mode 100644 index 000000000..c77d55fdc --- /dev/null +++ b/widget/light-profile/src/test/java/com/iohao/game/widget/light/profile/bean/DbProps.java @@ -0,0 +1,10 @@ +package com.iohao.game.widget.light.profile.bean; + +/** + * @Author: caochaojie + * @Date: 2026-01-2215:31 + */ +public class DbProps { + public static String JDBC_USER; + public static String JDBC_PWD; +} From 583ac37710d801c0a13b80698414a9f4c019055d Mon Sep 17 00:00:00 2001 From: caochaojie <258920553@qq.com> Date: Thu, 22 Jan 2026 15:44:34 +0800 Subject: [PATCH 2/8] =?UTF-8?q?refactor(profile):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E6=9C=AA=E4=BD=BF=E7=94=A8=E7=9A=84=E8=BD=AC=E6=8D=A2=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=92=8C=E5=8F=8D=E5=B0=84=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/iohao/game/widget/light/profile/ProfileManager.java | 2 -- .../iohao/game/widget/light/profile/ProfileStaticBinder.java | 1 - 2 files changed, 3 deletions(-) diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java index b314698a2..f3c8b7515 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java @@ -49,8 +49,6 @@ public final class ProfileManager { */ private final Map profileMap = new ConcurrentHashMap<>(); - private static final DefaultConversionService CONVERTER = new DefaultConversionService(); - public Profile profile() { return profile(MAIN_CONFIG); } diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java index fa3f93d0e..564741936 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java @@ -1,7 +1,6 @@ package com.iohao.game.widget.light.profile; import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.util.ReflectionUtils; import java.lang.reflect.Field; import java.lang.reflect.Modifier; From 870e36392f0c65bbac16e8320273ace5739f74e1 Mon Sep 17 00:00:00 2001 From: caochaojie <258920553@qq.com> Date: Thu, 22 Jan 2026 15:47:06 +0800 Subject: [PATCH 3/8] =?UTF-8?q?refactor(widget):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E8=AE=BF=E9=97=AE=E6=9D=83=E9=99=90=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E4=BB=A5=E6=8F=90=E5=8D=87=E5=AE=89=E5=85=A8=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/iohao/game/widget/light/profile/ProfileStaticBinder.java | 1 - 1 file changed, 1 deletion(-) diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java index 564741936..8469af1c1 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java @@ -40,7 +40,6 @@ public static void bind(Class targetClass, Map configMap) { // 匹配规则:原始名一致 OR 归一化后一致 if (fieldName.equalsIgnoreCase(configKey) || normalize(fieldName).equals(normalizedConfigKey)) { try { - field.setAccessible(true); Object convertedValue = CONVERTER.convert(value, field.getType()); field.set(null, convertedValue); } catch (Exception e) { From 5175bb0a7f288f389f16e560f16dd05b50e15ca4 Mon Sep 17 00:00:00 2001 From: caochaojie <258920553@qq.com> Date: Thu, 22 Jan 2026 16:38:42 +0800 Subject: [PATCH 4/8] =?UTF-8?q?fix(config):=20=E4=BD=BF=E7=94=A8=20InputSt?= =?UTF-8?q?reamReader=20=E6=9B=BF=E4=BB=A3=20InputStream=20=E4=BB=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=20UTF-8=20=E7=BC=96=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../game/widget/light/profile/Profile.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java index cf7d36641..933a29765 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java @@ -22,8 +22,9 @@ import lombok.extern.slf4j.Slf4j; import java.io.IOException; -import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.Objects; @@ -42,7 +43,6 @@ @Slf4j @ToString public class Profile { - /** key */ String key; Map map = new ConcurrentHashMap<>(); @@ -152,18 +152,17 @@ public void load(List urls) { urls.forEach(this::load); } - /** - * 需要加载的配置文件 - * - */ public void load(URL url) { - // 需要加载的配置文件 - try (InputStream inputStream = url.openStream()) { + if (url == null) { + log.warn("加载配置失败:URL 为空"); + return; + } + try (InputStreamReader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { Properties properties = new Properties(); - properties.load(inputStream); + properties.load(reader); this.load(properties); } catch (IOException e) { - log.error(e.getMessage(), e); + log.error("读取配置文件异常 [{}]: {}", url, e.getMessage(), e); } } } From 9d3f90a304d94df074372fa2a1fa8da5a6130cc1 Mon Sep 17 00:00:00 2001 From: caochaojie <258920553@qq.com> Date: Fri, 8 May 2026 01:23:14 +0800 Subject: [PATCH 5/8] =?UTF-8?q?refactor(widget):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=BB=91=E5=AE=9A=E5=8A=9F=E8=83=BD=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除实验性功能代码 - 新增 AbstractStaticBinder 抽象类统一配置绑定逻辑 - 添加 EnvStaticBinder 支持环境变量和配置文件合并绑定 - 修改 Profile 类中 map 类型从 Object 改为 String - 更新 ProfileManager 中配置绑定调用方式 - 重构 ProfileStaticBinder 继承 AbstractStaticBinder 基类 - 在服务配置文件中新增 HttpExternalJoinSelector --- ...ernal.core.micro.join.ExternalJoinSelector | 3 +- .../client/AbstractBrokerClientStartup.java | 9 --- .../light/profile/AbstractStaticBinder.java | 75 +++++++++++++++++++ .../widget/light/profile/EnvStaticBinder.java | 35 +++++++++ .../game/widget/light/profile/Profile.java | 7 +- .../widget/light/profile/ProfileManager.java | 5 +- .../light/profile/ProfileStaticBinder.java | 70 +++++------------ 7 files changed, 136 insertions(+), 68 deletions(-) create mode 100644 widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/AbstractStaticBinder.java create mode 100644 widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/EnvStaticBinder.java diff --git a/external/external-netty/src/main/resources/META-INF/services/com.iohao.game.external.core.micro.join.ExternalJoinSelector b/external/external-netty/src/main/resources/META-INF/services/com.iohao.game.external.core.micro.join.ExternalJoinSelector index 1b8aead09..5cc25d443 100644 --- a/external/external-netty/src/main/resources/META-INF/services/com.iohao.game.external.core.micro.join.ExternalJoinSelector +++ b/external/external-netty/src/main/resources/META-INF/services/com.iohao.game.external.core.micro.join.ExternalJoinSelector @@ -1,2 +1,3 @@ com.iohao.game.external.core.netty.micro.join.TcpExternalJoinSelector -com.iohao.game.external.core.netty.micro.join.WebSocketExternalJoinSelector \ No newline at end of file +com.iohao.game.external.core.netty.micro.join.WebSocketExternalJoinSelector +com.iohao.game.external.core.netty.micro.join.HttpExternalJoinSelector diff --git a/net-bolt/bolt-client/src/main/java/com/iohao/game/bolt/broker/client/AbstractBrokerClientStartup.java b/net-bolt/bolt-client/src/main/java/com/iohao/game/bolt/broker/client/AbstractBrokerClientStartup.java index 049ee01d5..8678f1c77 100644 --- a/net-bolt/bolt-client/src/main/java/com/iohao/game/bolt/broker/client/AbstractBrokerClientStartup.java +++ b/net-bolt/bolt-client/src/main/java/com/iohao/game/bolt/broker/client/AbstractBrokerClientStartup.java @@ -127,18 +127,9 @@ BrokerClientBuilder initConfig() { // 注册用户处理器 this.registerUserProcessor(this.brokerClientBuilder); - // 实验性功能 - experiment(); - return this.brokerClientBuilder; } - /** - * 实验性功能,将来可能移除的。 - */ - private void experiment() { - // ExtRegions.me().add(new MonitorExtRegion()); - } @Override public void setWithNo(int withNo) { diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/AbstractStaticBinder.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/AbstractStaticBinder.java new file mode 100644 index 000000000..5b39bbfc7 --- /dev/null +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/AbstractStaticBinder.java @@ -0,0 +1,75 @@ +package com.iohao.game.widget.light.profile; + +import org.springframework.core.convert.support.DefaultConversionService; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Map; + +public abstract class AbstractStaticBinder { + + // Spring 标准类型转换器 + protected static final DefaultConversionService CONVERTER = new DefaultConversionService(); + + /** + * 绑定数据到目标类的静态字段 + * + * @param targetClass 目标类 + */ + public void bind(Class targetClass) { + if (!isValidDataSource()) { + return; + } + + Field[] fields = targetClass.getDeclaredFields(); + + Map dataMap = getDataMap(); + dataMap.forEach((key, value) -> { + if (key == null || value == null) { + return; + } + + String normalizedKey = normalize(key.toString()); + + for (Field field : fields) { + int mod = field.getModifiers(); + + if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) { + String fieldName = field.getName(); + + if (fieldName.equalsIgnoreCase(key.toString()) || normalize(fieldName).equals(normalizedKey)) { + try { + Object convertedValue = CONVERTER.convert(value, field.getType()); + field.set(null, convertedValue); + } catch (Exception e) { + System.err.printf("[%s] 字段 [%s] 赋值失败: %s%n", getSourceName(), fieldName, e.getMessage()); + } + break; + } + } + } + }); + } + + /** + * 数据源是否有效 + */ + protected abstract boolean isValidDataSource(); + + /** + * 获取数据映射(键值对) + */ + protected abstract Map getDataMap(); + + /** + * 获取数据源名称(用于日志输出) + */ + protected abstract String getSourceName(); + + /** + * 归一化处理:移除所有下划线并转为小写 + */ + private String normalize(String name) { + return name.replace("_", "").toLowerCase(); + } +} diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/EnvStaticBinder.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/EnvStaticBinder.java new file mode 100644 index 000000000..5de8cef8f --- /dev/null +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/EnvStaticBinder.java @@ -0,0 +1,35 @@ +package com.iohao.game.widget.light.profile; + +import java.util.HashMap; +import java.util.Map; + +public class EnvStaticBinder extends AbstractStaticBinder { + + private final Map configFileData; + + // 构造函数:接受配置文件数据 + public EnvStaticBinder(Map configFileData) { + this.configFileData = configFileData != null ? configFileData : new HashMap<>(); + } + + @Override + protected boolean isValidDataSource() { + // 检查配置文件或环境变量是否有数据 + return !configFileData.isEmpty() || !System.getenv().isEmpty(); + } + + @Override + protected Map getDataMap() { + // 合并配置文件和环境变量数据,配置文件优先 + Map mergedData = new HashMap<>(configFileData); + System.getenv().forEach((key, value) -> { + mergedData.putIfAbsent(key, value); // 环境变量作为备选 + }); + return mergedData; + } + + @Override + protected String getSourceName() { + return "Env"; // 日志标识仍为 Env,但内部已融合配置文件数据 + } +} diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java index 933a29765..6393fecb2 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java @@ -45,7 +45,7 @@ public class Profile { String key; - Map map = new ConcurrentHashMap<>(); + Map map = new ConcurrentHashMap<>(); Profile() { } @@ -134,10 +134,7 @@ public int getInt(String key, int defVal) { public void load(Properties properties) { for (Object o : properties.keySet()) { String key = o.toString(); - - Object value = properties.get(o); - // 理论上在这里做数据类型解析会好一些,但现在不着急 - + String value = properties.getProperty(key); this.map.put(key, value); } } diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java index f3c8b7515..5270f71cb 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java @@ -21,7 +21,6 @@ import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; -import org.springframework.core.convert.support.DefaultConversionService; import java.net.URL; import java.util.*; @@ -143,8 +142,8 @@ public void loadEnvFileFillStaticClass(String env, String fileName, Class sta log.warn("加载配置文件失败,跳过加载"); return; } - ProfileStaticBinder.bind(staticClass, profile.map); + ProfileStaticBinder profileStaticBinder = new ProfileStaticBinder(profile.map); + profileStaticBinder.bind(staticClass); } - } diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java index 8469af1c1..eca5bcb80 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java @@ -1,62 +1,32 @@ package com.iohao.game.widget.light.profile; -import org.springframework.core.convert.support.DefaultConversionService; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.Map; /** * @Author: caochaojie * @Date: 2026-01-2215:29 */ -public class ProfileStaticBinder { - - // Spring 标准类型转换器:支持 String -> int/long/boolean/Enum/Array 等 - private static final DefaultConversionService CONVERTER = new DefaultConversionService(); - - public static void bind(Class targetClass, Map configMap) { - if (configMap == null || configMap.isEmpty()) { - return; - } - - // 获取类中定义的全部字段 - Field[] fields = targetClass.getDeclaredFields(); - - configMap.forEach((key, value) -> { - if (key == null || value == null) return; - - String configKey = key.toString(); - // 归一化处理:去掉下划线并转为小写 - String normalizedConfigKey = normalize(configKey); - - for (Field field : fields) { - int mod = field.getModifiers(); - - // 仅处理 public static 字段 - if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) { - String fieldName = field.getName(); - - // 匹配规则:原始名一致 OR 归一化后一致 - if (fieldName.equalsIgnoreCase(configKey) || normalize(fieldName).equals(normalizedConfigKey)) { - try { - Object convertedValue = CONVERTER.convert(value, field.getType()); - field.set(null, convertedValue); - } catch (Exception e) { - System.err.printf("[Config] 字段 [%s] 赋值失败: %s%n", fieldName, e.getMessage()); - } - break; // 匹配到一个字段后跳出内层循环 - } - } - } - }); +public class ProfileStaticBinder extends AbstractStaticBinder { + + private final Map configMap; + + public ProfileStaticBinder(Map configMap) { + this.configMap = configMap; + } + + @Override + protected boolean isValidDataSource() { + return configMap != null && !configMap.isEmpty(); + } + + @Override + protected Map getDataMap() { + return configMap; } - /** - * 归一化处理:移除所有下划线并转为小写 - * 例如: SERVER_ID -> serverid, serverId -> serverid, server_id -> serverid - */ - private static String normalize(String name) { - return name.replace("_", "").toLowerCase(); + @Override + protected String getSourceName() { + return "Config"; } + } \ No newline at end of file From 40acd8d64cf1c738f660f84f4d7cd7074db4d916 Mon Sep 17 00:00:00 2001 From: caochaojie <258920553@qq.com> Date: Sat, 9 May 2026 02:24:20 +0800 Subject: [PATCH 6/8] =?UTF-8?q?feat(profile):=20=E6=B7=BB=E5=8A=A0=20Lombo?= =?UTF-8?q?k=20Getter=20=E6=B3=A8=E8=A7=A3=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入 Lombok Getter 注解以自动生成 getter 方法 - 为 Profile 类的 map 字段添加 @Getter 注解 - 简化代码结构并提高可读性 --- .../java/com/iohao/game/widget/light/profile/Profile.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java index 6393fecb2..9f238d878 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java @@ -18,6 +18,7 @@ */ package com.iohao.game.widget.light.profile; +import lombok.Getter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; @@ -44,7 +45,7 @@ @ToString public class Profile { String key; - + @Getter Map map = new ConcurrentHashMap<>(); Profile() { @@ -162,4 +163,5 @@ public void load(URL url) { log.error("读取配置文件异常 [{}]: {}", url, e.getMessage(), e); } } + } From a50018384b658e6232d97992126211c0eaae5f86 Mon Sep 17 00:00:00 2001 From: caochaojie <258920553@qq.com> Date: Mon, 11 May 2026 01:36:59 +0800 Subject: [PATCH 7/8] =?UTF-8?q?feat(light-profile):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=BB=91=E5=AE=9A=E6=8A=BD=E8=B1=A1=E7=B1=BB?= =?UTF-8?q?=E5=92=8C=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 AbstractStaticBinder 抽象类,提供通用的数据绑定逻辑 - 添加 EnvStaticBinder 类,支持从环境变量和配置文件绑定数据 - 添加 Profile 类,用于配置文件管理域操作 - 修改 ProfileManager 的 loadEnvFileFillStaticClass 方法,支持多环境配置加载 - 更新 ProfileStaticBinder 以支持 Object 类型配置值 - 优化配置加载日志输出和错误处理机制 --- .../light/profile/AbstractStaticBinder.java | 4 +- .../widget/light/profile/EnvStaticBinder.java | 4 +- .../game/widget/light/profile/Profile.java | 2 +- .../widget/light/profile/ProfileManager.java | 41 +++++++++++++++++-- .../light/profile/ProfileStaticBinder.java | 6 +-- 5 files changed, 46 insertions(+), 11 deletions(-) diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/AbstractStaticBinder.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/AbstractStaticBinder.java index 5b39bbfc7..7b8f86a25 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/AbstractStaticBinder.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/AbstractStaticBinder.java @@ -23,7 +23,7 @@ public void bind(Class targetClass) { Field[] fields = targetClass.getDeclaredFields(); - Map dataMap = getDataMap(); + Map dataMap = getDataMap(); dataMap.forEach((key, value) -> { if (key == null || value == null) { return; @@ -59,7 +59,7 @@ public void bind(Class targetClass) { /** * 获取数据映射(键值对) */ - protected abstract Map getDataMap(); + protected abstract Map getDataMap(); /** * 获取数据源名称(用于日志输出) diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/EnvStaticBinder.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/EnvStaticBinder.java index 5de8cef8f..411759ba6 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/EnvStaticBinder.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/EnvStaticBinder.java @@ -19,9 +19,9 @@ protected boolean isValidDataSource() { } @Override - protected Map getDataMap() { + protected Map getDataMap() { // 合并配置文件和环境变量数据,配置文件优先 - Map mergedData = new HashMap<>(configFileData); + Map mergedData = new HashMap<>(configFileData); System.getenv().forEach((key, value) -> { mergedData.putIfAbsent(key, value); // 环境变量作为备选 }); diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java index 9f238d878..12f13471e 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/Profile.java @@ -46,7 +46,7 @@ public class Profile { String key; @Getter - Map map = new ConcurrentHashMap<>(); + Map map = new ConcurrentHashMap<>(); Profile() { } diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java index 5270f71cb..f90f911b0 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java @@ -132,18 +132,53 @@ public Profile loadEnvFile(String env, String fileName) { return profile; } + /** + * 加载特定环境的配置文件并填充到静态类 + *
+     * env 参数支持逗号分隔的多个环境名称,例如: "blocks,local"
+     * 会按顺序加载每个环境的配置文件,后面的环境配置会覆盖前面的同名配置项
+     * 所有环境配置加载完成后,再统一进行静态类的映射绑定
+     * 
+ * + * @param env 环境名称,支持逗号分隔的多个环境,例如: "blocks,local" + * @param fileName 配置文件名 (不需要带 .props 后缀) + * @param staticClass 需要绑定的静态类 + */ public void loadEnvFileFillStaticClass(String env, String fileName, Class staticClass) { if (StringUtils.isEmpty(env) || StringUtils.isEmpty(fileName)) { log.warn("环境或文件名为空,跳过加载"); return; } - Profile profile = loadEnvFile(env, fileName); - if (Objects.isNull(profile)) { + + final String separator = ","; + List envList = Arrays.stream(env.split(separator)) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + + log.debug("加载的环境列表 - size {} - {}", envList.size(), envList); + + Profile mergedProfile = profile(fileName); + mergedProfile.map.clear(); + + for (String singleEnv : envList) { + URL fileURL = ResourcePatternResolverProfile.getFileURL(singleEnv, fileName); + if (fileURL != null) { + mergedProfile.load(fileURL); + log.debug("加载环境 [{}] 的配置 - size:{}", singleEnv, mergedProfile.map.size()); + } else { + log.warn("环境 [{}] 下未找到配置文件 [{}]", singleEnv, fileName); + } + } + + if (mergedProfile.map.isEmpty()) { log.warn("加载配置文件失败,跳过加载"); return; } - ProfileStaticBinder profileStaticBinder = new ProfileStaticBinder(profile.map); + + ProfileStaticBinder profileStaticBinder = new ProfileStaticBinder(mergedProfile.map); profileStaticBinder.bind(staticClass); + log.debug("配置内容已绑定到静态类 - size:{} - {}", mergedProfile.map.size(), mergedProfile.map); } } diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java index eca5bcb80..720b24966 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java @@ -8,9 +8,9 @@ */ public class ProfileStaticBinder extends AbstractStaticBinder { - private final Map configMap; + private final Map configMap; - public ProfileStaticBinder(Map configMap) { + public ProfileStaticBinder(Map configMap) { this.configMap = configMap; } @@ -20,7 +20,7 @@ protected boolean isValidDataSource() { } @Override - protected Map getDataMap() { + protected Map getDataMap() { return configMap; } From 661f9f707489ab356d5f830f2a34e4e068465d21 Mon Sep 17 00:00:00 2001 From: caochaojie <258920553@qq.com> Date: Mon, 11 May 2026 03:15:44 +0800 Subject: [PATCH 8/8] =?UTF-8?q?feat(widget):=20=E6=B7=BB=E5=8A=A0=E8=BD=BB?= =?UTF-8?q?=E9=87=8F=E7=BA=A7=E9=85=8D=E7=BD=AE=E7=AE=A1=E7=90=86=E5=99=A8?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现 ProfileManager 配置管理类,支持多环境配置加载 - 添加主配置加载功能,支持逗号分隔的目录列表 - 实现环境变量覆盖机制,系统环境变量可覆盖配置项 - 提供配置文件解析和合并功能,支持多环境配置覆盖 - 集成静态类绑定功能,自动将配置映射到静态字段 - 添加资源路径解析器,支持从 resources/conf 目录加载配置 - 实现无锁化并发安全的配置缓存机制 --- .../widget/light/profile/ProfileManager.java | 76 ++++++++++++------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java index f90f911b0..a92f66eb9 100644 --- a/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileManager.java @@ -115,46 +115,45 @@ public void loadMainProfile(String profileConfigName) { } /** - * 加载特定环境的配置文件 + * 解析环境列表字符串为环境名称列表 + *
+     * 支持逗号分隔的多个环境名称,例如: "blocks,local"
+     * 会自动去除空格和空字符串
+     * 
* - * @param env 环境名称 - * @param fileName 配置文件名 (不需要带 .props 后缀) + * @param env 环境名称字符串,支持逗号分隔 + * @return 环境名称列表 */ - public Profile loadEnvFile(String env, String fileName) { - if (StringUtils.isEmpty(env) || StringUtils.isEmpty(fileName)) { - log.warn("环境或文件名为空,跳过加载"); - return null; - } - URL fileURL = ResourcePatternResolverProfile.getFileURL(env, fileName); - Profile profile = profile(fileName); - profile.load(fileURL); - log.debug("配置内容 - size:{} - {}", profile.map.size(), profile.map); - return profile; + private List parseEnvList(String env) { + final String separator = ","; + return Arrays.stream(env.split(separator)) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); } /** - * 加载特定环境的配置文件并填充到静态类 + * 按顺序加载多个环境的配置文件并合并 *
      * env 参数支持逗号分隔的多个环境名称,例如: "blocks,local"
      * 会按顺序加载每个环境的配置文件,后面的环境配置会覆盖前面的同名配置项
-     * 所有环境配置加载完成后,再统一进行静态类的映射绑定
      * 
* - * @param env 环境名称,支持逗号分隔的多个环境,例如: "blocks,local" - * @param fileName 配置文件名 (不需要带 .props 后缀) - * @param staticClass 需要绑定的静态类 + * @param env 环境名称,支持逗号分隔的多个环境,例如: "blocks,local" + * @param fileName 配置文件名 (不需要带 .props 后缀) + * @return 合并后的 Profile 对象,如果加载失败返回 null */ - public void loadEnvFileFillStaticClass(String env, String fileName, Class staticClass) { + public Profile loadEnvFile(String env, String fileName) { if (StringUtils.isEmpty(env) || StringUtils.isEmpty(fileName)) { log.warn("环境或文件名为空,跳过加载"); - return; + return null; } - final String separator = ","; - List envList = Arrays.stream(env.split(separator)) - .map(String::trim) - .filter(s -> !s.isEmpty()) - .collect(Collectors.toList()); + List envList = parseEnvList(env); + if (envList.isEmpty()) { + log.warn("环境列表为空,跳过加载"); + return null; + } log.debug("加载的环境列表 - size {} - {}", envList.size(), envList); @@ -173,12 +172,35 @@ public void loadEnvFileFillStaticClass(String env, String fileName, Class sta if (mergedProfile.map.isEmpty()) { log.warn("加载配置文件失败,跳过加载"); + return null; + } + + log.debug("配置内容 - size:{} - {}", mergedProfile.map.size(), mergedProfile.map); + return mergedProfile; + } + + /** + * 加载特定环境的配置文件并填充到静态类 + *
+     * env 参数支持逗号分隔的多个环境名称,例如: "blocks,local"
+     * 会按顺序加载每个环境的配置文件,后面的环境配置会覆盖前面的同名配置项
+     * 所有环境配置加载完成后,再统一进行静态类的映射绑定
+     * 
+ * + * @param env 环境名称,支持逗号分隔的多个环境,例如: "blocks,local" + * @param fileName 配置文件名 (不需要带 .props 后缀) + * @param staticClass 需要绑定的静态类 + */ + public void loadEnvFileFillStaticClass(String env, String fileName, Class staticClass) { + Profile profile = loadEnvFile(env, fileName); + if (Objects.isNull(profile) || profile.map.isEmpty()) { + log.warn("加载配置文件失败,跳过绑定静态类"); return; } - ProfileStaticBinder profileStaticBinder = new ProfileStaticBinder(mergedProfile.map); + ProfileStaticBinder profileStaticBinder = new ProfileStaticBinder(profile.map); profileStaticBinder.bind(staticClass); - log.debug("配置内容已绑定到静态类 - size:{} - {}", mergedProfile.map.size(), mergedProfile.map); + log.debug("配置内容已绑定到静态类 - size:{} - {}", profile.map.size(), profile.map); } }