From 9ed6412bac23b2c3676f7b97ef7482ec7ca933fc Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 21 Aug 2019 18:04:00 +0800 Subject: [PATCH] Props add toBean method --- CHANGELOG.md | 1 + .../java/cn/hutool/core/util/ReflectUtil.java | 7 +- hutool-setting/pom.xml | 14 ++- .../java/cn/hutool/setting/dialect/Props.java | 107 ++++++++++++++++-- .../cn/hutool/setting/test/PropsTest.java | 66 +++++++++-- .../test/resources/to_bean_test.properties | 20 ++++ 6 files changed, 186 insertions(+), 29 deletions(-) create mode 100644 hutool-setting/src/test/resources/to_bean_test.properties diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a891367d..cc96caed4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * 【extra】 邮件增加图片支持(pr#495@Github) * 【core】 MapUtil、CollUtil增加emptyIfNull(issue#502@Github) * 【core】 增加emptyIfNull等(issue#503@Github) +* 【setting】 Props增加toBean方法(issue#499@Github) ### Bug修复 * 【http】 修复HttpRquest中body方法长度计算问题(issue#I10UPG@Gitee) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java index 5317059b8..9e5f5adcf 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java @@ -236,7 +236,10 @@ public class ReflectUtil { public static void setFieldValue(Object obj, String fieldName, Object value) throws UtilException { Assert.notNull(obj); Assert.notBlank(fieldName); - setFieldValue(obj, getField(obj.getClass(), fieldName), value); + + final Field field = getField(obj.getClass(), fieldName); + Assert.notNull(field, "Field [{}] is not exist in [{}]", fieldName, obj.getClass().getName()); + setFieldValue(obj, field, value); } /** @@ -249,7 +252,7 @@ public class ReflectUtil { */ public static void setFieldValue(Object obj, Field field, Object value) throws UtilException { Assert.notNull(obj); - Assert.notNull(field); + Assert.notNull(field, "Field in [{}] not exist !", obj.getClass().getName()); field.setAccessible(true); if(null != value) { diff --git a/hutool-setting/pom.xml b/hutool-setting/pom.xml index 007f4047b..3863357d6 100644 --- a/hutool-setting/pom.xml +++ b/hutool-setting/pom.xml @@ -1,9 +1,11 @@ - + 4.0.0 jar - + cn.hutool hutool-parent @@ -13,7 +15,7 @@ hutool-setting ${project.artifactId} Hutool 配置文件增强 - + cn.hutool @@ -25,5 +27,11 @@ hutool-log ${project.parent.version} + + org.projectlombok + lombok + 1.18.6 + test + diff --git a/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java b/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java index d2705dc38..d1a38f50b 100644 --- a/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java +++ b/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java @@ -14,6 +14,7 @@ import java.nio.file.WatchEvent; import java.util.Date; import java.util.Properties; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.getter.BasicTypeGetter; @@ -31,9 +32,11 @@ import cn.hutool.core.io.watch.WatchMonitor; import cn.hutool.core.io.watch.WatchUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.log.Log; import cn.hutool.log.LogFactory; +import cn.hutool.log.StaticLog; import cn.hutool.setting.SettingRuntimeException; /** @@ -57,9 +60,9 @@ public final class Props extends Properties implements BasicTypeGetter, * 获得Classpath下的Properties文件 * * @param resource 资源(相对Classpath的路径) - * @return Properties + * @return Props */ - public static Properties getProp(String resource) { + public static Props getProp(String resource) { return new Props(resource); } @@ -70,7 +73,7 @@ public final class Props extends Properties implements BasicTypeGetter, * @param charsetName 字符集 * @return Properties */ - public static Properties getProp(String resource, String charsetName) { + public static Props getProp(String resource, String charsetName) { return new Props(resource, charsetName); } @@ -81,7 +84,7 @@ public final class Props extends Properties implements BasicTypeGetter, * @param charset 字符集 * @return Properties */ - public static Properties getProp(String resource, Charset charset) { + public static Props getProp(String resource, Charset charset) { return new Props(resource, charset); } @@ -120,7 +123,7 @@ public final class Props extends Properties implements BasicTypeGetter, */ public Props(String path, Charset charset) { Assert.notBlank(path, "Blank properties file path !"); - if(null != charset) { + if (null != charset) { this.charset = charset; } this.load(ResourceUtil.getResourceObj(path)); @@ -187,7 +190,7 @@ public final class Props extends Properties implements BasicTypeGetter, */ public Props(String path, Class clazz, Charset charset) { Assert.notBlank(path, "Blank properties file path !"); - if(null != charset) { + if (null != charset) { this.charset = charset; } this.load(new ClassPathResource(path, clazz)); @@ -220,7 +223,7 @@ public final class Props extends Properties implements BasicTypeGetter, */ public Props(URL propertiesUrl, Charset charset) { Assert.notNull(propertiesUrl, "Null properties URL !"); - if(null != charset) { + if (null != charset) { this.charset = charset; } this.load(new UrlResource(propertiesUrl)); @@ -276,7 +279,7 @@ public final class Props extends Properties implements BasicTypeGetter, // 先关闭之前的监听 this.watchMonitor.close(); } - this.watchMonitor = WatchUtil.createModify(this.propertiesFileUrl, new SimpleWatcher(){ + this.watchMonitor = WatchUtil.createModify(this.propertiesFileUrl, new SimpleWatcher() { @Override public void onModify(WatchEvent event, Path currentPath) { load(); @@ -441,17 +444,17 @@ public final class Props extends Properties implements BasicTypeGetter, public > E getEnum(Class clazz, String key) { return getEnum(clazz, key, null); } - + @Override public Date getDate(String key, Date defaultValue) { return Convert.toDate(getStr(key), defaultValue); } - + @Override public Date getDate(String key) { return getDate(key, null); } - + /** * 获取并删除键值对,当指定键对应值非空时,返回并删除这个值,后边的键对应的值不再查找 * @@ -469,6 +472,86 @@ public final class Props extends Properties implements BasicTypeGetter, } return (String) value; } + + /** + * 将配置文件转换为Bean,支持嵌套Bean
+ * 支持的表达式: + * + *
+	 * persion
+	 * persion.name
+	 * persons[3]
+	 * person.friends[5].name
+	 * ['person']['friends'][5]['name']
+	 * 
+ * + * @param beanClass Bean类 + * @return Bean对象 + * @since 4.6.3 + */ + public T toBean(Class beanClass) { + return toBean(beanClass, null); + } + + /** + * 将配置文件转换为Bean,支持嵌套Bean
+ * 支持的表达式: + * + *
+	 * persion
+	 * persion.name
+	 * persons[3]
+	 * person.friends[5].name
+	 * ['person']['friends'][5]['name']
+	 * 
+ * + * @param beanClass Bean类 + * @param prefix 公共前缀,不指定前缀传null,当指定前缀后非此前缀的属性被忽略 + * @return Bean对象 + * @since 4.6.3 + */ + public T toBean(Class beanClass, String prefix) { + final T bean = ReflectUtil.newInstanceIfPossible(beanClass); + return fillBean(bean, prefix); + } + + /** + * 将配置文件转换为Bean,支持嵌套Bean
+ * 支持的表达式: + * + *
+	 * persion
+	 * persion.name
+	 * persons[3]
+	 * person.friends[5].name
+	 * ['person']['friends'][5]['name']
+	 * 
+ * + * @param bean Bean对象 + * @param prefix 公共前缀,不指定前缀传null,当指定前缀后非此前缀的属性被忽略 + * @return Bean对象 + * @since 4.6.3 + */ + public T fillBean(T bean, String prefix) { + prefix = StrUtil.addSuffixIfNot(prefix, StrUtil.DOT); + + String key; + for (java.util.Map.Entry entry : this.entrySet()) { + key = (String) entry.getKey(); + if(false == StrUtil.startWith(key, prefix)) { + // 非指定开头的属性忽略掉 + continue; + } + try { + BeanUtil.setProperty(bean, StrUtil.subSuf(key, prefix.length()), entry.getValue()); + } catch (Exception e) { + // 忽略注入失败的字段(这些字段可能用于其它配置) + StaticLog.debug("Ignore property: [{}]", key); + } + } + + return bean; + } // ----------------------------------------------------------------------- Get end @@ -489,7 +572,7 @@ public final class Props extends Properties implements BasicTypeGetter, * @param absolutePath 设置文件的绝对路径 * @throws IORuntimeException IO异常,可能为文件未找到 */ - public void store(String absolutePath) throws IORuntimeException{ + public void store(String absolutePath) throws IORuntimeException { Writer writer = null; try { writer = FileUtil.getWriter(absolutePath, charset, false); diff --git a/hutool-setting/src/test/java/cn/hutool/setting/test/PropsTest.java b/hutool-setting/src/test/java/cn/hutool/setting/test/PropsTest.java index 71144461a..3ce252183 100644 --- a/hutool-setting/src/test/java/cn/hutool/setting/test/PropsTest.java +++ b/hutool-setting/src/test/java/cn/hutool/setting/test/PropsTest.java @@ -1,5 +1,8 @@ package cn.hutool.setting.test; +import java.util.List; +import java.util.Map; + import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; @@ -8,37 +11,76 @@ import org.junit.Test; import cn.hutool.log.LogFactory; import cn.hutool.log.dialect.console.ConsoleLogFactory; import cn.hutool.setting.dialect.Props; +import lombok.Data; /** * Setting单元测试 + * * @author Looly * */ public class PropsTest { - + @Before - public void init(){ + public void init() { LogFactory.setCurrentLogFactory(ConsoleLogFactory.class); } - + @Test - public void propTest(){ + public void propTest() { Props props = new Props("test.properties"); String user = props.getProperty("user"); Assert.assertEquals(user, "root"); - + + String driver = props.getStr("driver"); + Assert.assertEquals(driver, "com.mysql.jdbc.Driver"); + } + + @Test + @Ignore + public void propTestForAbsPAth() { + Props props = new Props("d:/test.properties"); + String user = props.getProperty("user"); + Assert.assertEquals(user, "root"); + String driver = props.getStr("driver"); Assert.assertEquals(driver, "com.mysql.jdbc.Driver"); } @Test - @Ignore - public void propTestForAbsPAth(){ - Props props = new Props("d:/test.properties"); - String user = props.getProperty("user"); - Assert.assertEquals(user, "root"); + public void toBeanTest() { + Props props = Props.getProp("to_bean_test.properties"); - String driver = props.getStr("driver"); - Assert.assertEquals(driver, "com.mysql.jdbc.Driver"); + ConfigProperties cfg = props.toBean(ConfigProperties.class, "mail"); + Assert.assertEquals("mailer@mail.com", cfg.getHost()); + Assert.assertEquals(9000, cfg.getPort()); + Assert.assertEquals("mailer@mail.com", cfg.getFrom()); + + Assert.assertEquals("john", cfg.getCredentials().getUsername()); + Assert.assertEquals("password", cfg.getCredentials().getPassword()); + Assert.assertEquals("SHA1", cfg.getCredentials().getAuthMethod()); + + Assert.assertEquals("true", cfg.getAdditionalHeaders().get("redelivery")); + Assert.assertEquals("true", cfg.getAdditionalHeaders().get("secure")); + + Assert.assertEquals("admin@mail.com", cfg.getDefaultRecipients().get(0)); + Assert.assertEquals("owner@mail.com", cfg.getDefaultRecipients().get(1)); + } + + @Data + public static class ConfigProperties { + private String host; + private int port; + private String from; + private Credentials credentials; + private List defaultRecipients; + private Map additionalHeaders; + } + + @Data + public static class Credentials { + private String authMethod; + private String username; + private String password; } } diff --git a/hutool-setting/src/test/resources/to_bean_test.properties b/hutool-setting/src/test/resources/to_bean_test.properties new file mode 100644 index 000000000..af19a571d --- /dev/null +++ b/hutool-setting/src/test/resources/to_bean_test.properties @@ -0,0 +1,20 @@ +#Simple properties +mail.host=mailer@mail.com +mail.port=9000 +mail.from=mailer@mail.com + +#List properties +mail.defaultRecipients[0]=admin@mail.com +mail.defaultRecipients[1]=owner@mail.com + +#Map Properties +mail.additionalHeaders.redelivery=true +mail.additionalHeaders.secure=true + +#Object properties +mail.credentials.username=john +mail.credentials.password=password +mail.credentials.authMethod=SHA1 + +# ignore properties +mail.ignore.filed = balabala \ No newline at end of file