浏览代码

add DynabeanValueProvider

Looly 5 年之前
父节点
当前提交
5aa10c06ee

+ 68 - 55
hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java

@@ -6,154 +6,167 @@ import cn.hutool.core.util.ClassUtil;
 import cn.hutool.core.util.ReflectUtil;
 
 import java.io.Serializable;
-import java.lang.reflect.Method;
 import java.util.Map;
 
 /**
  * 动态Bean,通过反射对Bean的相关方法做操作<br>
  * 支持Map和普通Bean
- * 
+ *
  * @author Looly
  * @since 3.0.7
  */
-public class DynaBean extends CloneSupport<DynaBean> implements Serializable{
+public class DynaBean extends CloneSupport<DynaBean> implements Serializable {
 	private static final long serialVersionUID = 1L;
 
 	private final Class<?> beanClass;
 	private final Object bean;
-	
+
 	/**
 	 * 创建一个{@link DynaBean}
+	 *
 	 * @param bean 普通Bean
 	 * @return {@link DynaBean}
 	 */
-	public static DynaBean create(Object bean){
+	public static DynaBean create(Object bean) {
 		return new DynaBean(bean);
 	}
+
 	/**
 	 * 创建一个{@link DynaBean}
+	 *
 	 * @param beanClass Bean类
-	 * @param params 构造Bean所需要的参数
+	 * @param params    构造Bean所需要的参数
 	 * @return {@link DynaBean}
 	 */
-	public static DynaBean create(Class<?> beanClass, Object... params){
+	public static DynaBean create(Class<?> beanClass, Object... params) {
 		return new DynaBean(beanClass, params);
 	}
-	
+
 	//------------------------------------------------------------------------ Constructor start
+
 	/**
 	 * 构造
+	 *
 	 * @param beanClass Bean类
-	 * @param params 构造Bean所需要的参数
+	 * @param params    构造Bean所需要的参数
 	 */
-	public DynaBean(Class<?> beanClass, Object... params){
+	public DynaBean(Class<?> beanClass, Object... params) {
 		this(ReflectUtil.newInstance(beanClass, params));
 	}
-	
+
 	/**
 	 * 构造
+	 *
 	 * @param bean 原始Bean
 	 */
-	public DynaBean(Object bean){
+	public DynaBean(Object bean) {
 		Assert.notNull(bean);
-		if(bean instanceof DynaBean){
-			bean = ((DynaBean)bean).getBean();
+		if (bean instanceof DynaBean) {
+			bean = ((DynaBean) bean).getBean();
 		}
 		this.bean = bean;
 		this.beanClass = ClassUtil.getClass(bean);
 	}
 	//------------------------------------------------------------------------ Constructor end
-	
+
 	/**
 	 * 获得字段对应值
-	 * @param <T> 属性值类型
+	 *
+	 * @param <T>       属性值类型
 	 * @param fieldName 字段名
 	 * @return 字段值
 	 * @throws BeanException 反射获取属性值或字段值导致的异常
 	 */
 	@SuppressWarnings("unchecked")
-	public <T> T get(String fieldName) throws BeanException{
-		if(Map.class.isAssignableFrom(beanClass)){
-			return (T) ((Map<?, ?>)bean).get(fieldName);
-		}else{
-			try {
-				final Method method = BeanUtil.getBeanDesc(beanClass).getGetter(fieldName);
-				if(null == method){
-					throw new BeanException("No get method for {}", fieldName);
-				}
-				return (T) method.invoke(this.bean);
-			} catch (Exception e) {
-				throw new BeanException(e);
+	public <T> T get(String fieldName) throws BeanException {
+		if (Map.class.isAssignableFrom(beanClass)) {
+			return (T) ((Map<?, ?>) bean).get(fieldName);
+		} else {
+			final BeanDesc.PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
+			if(null == prop){
+				throw new BeanException("No public field or get method for {}", fieldName);
 			}
+			return (T) prop.getValue(bean);
 		}
 	}
-	
+
+	/**
+	 * 检查是否有指定名称的bean属性
+	 *
+	 * @param fieldName 字段名
+	 * @return 是否有bean属性
+	 * @since 5.4.2
+	 */
+	public boolean containsProp(String fieldName){
+		return null != BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
+	}
+
 	/**
 	 * 获得字段对应值,获取异常返回{@code null}
-	 * 
-	 * @param <T> 属性值类型
+	 *
+	 * @param <T>       属性值类型
 	 * @param fieldName 字段名
 	 * @return 字段值
 	 * @since 3.1.1
 	 */
-	public <T> T safeGet(String fieldName){
+	public <T> T safeGet(String fieldName) {
 		try {
 			return get(fieldName);
 		} catch (Exception e) {
 			return null;
 		}
 	}
-	
+
 	/**
 	 * 设置字段值
+	 *
 	 * @param fieldName 字段名
-	 * @param value 字段值
+	 * @param value     字段值
 	 * @throws BeanException 反射获取属性值或字段值导致的异常
 	 */
-	@SuppressWarnings({ "unchecked", "rawtypes" })
-	public void set(String fieldName, Object value) throws BeanException{
-		if(Map.class.isAssignableFrom(beanClass)){
-			((Map)bean).put(fieldName, value);
-		}else{
-			try {
-				final Method setter = BeanUtil.getBeanDesc(beanClass).getSetter(fieldName);
-				if(null == setter){
-					throw new BeanException("No set method for {}", fieldName);
-				}
-				setter.invoke(this.bean, value);
-			} catch (Exception e) {
-				throw new BeanException(e);
+	@SuppressWarnings({"unchecked", "rawtypes"})
+	public void set(String fieldName, Object value) throws BeanException {
+		if (Map.class.isAssignableFrom(beanClass)) {
+			((Map) bean).put(fieldName, value);
+		} else {
+			final BeanDesc.PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
+			if(null == prop){
+				throw new BeanException("No public field or set method for {}", fieldName);
 			}
+			prop.setValue(bean, value);
 		}
 	}
-	
+
 	/**
 	 * 执行原始Bean中的方法
+	 *
 	 * @param methodName 方法名
-	 * @param params 参数
+	 * @param params     参数
 	 * @return 执行结果,可能为null
 	 */
-	public Object invoke(String methodName, Object... params){
+	public Object invoke(String methodName, Object... params) {
 		return ReflectUtil.invoke(this.bean, methodName, params);
 	}
-	
+
 	/**
 	 * 获得原始Bean
+	 *
 	 * @param <T> Bean类型
 	 * @return bean
 	 */
 	@SuppressWarnings("unchecked")
-	public <T> T getBean(){
-		return (T)this.bean;
+	public <T> T getBean() {
+		return (T) this.bean;
 	}
-	
+
 	/**
 	 * 获得Bean的类型
+	 *
 	 * @param <T> Bean类型
 	 * @return Bean类型
 	 */
 	@SuppressWarnings("unchecked")
-	public <T> Class<T> getBeanClass(){
+	public <T> Class<T> getBeanClass() {
 		return (Class<T>) this.beanClass;
 	}
 

+ 5 - 0
hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java

@@ -3,7 +3,9 @@ package cn.hutool.core.bean.copier;
 import cn.hutool.core.bean.BeanDesc.PropDesc;
 import cn.hutool.core.bean.BeanException;
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.bean.DynaBean;
 import cn.hutool.core.bean.copier.provider.BeanValueProvider;
+import cn.hutool.core.bean.copier.provider.DynaBeanValueProvider;
 import cn.hutool.core.bean.copier.provider.MapValueProvider;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.copier.Copier;
@@ -97,6 +99,9 @@ public class BeanCopier<T> implements Copier<T>, Serializable {
 			if (this.source instanceof ValueProvider) {
 				// 目标只支持Bean
 				valueProviderToBean((ValueProvider<String>) this.source, this.dest);
+			} else if (this.source instanceof DynaBean) {
+				// 目标只支持Bean
+				valueProviderToBean(new DynaBeanValueProvider((DynaBean) this.source, copyOptions.ignoreError), this.dest);
 			} else if (this.source instanceof Map) {
 				if (this.dest instanceof Map) {
 					mapToMap((Map<?, ?>) this.source, (Map<?, ?>) this.dest);

+ 10 - 14
hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java

@@ -10,9 +10,8 @@ import java.util.Map;
 
 /**
  * Bean的值提供者
- * 
- * @author looly
  *
+ * @author looly
  */
 public class BeanValueProvider implements ValueProvider<String> {
 
@@ -22,9 +21,9 @@ public class BeanValueProvider implements ValueProvider<String> {
 
 	/**
 	 * 构造
-	 * 
-	 * @param bean Bean
-	 * @param ignoreCase 是否忽略字段大小写
+	 *
+	 * @param bean        Bean
+	 * @param ignoreCase  是否忽略字段大小写
 	 * @param ignoreError 是否忽略字段值读取错误
 	 */
 	public BeanValueProvider(Object bean, boolean ignoreCase, boolean ignoreError) {
@@ -35,11 +34,7 @@ public class BeanValueProvider implements ValueProvider<String> {
 
 	@Override
 	public Object value(String key, Type valueType) {
-		PropDesc sourcePd = sourcePdMap.get(key);
-		if(null == sourcePd && (Boolean.class == valueType || boolean.class == valueType)) {
-			//boolean类型字段字段名支持两种方式
-			sourcePd = sourcePdMap.get(StrUtil.upperFirstAndAddPre(key, "is"));
-		}
+		final PropDesc sourcePd = getPropDesc(key, valueType);
 
 		Object result = null;
 		if (null != sourcePd) {
@@ -50,7 +45,7 @@ public class BeanValueProvider implements ValueProvider<String> {
 
 	@Override
 	public boolean containsKey(String key) {
-		PropDesc sourcePd = getPropDesc(key);
+		final PropDesc sourcePd = getPropDesc(key, null);
 
 		// 字段描述不存在或忽略读的情况下,表示不存在
 		return null != sourcePd && false == sourcePd.isIgnoreGet();
@@ -59,12 +54,13 @@ public class BeanValueProvider implements ValueProvider<String> {
 	/**
 	 * 获得属性描述
 	 *
-	 * @param key 字段名
+	 * @param key       字段名
+	 * @param valueType 值类型,用于判断是否为Boolean,可以为null
 	 * @return 属性描述
 	 */
-	private PropDesc getPropDesc(String key){
+	private PropDesc getPropDesc(String key, Type valueType) {
 		PropDesc sourcePd = sourcePdMap.get(key);
-		if(null == sourcePd) {
+		if (null == sourcePd && (null == valueType || Boolean.class == valueType || boolean.class == valueType)) {
 			//boolean类型字段字段名支持两种方式
 			sourcePd = sourcePdMap.get(StrUtil.upperFirstAndAddPre(key, "is"));
 		}

+ 42 - 0
hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/DynaBeanValueProvider.java

@@ -0,0 +1,42 @@
+package cn.hutool.core.bean.copier.provider;
+
+import cn.hutool.core.bean.DynaBean;
+import cn.hutool.core.bean.copier.ValueProvider;
+import cn.hutool.core.convert.Convert;
+
+import java.lang.reflect.Type;
+
+/**
+ * DynaBean值提供者
+ *
+ * @author looly
+ * @since 5.4.2
+ */
+public class DynaBeanValueProvider implements ValueProvider<String> {
+
+	private final DynaBean dynaBean;
+	private final boolean ignoreError;
+
+	/**
+	 * 构造
+	 *
+	 * @param dynaBean        DynaBean
+	 * @param ignoreError 是否忽略错误
+	 */
+	public DynaBeanValueProvider(DynaBean dynaBean, boolean ignoreError) {
+		this.dynaBean = dynaBean;
+		this.ignoreError = ignoreError;
+	}
+
+	@Override
+	public Object value(String key, Type valueType) {
+		final Object value = dynaBean.get(key);
+		return Convert.convertWithCheck(valueType, value, null, this.ignoreError);
+	}
+
+	@Override
+	public boolean containsKey(String key) {
+		return dynaBean.containsProp(key);
+	}
+
+}