# 第11天:实现配置属性类和工具类 ## 📚 今日目标 1. 实现DBRouterConfig配置属性类 2. 实现StringUtils工具类 3. 完善PropertyUtil工具类 4. 理解@ConfigurationProperties --- ## 🎯 知识点1:@ConfigurationProperties ### 作用 **将配置文件中的属性绑定到Java对象** ```yaml # application.yml db-router: db-count: 2 tb-count: 4 router-key: userId ``` ```java @ConfigurationProperties(prefix = "db-router") public class DBRouterConfig { private int dbCount; private int tbCount; private String routerKey; } ``` **自动绑定**:Spring Boot会自动将配置绑定到对象 --- ## 🛠️ 实践任务1:实现DBRouterConfig ### 步骤1:创建DBRouterConfig类 在 `src/main/java/cn/bugstack/middleware/db/router/` 目录下创建 `DBRouterConfig.java`: ```java package cn.bugstack.middleware.db.router; import org.springframework.boot.context.properties.ConfigurationProperties; /** * 数据库路由配置 * * @author 小傅哥 */ @ConfigurationProperties(prefix = "router.jdbc.datasource") public class DBRouterConfig { /** 分库数量 */ private int dbCount; /** 分表数量 */ private int tbCount; /** 路由键 */ private String routerKey; public DBRouterConfig() { } public DBRouterConfig(int dbCount, int tbCount, String routerKey) { this.dbCount = dbCount; this.tbCount = tbCount; this.routerKey = routerKey; } public int getDbCount() { return dbCount; } public void setDbCount(int dbCount) { this.dbCount = dbCount; } public int getTbCount() { return tbCount; } public void setTbCount(int tbCount) { this.tbCount = tbCount; } public String getRouterKey() { return routerKey; } public void setRouterKey(String routerKey) { this.routerKey = routerKey; } } ``` ### 配置示例 在 `application.yml` 中配置: ```yaml router: jdbc: datasource: dbCount: 2 # 2个数据库 tbCount: 4 # 每个库4张表 routerKey: userId # 路由键 ``` --- ## 🛠️ 实践任务2:实现StringUtils工具类 ### 步骤1:创建StringUtils类 在 `src/main/java/cn/bugstack/middleware/db/router/util/` 目录下创建 `StringUtils.java`: ```java package cn.bugstack.middleware.db.router.util; import org.apache.commons.lang.StringUtils as ApacheStringUtils; /** * 字符串工具类 * * @author 小傅哥 */ public class StringUtils { /** * 判断字符串是否为空 * * @param str 字符串 * @return true:为空,false:不为空 */ public static boolean isBlank(String str) { return ApacheStringUtils.isBlank(str); } /** * 判断字符串是否不为空 * * @param str 字符串 * @return true:不为空,false:为空 */ public static boolean isNotBlank(String str) { return ApacheStringUtils.isNotBlank(str); } /** * 中划线转驼峰 * * @param str 中划线字符串,如:user-name * @return 驼峰字符串,如:userName */ public static String middleScoreToCamelCase(String str) { if (isBlank(str)) { return str; } StringBuilder sb = new StringBuilder(); boolean nextUpperCase = false; for (char c : str.toCharArray()) { if (c == '-') { nextUpperCase = true; } else { if (nextUpperCase) { sb.append(Character.toUpperCase(c)); nextUpperCase = false; } else { sb.append(c); } } } return sb.toString(); } } ``` ### 代码解释 1. **isBlank/isNotBlank**: - 使用Apache Commons Lang的工具方法 - 判断字符串是否为空(包括null、空字符串、只有空格) 2. **middleScoreToCamelCase**: - 将中划线命名转为驼峰命名 - 如:`user-name` → `userName` --- ## 🛠️ 实践任务3:完善PropertyUtil工具类 ### 步骤1:完善PropertyUtil类 在 `src/main/java/cn/bugstack/middleware/db/router/util/` 目录下完善 `PropertyUtil.java`: ```java package cn.bugstack.middleware.db.router.util; import org.apache.commons.beanutils.PropertyUtils; import org.springframework.core.env.Environment; /** * 属性工具类 * * @author 小傅哥 */ public class PropertyUtil { private static int springBootVersion = 1; static { try { // 检测Spring Boot版本 Class.forName("org.springframework.boot.bind.RelaxedPropertyResolver"); } catch (ClassNotFoundException e) { springBootVersion = 2; } } /** * 处理Spring环境属性 */ public static T handle(Environment environment, String prefix, Class targetClass) { try { if (springBootVersion == 1) { return (T) v1(environment, prefix); } else { return (T) v2(environment, prefix, targetClass); } } catch (Exception e) { throw new RuntimeException("获取属性失败: " + prefix, e); } } /** * Spring Boot 1.x 版本 */ private static Object v1(Environment environment, String prefix) { throw new UnsupportedOperationException("Spring Boot 1.x 暂不支持"); } /** * Spring Boot 2.x 版本 */ private static Object v2(Environment environment, String prefix, Class targetClass) { try { return org.springframework.boot.context.properties.bind.Binder .get(environment) .bind(prefix, targetClass) .orElse(null); } catch (Exception e) { throw new RuntimeException("绑定属性失败: " + prefix, e); } } /** * 获取对象属性值 */ public static Object getProperty(Object obj, String propertyName) { try { return PropertyUtils.getProperty(obj, propertyName); } catch (Exception e) { throw new RuntimeException("获取属性值失败: " + propertyName, e); } } /** * 设置对象属性值 */ public static void setProperty(Object obj, String propertyName, Object value) { try { PropertyUtils.setProperty(obj, propertyName, value); } catch (Exception e) { throw new RuntimeException("设置属性值失败: " + propertyName, e); } } } ``` --- ## 🎓 知识点拓展 ### 拓展1:@ConfigurationProperties vs @Value **@ConfigurationProperties**: - ✅ 类型安全 - ✅ 支持复杂对象 - ✅ IDE提示 **@Value**: - ✅ 简单直接 - ❌ 类型不安全 - ❌ 不支持复杂对象 **选择建议**: - 简单配置:@Value - 复杂配置:@ConfigurationProperties ### 拓展2:配置属性验证 ```java @ConfigurationProperties(prefix = "db-router") @Validated public class DBRouterConfig { @Min(1) @Max(100) private int dbCount; @NotBlank private String routerKey; } ``` ### 拓展3:多环境配置 ```yaml # application.yml db-router: db-count: 2 # application-dev.yml db-router: db-count: 2 # application-prod.yml db-router: db-count: 4 ``` --- ## ✅ 今日检查清单 - [ ] 实现了DBRouterConfig配置属性类 - [ ] 实现了StringUtils工具类 - [ ] 完善了PropertyUtil工具类 - [ ] 理解了@ConfigurationProperties - [ ] 完成了拓展阅读 --- ## 🎯 明日预告 明天我们将实现: - 哈希路由策略DBRouterStrategyHashCode - 理解哈希算法的实现 --- ## 💡 思考题 1. @ConfigurationProperties和@Value的区别? 2. 如何验证配置属性的有效性? 3. 如何支持多环境配置? --- ## 📚 参考资源 - [@ConfigurationProperties文档](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.typesafe-configuration-properties) - [Apache Commons Lang](https://commons.apache.org/proper/commons-lang/) - [Apache Commons BeanUtils](https://commons.apache.org/proper/commons-beanutils/)