浏览代码

Props add toBean method

Looly 6 年之前
父节点
当前提交
9ed6412bac

+ 1 - 0
CHANGELOG.md

@@ -10,6 +10,7 @@
 * 【extra】        邮件增加图片支持(pr#495@Github)
 * 【extra】        邮件增加图片支持(pr#495@Github)
 * 【core】        MapUtil、CollUtil增加emptyIfNull(issue#502@Github)
 * 【core】        MapUtil、CollUtil增加emptyIfNull(issue#502@Github)
 * 【core】        增加emptyIfNull等(issue#503@Github)
 * 【core】        增加emptyIfNull等(issue#503@Github)
+* 【setting】     Props增加toBean方法(issue#499@Github)
 
 
 ### Bug修复
 ### Bug修复
 * 【http】         修复HttpRquest中body方法长度计算问题(issue#I10UPG@Gitee)
 * 【http】         修复HttpRquest中body方法长度计算问题(issue#I10UPG@Gitee)

+ 5 - 2
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 {
 	public static void setFieldValue(Object obj, String fieldName, Object value) throws UtilException {
 		Assert.notNull(obj);
 		Assert.notNull(obj);
 		Assert.notBlank(fieldName);
 		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 {
 	public static void setFieldValue(Object obj, Field field, Object value) throws UtilException {
 		Assert.notNull(obj);
 		Assert.notNull(obj);
-		Assert.notNull(field);
+		Assert.notNull(field, "Field in [{}] not exist !", obj.getClass().getName());
 		field.setAccessible(true);
 		field.setAccessible(true);
 		
 		
 		if(null != value) {
 		if(null != value) {

+ 11 - 3
hutool-setting/pom.xml

@@ -1,9 +1,11 @@
 <?xml version='1.0' encoding='utf-8'?>
 <?xml version='1.0' encoding='utf-8'?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<modelVersion>4.0.0</modelVersion>
 
 
 	<packaging>jar</packaging>
 	<packaging>jar</packaging>
-	
+
 	<parent>
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
 		<artifactId>hutool-parent</artifactId>
@@ -13,7 +15,7 @@
 	<artifactId>hutool-setting</artifactId>
 	<artifactId>hutool-setting</artifactId>
 	<name>${project.artifactId}</name>
 	<name>${project.artifactId}</name>
 	<description>Hutool 配置文件增强</description>
 	<description>Hutool 配置文件增强</description>
-	
+
 	<dependencies>
 	<dependencies>
 		<dependency>
 		<dependency>
 			<groupId>cn.hutool</groupId>
 			<groupId>cn.hutool</groupId>
@@ -25,5 +27,11 @@
 			<artifactId>hutool-log</artifactId>
 			<artifactId>hutool-log</artifactId>
 			<version>${project.parent.version}</version>
 			<version>${project.parent.version}</version>
 		</dependency>
 		</dependency>
+		<dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+			<version>1.18.6</version>
+			<scope>test</scope>
+		</dependency>
 	</dependencies>
 	</dependencies>
 </project>
 </project>

+ 95 - 12
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.Date;
 import java.util.Properties;
 import java.util.Properties;
 
 
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.convert.Convert;
 import cn.hutool.core.convert.Convert;
 import cn.hutool.core.getter.BasicTypeGetter;
 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.io.watch.WatchUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.CharsetUtil;
 import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.ReflectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.log.Log;
 import cn.hutool.log.Log;
 import cn.hutool.log.LogFactory;
 import cn.hutool.log.LogFactory;
+import cn.hutool.log.StaticLog;
 import cn.hutool.setting.SettingRuntimeException;
 import cn.hutool.setting.SettingRuntimeException;
 
 
 /**
 /**
@@ -57,9 +60,9 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
 	 * 获得Classpath下的Properties文件
 	 * 获得Classpath下的Properties文件
 	 * 
 	 * 
 	 * @param resource 资源(相对Classpath的路径)
 	 * @param resource 资源(相对Classpath的路径)
-	 * @return Properties
+	 * @return Props
 	 */
 	 */
-	public static Properties getProp(String resource) {
+	public static Props getProp(String resource) {
 		return new Props(resource);
 		return new Props(resource);
 	}
 	}
 
 
@@ -70,7 +73,7 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
 	 * @param charsetName 字符集
 	 * @param charsetName 字符集
 	 * @return Properties
 	 * @return Properties
 	 */
 	 */
-	public static Properties getProp(String resource, String charsetName) {
+	public static Props getProp(String resource, String charsetName) {
 		return new Props(resource, charsetName);
 		return new Props(resource, charsetName);
 	}
 	}
 
 
@@ -81,7 +84,7 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
 	 * @param charset 字符集
 	 * @param charset 字符集
 	 * @return Properties
 	 * @return Properties
 	 */
 	 */
-	public static Properties getProp(String resource, Charset charset) {
+	public static Props getProp(String resource, Charset charset) {
 		return new Props(resource, charset);
 		return new Props(resource, charset);
 	}
 	}
 
 
@@ -120,7 +123,7 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
 	 */
 	 */
 	public Props(String path, Charset charset) {
 	public Props(String path, Charset charset) {
 		Assert.notBlank(path, "Blank properties file path !");
 		Assert.notBlank(path, "Blank properties file path !");
-		if(null != charset) {
+		if (null != charset) {
 			this.charset = charset;
 			this.charset = charset;
 		}
 		}
 		this.load(ResourceUtil.getResourceObj(path));
 		this.load(ResourceUtil.getResourceObj(path));
@@ -187,7 +190,7 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
 	 */
 	 */
 	public Props(String path, Class<?> clazz, Charset charset) {
 	public Props(String path, Class<?> clazz, Charset charset) {
 		Assert.notBlank(path, "Blank properties file path !");
 		Assert.notBlank(path, "Blank properties file path !");
-		if(null != charset) {
+		if (null != charset) {
 			this.charset = charset;
 			this.charset = charset;
 		}
 		}
 		this.load(new ClassPathResource(path, clazz));
 		this.load(new ClassPathResource(path, clazz));
@@ -220,7 +223,7 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
 	 */
 	 */
 	public Props(URL propertiesUrl, Charset charset) {
 	public Props(URL propertiesUrl, Charset charset) {
 		Assert.notNull(propertiesUrl, "Null properties URL !");
 		Assert.notNull(propertiesUrl, "Null properties URL !");
-		if(null != charset) {
+		if (null != charset) {
 			this.charset = charset;
 			this.charset = charset;
 		}
 		}
 		this.load(new UrlResource(propertiesUrl));
 		this.load(new UrlResource(propertiesUrl));
@@ -276,7 +279,7 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
 				// 先关闭之前的监听
 				// 先关闭之前的监听
 				this.watchMonitor.close();
 				this.watchMonitor.close();
 			}
 			}
-			this.watchMonitor = WatchUtil.createModify(this.propertiesFileUrl, new SimpleWatcher(){
+			this.watchMonitor = WatchUtil.createModify(this.propertiesFileUrl, new SimpleWatcher() {
 				@Override
 				@Override
 				public void onModify(WatchEvent<?> event, Path currentPath) {
 				public void onModify(WatchEvent<?> event, Path currentPath) {
 					load();
 					load();
@@ -441,17 +444,17 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
 	public <E extends Enum<E>> E getEnum(Class<E> clazz, String key) {
 	public <E extends Enum<E>> E getEnum(Class<E> clazz, String key) {
 		return getEnum(clazz, key, null);
 		return getEnum(clazz, key, null);
 	}
 	}
-	
+
 	@Override
 	@Override
 	public Date getDate(String key, Date defaultValue) {
 	public Date getDate(String key, Date defaultValue) {
 		return Convert.toDate(getStr(key), defaultValue);
 		return Convert.toDate(getStr(key), defaultValue);
 	}
 	}
-	
+
 	@Override
 	@Override
 	public Date getDate(String key) {
 	public Date getDate(String key) {
 		return getDate(key, null);
 		return getDate(key, null);
 	}
 	}
-	
+
 	/**
 	/**
 	 * 获取并删除键值对,当指定键对应值非空时,返回并删除这个值,后边的键对应的值不再查找
 	 * 获取并删除键值对,当指定键对应值非空时,返回并删除这个值,后边的键对应的值不再查找
 	 * 
 	 * 
@@ -469,6 +472,86 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
 		}
 		}
 		return (String) value;
 		return (String) value;
 	}
 	}
+	
+	/**
+	 * 将配置文件转换为Bean,支持嵌套Bean<br>
+	 * 支持的表达式:
+	 * 
+	 * <pre>
+	 * persion
+	 * persion.name
+	 * persons[3]
+	 * person.friends[5].name
+	 * ['person']['friends'][5]['name']
+	 * </pre>
+	 * 
+	 * @param beanClass Bean类
+	 * @return Bean对象
+	 * @since 4.6.3
+	 */
+	public <T> T toBean(Class<T> beanClass) {
+		return toBean(beanClass, null);
+	}
+
+	/**
+	 * 将配置文件转换为Bean,支持嵌套Bean<br>
+	 * 支持的表达式:
+	 * 
+	 * <pre>
+	 * persion
+	 * persion.name
+	 * persons[3]
+	 * person.friends[5].name
+	 * ['person']['friends'][5]['name']
+	 * </pre>
+	 * 
+	 * @param beanClass Bean类
+	 * @param prefix 公共前缀,不指定前缀传null,当指定前缀后非此前缀的属性被忽略
+	 * @return Bean对象
+	 * @since 4.6.3
+	 */
+	public <T> T toBean(Class<T> beanClass, String prefix) {
+		final T bean = ReflectUtil.newInstanceIfPossible(beanClass);
+		return fillBean(bean, prefix);
+	}
+	
+	/**
+	 * 将配置文件转换为Bean,支持嵌套Bean<br>
+	 * 支持的表达式:
+	 * 
+	 * <pre>
+	 * persion
+	 * persion.name
+	 * persons[3]
+	 * person.friends[5].name
+	 * ['person']['friends'][5]['name']
+	 * </pre>
+	 * 
+	 * @param bean Bean对象
+	 * @param prefix 公共前缀,不指定前缀传null,当指定前缀后非此前缀的属性被忽略
+	 * @return Bean对象
+	 * @since 4.6.3
+	 */
+	public <T> T fillBean(T bean, String prefix) {
+		prefix = StrUtil.addSuffixIfNot(prefix, StrUtil.DOT);
+
+		String key;
+		for (java.util.Map.Entry<Object, Object> 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
 	// ----------------------------------------------------------------------- Get end
 
 
@@ -489,7 +572,7 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
 	 * @param absolutePath 设置文件的绝对路径
 	 * @param absolutePath 设置文件的绝对路径
 	 * @throws IORuntimeException IO异常,可能为文件未找到
 	 * @throws IORuntimeException IO异常,可能为文件未找到
 	 */
 	 */
-	public void store(String absolutePath) throws IORuntimeException{
+	public void store(String absolutePath) throws IORuntimeException {
 		Writer writer = null;
 		Writer writer = null;
 		try {
 		try {
 			writer = FileUtil.getWriter(absolutePath, charset, false);
 			writer = FileUtil.getWriter(absolutePath, charset, false);

+ 50 - 8
hutool-setting/src/test/java/cn/hutool/setting/test/PropsTest.java

@@ -1,5 +1,8 @@
 package cn.hutool.setting.test;
 package cn.hutool.setting.test;
 
 
+import java.util.List;
+import java.util.Map;
+
 import org.junit.Assert;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Ignore;
@@ -8,37 +11,76 @@ import org.junit.Test;
 import cn.hutool.log.LogFactory;
 import cn.hutool.log.LogFactory;
 import cn.hutool.log.dialect.console.ConsoleLogFactory;
 import cn.hutool.log.dialect.console.ConsoleLogFactory;
 import cn.hutool.setting.dialect.Props;
 import cn.hutool.setting.dialect.Props;
+import lombok.Data;
 
 
 /**
 /**
  * Setting单元测试
  * Setting单元测试
+ * 
  * @author Looly
  * @author Looly
  *
  *
  */
  */
 public class PropsTest {
 public class PropsTest {
-	
+
 	@Before
 	@Before
-	public void init(){
+	public void init() {
 		LogFactory.setCurrentLogFactory(ConsoleLogFactory.class);
 		LogFactory.setCurrentLogFactory(ConsoleLogFactory.class);
 	}
 	}
-	
+
 	@Test
 	@Test
-	public void propTest(){
+	public void propTest() {
 		Props props = new Props("test.properties");
 		Props props = new Props("test.properties");
 		String user = props.getProperty("user");
 		String user = props.getProperty("user");
 		Assert.assertEquals(user, "root");
 		Assert.assertEquals(user, "root");
-		
+
 		String driver = props.getStr("driver");
 		String driver = props.getStr("driver");
 		Assert.assertEquals(driver, "com.mysql.jdbc.Driver");
 		Assert.assertEquals(driver, "com.mysql.jdbc.Driver");
 	}
 	}
-	
+
 	@Test
 	@Test
 	@Ignore
 	@Ignore
-	public void propTestForAbsPAth(){
+	public void propTestForAbsPAth() {
 		Props props = new Props("d:/test.properties");
 		Props props = new Props("d:/test.properties");
 		String user = props.getProperty("user");
 		String user = props.getProperty("user");
 		Assert.assertEquals(user, "root");
 		Assert.assertEquals(user, "root");
-		
+
 		String driver = props.getStr("driver");
 		String driver = props.getStr("driver");
 		Assert.assertEquals(driver, "com.mysql.jdbc.Driver");
 		Assert.assertEquals(driver, "com.mysql.jdbc.Driver");
 	}
 	}
+	
+	@Test
+	public void toBeanTest() {
+		Props props = Props.getProp("to_bean_test.properties");
+		
+		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<String> defaultRecipients;
+		private Map<String, String> additionalHeaders;
+	}
+	
+	@Data
+	public static class Credentials {
+		private String authMethod;
+		private String username;
+		private String password;
+	}
 }
 }

+ 20 - 0
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