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..7b8f86a25 --- /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..411759ba6 --- /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 d8ab178de..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 @@ -18,12 +18,14 @@ */ package com.iohao.game.widget.light.profile; +import lombok.Getter; import lombok.ToString; 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,9 +44,8 @@ @Slf4j @ToString public class Profile { - /** key */ String key; - + @Getter Map map = new ConcurrentHashMap<>(); Profile() { @@ -134,10 +135,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); } } @@ -149,18 +147,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); + urls.forEach(this::load); + } - } catch (IOException e) { - log.error(e.getMessage(), e); - } - }); + public void load(URL url) { + if (url == null) { + log.warn("加载配置失败:URL 为空"); + return; + } + try (InputStreamReader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { + Properties properties = new Properties(); + properties.load(reader); + this.load(properties); + } catch (IOException e) { + log.error("读取配置文件异常 [{}]: {}", url, 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..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 @@ -48,7 +48,6 @@ public final class ProfileManager { */ private final Map profileMap = new ConcurrentHashMap<>(); - public Profile profile() { return profile(MAIN_CONFIG); } @@ -115,5 +114,93 @@ public void loadMainProfile(String profileConfigName) { } + /** + * 解析环境列表字符串为环境名称列表 + *
+     * 支持逗号分隔的多个环境名称,例如: "blocks,local"
+     * 会自动去除空格和空字符串
+     * 
+ * + * @param env 环境名称字符串,支持逗号分隔 + * @return 环境名称列表 + */ + 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 后缀) + * @return 合并后的 Profile 对象,如果加载失败返回 null + */ + public Profile loadEnvFile(String env, String fileName) { + if (StringUtils.isEmpty(env) || StringUtils.isEmpty(fileName)) { + log.warn("环境或文件名为空,跳过加载"); + return null; + } + + List envList = parseEnvList(env); + if (envList.isEmpty()) { + log.warn("环境列表为空,跳过加载"); + return null; + } + + 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 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(profile.map); + profileStaticBinder.bind(staticClass); + log.debug("配置内容已绑定到静态类 - size:{} - {}", profile.map.size(), 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..720b24966 --- /dev/null +++ b/widget/light-profile/src/main/java/com/iohao/game/widget/light/profile/ProfileStaticBinder.java @@ -0,0 +1,32 @@ +package com.iohao.game.widget.light.profile; + +import java.util.Map; + +/** + * @Author: caochaojie + * @Date: 2026-01-2215:29 + */ +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; + } + + @Override + protected String getSourceName() { + return "Config"; + } + +} \ 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; +}