All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.feilong.core.bean.PropertyUtil Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.0.8
Show newest version
/*
 * Copyright (C) 2008 feilong
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.feilong.core.bean;

import static com.feilong.core.Validator.isNotNullOrEmpty;
import static com.feilong.core.Validator.isNullOrEmpty;
import static com.feilong.core.util.MapUtil.newLinkedHashMap;

import java.beans.PropertyDescriptor;
import java.util.Collection;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.feilong.core.Validate;
import com.feilong.core.lang.ClassUtil;
import com.feilong.lib.beanutils.PropertyUtils;
import com.feilong.lib.lang3.ClassUtils;
import com.feilong.tools.slf4j.Slf4jUtil;

/**
 * 对 {@link com.feilong.lib.beanutils.PropertyUtils}的再次封装.
 * 
 * 

说明:

*
*
    *
  1. 目的是将原来的 checkedException 异常 转换成 {@link BeanOperationException}
  2. *
*
* *

{@link PropertyUtils}与 {@link com.feilong.lib.beanutils.BeanUtils}:

* *
*

* {@link PropertyUtils}类和{@link com.feilong.lib.beanutils.BeanUtils}类很多的方法在参数上都是相同的,但返回值不同.
* {@link com.feilong.lib.beanutils.BeanUtils}着重于"Bean",返回值通常是{@link String},
* 而{@link PropertyUtils}着重于属性,它的返回值通常是{@link Object}.  *

*
* * @author feilong * @see com.feilong.lib.beanutils.PropertyUtils * @see com.feilong.core.bean.BeanUtil * @since 1.0.0 */ public final class PropertyUtil{ /** The Constant LOGGER. */ private static final Logger LOGGER = LoggerFactory.getLogger(PropertyUtil.class); /** * 传入的bean 是null 的消息提示. * * @since 2.1.0 */ private static final String MESSAGE_BEAN_IS_NULL = "bean can't be null!"; /** * 传入的propertyName 是blank 的消息提示. * * @since 2.1.0 */ private static final String MESSAGE_PROPERTYNAME_IS_BLANK = "propertyName can't be blank!"; //--------------------------------------------------------------- /** Don't let anyone instantiate this class. */ private PropertyUtil(){ //AssertionError不是必须的. 但它可以避免不小心在类的内部调用构造器. 保证该类在任何情况下都不会被实例化. //see 《Effective Java》 2nd throw new AssertionError("No " + getClass().getName() + " instances for you!"); } //--------------------------------------------------------------- /** * Retrieve the property descriptors for the specified class, introspecting and caching them the first time a particular bean class is * encountered. * * @param klass * the klass * @return 如果 klass 是null,抛出 {@link NullPointerException}
* @since 3.0.0 */ public static PropertyDescriptor[] getPropertyDescriptors(Class klass){ Validate.notNull(klass, "klass can't be null!"); try{ return PropertyUtils.getPropertyDescriptors(klass); }catch (Exception e){ String pattern = "getPropertyDescriptors exception,klass:[{}]"; throw new BeanOperationException(Slf4jUtil.format(pattern, klass), e); } } //--------------------------------------------------------------- /** * 将 fromObj 中的全部或者一组属性的值,复制到 toObj 对象中. * *

注意点:

* *
* *
    *
  1. 如果 toObj 是null,抛出 {@link NullPointerException}
  2. *
  3. 如果 fromObj 是null,抛出 {@link NullPointerException}
  4. *
  5. 对于Date类型,不需要先注册converter
  6. *
  7. 这种copy都是 浅拷贝,复制后的2个Bean的同一个属性可能拥有同一个对象的ref,这个在使用时要小心,特别是对于属性为自定义类的情况 .
  8. *
*
* *

使用示例:

* *
* *
     * User oldUser = new User();
     * oldUser.setId(5L);
     * oldUser.setMoney(new BigDecimal(500000));
     * oldUser.setDate(now());
     * oldUser.setNickName(ConvertUtil.toArray("feilong", "飞天奔月", "venusdrogon"));
     * 
     * User newUser = new User();
     * PropertyUtil.copyProperties(newUser, oldUser, "date", "money", "nickName");
     * LOGGER.debug(JsonUtil.format(newUser));
     * 
* * 返回: * *
     * {
     *         "date": "2015-09-06 13:27:43",
     *         "id": 0,
     *         "nickName":         [
     *             "feilong",
     *             "飞天奔月",
     *             "venusdrogon"
     *         ],
     *         "age": 0,
     *         "name": "feilong",
     *         "money": 500000,
     *         "userInfo": {"age": 0}
     * }
     * 
* *
* *

重构:

* *
* *

* 对于以下代码: *

* *
     * 
     * private ContactCommand toContactCommand(ShippingInfoSubForm shippingInfoSubForm){
     *     ContactCommand contactCommand = new ContactCommand();
     *     contactCommand.setCountryId(shippingInfoSubForm.getCountryId());
     *     contactCommand.setProvinceId(shippingInfoSubForm.getProvinceId());
     *     contactCommand.setCityId(shippingInfoSubForm.getCityId());
     *     contactCommand.setAreaId(shippingInfoSubForm.getAreaId());
     *     contactCommand.setTownId(shippingInfoSubForm.getTownId());
     *     return contactCommand;
     * }
     * 
     * 
* * 可以重构成: * *
     * 
     * private ContactCommand toContactCommand(ShippingInfoSubForm shippingInfoSubForm){
     *     ContactCommand contactCommand = new ContactCommand();
     *     PropertyUtil.copyProperties(contactCommand, shippingInfoSubForm, "countryId", "provinceId", "cityId", "areaId", "townId");
     *     return contactCommand;
     * }
     * 
* *

* 可以看出,代码更精简,目的性更明确 *

* *
* *

{@link com.feilong.lib.beanutils.BeanUtils#copyProperties(Object, Object)}与 * {@link PropertyUtils#copyProperties(Object, Object)}区别

* *
*
    *
  • {@link com.feilong.lib.beanutils.BeanUtils#copyProperties(Object, Object) BeanUtils} * 提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,
    * 而 {@link PropertyUtils#copyProperties(Object, Object) PropertyUtils}不支持这个功能,但是速度会更快一些.
  • *
  • commons-beanutils v1.9.0以前的版本 BeanUtils不允许对象的属性值为 null,PropertyUtils可以拷贝属性值 null的对象.
    * (注:commons-beanutils v1.9.0+修复了这个情况,BeanUtilsBean.copyProperties() no longer throws a ConversionException for null properties * of certain data types),具体参阅commons-beanutils的 * RELEASE-NOTES.txt
  • *
*
* *

相比较直接调用 {@link PropertyUtils#copyProperties(Object, Object)}的优点:

*
*
    *
  1. 将 checkedException 异常转成了 {@link BeanOperationException} RuntimeException,因为通常copy的时候出现了checkedException,也是普普通通记录下log,没有更好的处理方式 *
  2. *
  3. 支持 includePropertyNames 参数,允许针对性copy 个别属性
  4. *
  5. 更多,更容易理解的的javadoc
  6. *
*
* * @param toObj * 目标对象 * @param fromObj * 原始对象 * @param includePropertyNames * 包含的属性数组名字数组,(can be nested/indexed/mapped/combo)
* 如果是null或者empty,将会调用 {@link PropertyUtils#copyProperties(Object, Object)}
*
    *
  1. 如果没有传入includePropertyNames参数,那么直接调用{@link PropertyUtils#copyProperties(Object, Object)},否则循环调用 * {@link #getProperty(Object, String)}再{@link #setProperty(Object, String, Object)}到toObj对象中
  2. *
  3. 如果传入的includePropertyNames,含有 fromObj没有的属性名字,将会抛出异常
  4. *
  5. 如果传入的includePropertyNames,含有 fromObj有,但是 toObj没有的属性名字,会抛出异常,see * {@link com.feilong.lib.beanutils.PropertyUtilsBean#setSimpleProperty(Object, String, Object) copyProperties} * Line2078
  6. *
* @throws NullPointerException * 如果 toObj 是null,或者 fromObj 是null * @throws BeanOperationException * 如果在copy的过程中,有任何的checkedException,将会被转成该异常返回 * @see #setProperty(Object, String, Object) * @see BeanUtil#copyProperties(Object, Object, String...) * @see "org.apache.commons.beanutils.PropertyUtilsBean#copyProperties(Object, Object)" * @see Bean复制的几种框架性能比较(Apache BeanUtils、PropertyUtils,Spring * BeanUtils,Cglib BeanCopier) * @since 1.4.1 */ public static void copyProperties(Object toObj,Object fromObj,String...includePropertyNames){ Validate.notNull(toObj, "toObj [destination bean] not specified!"); Validate.notNull(fromObj, "fromObj [origin bean] not specified!"); //--------------------------------------------------------------- if (isNullOrEmpty(includePropertyNames)){ try{ PropertyUtils.copyProperties(toObj, fromObj); return; }catch (Exception e){ String pattern = "copyProperties exception,toObj:[{}],fromObj:[{}],includePropertyNames:[{}]"; throw new BeanOperationException(Slf4jUtil.format(pattern, toObj, fromObj, includePropertyNames), e); } } //--------------------------------------------------------------- for (String propertyName : includePropertyNames){ Object value = getProperty(fromObj, propertyName); setProperty(toObj, propertyName, value); } } //--------------------------------------------------------------- /** * 返回一个 bean中指定属性 propertyNames可读属性,并将属性名/属性值放入一个 * {@link java.util.LinkedHashMap LinkedHashMap} 中. * *

示例:

* *
* *

* 场景: 取到user bean里面所有的属性成map *

* *
     * User user = new User();
     * user.setId(5L);
     * user.setDate(now());
     * 
     * LOGGER.debug(JsonUtil.format(PropertyUtil.describe(user));
     * 
* * 返回: * *
     * {
     * "id": 5,
     * "name": "feilong",
     * "age": null,
     * "date": "2016-07-13 22:18:26"
     * }
     * 
* *
* *

* 场景: 提取user bean "date"和 "id"属性: *

* *
     * User user = new User();
     * user.setId(5L);
     * user.setDate(now());
     * 
     * LOGGER.debug(JsonUtil.format(PropertyUtil.describe(user, "date", "id"));
     * 
* * 返回的结果,按照指定参数名称顺序: * *
     * {
     * "date": "2016-07-13 22:21:24",
     * "id": 5
     * }
     * 
* *
* *

说明:

*
*
    *
  1. 另外还有一个名为class的属性,属性值是Object的类名,事实上class是java.lang.Object的一个属性
  2. *
  3. 如果 propertyNames是null或者 empty,那么获取所有属性的值
  4. *
  5. map的key按照 propertyNames 的顺序
  6. *
*
* *

原理:

* *
*
    *
  1. 取到bean class的 {@link java.beans.PropertyDescriptor}数组
  2. *
  3. 循环,找到 {@link java.beans.PropertyDescriptor#getReadMethod()}
  4. *
  5. 将 name and {@link com.feilong.lib.beanutils.PropertyUtilsBean#getProperty(Object, String)} 设置到map中
  6. *
*
* * @param bean * Bean whose properties are to be extracted * @param propertyNames * 属性名称 (can be nested/indexed/mapped/combo),参见 propertyName * @return 如果 propertyNames 是null或者empty,返回 {@link PropertyUtils#describe(Object)}
* @throws NullPointerException * 如果 bean 是null,或者propertyNames 包含 null的元素 * @throws IllegalArgumentException * 如果 propertyNames 包含 blank的元素 * @see com.feilong.lib.beanutils.BeanUtils#describe(Object) * @see com.feilong.lib.beanutils.PropertyUtils#describe(Object) * @since 1.8.0 */ public static Map describe(Object bean,String...propertyNames){ Validate.notNull(bean, MESSAGE_BEAN_IS_NULL); //--------------------------------------------------------------- if (isNullOrEmpty(propertyNames)){ try{ return PropertyUtils.describe(bean); }catch (Exception e){ String pattern = "describe exception,bean:[{}],propertyNames:[{}]"; throw new BeanOperationException(Slf4jUtil.format(pattern, bean, propertyNames), e); } } //--------------------------------------------------------------- Map map = newLinkedHashMap(propertyNames.length); for (String propertyName : propertyNames){ map.put(propertyName, getProperty(bean, propertyName)); } return map; } //--------------------------------------------------------------- /** * 使用 {@link PropertyUtils#setProperty(Object, String, Object)} 来设置指定bean对象中的指定属性的值. * *

说明:

*
*
    *
  1. 不会进行类型转换
  2. *
*
* *

示例:

* *
* *
     * User newUser = new User();
     * PropertyUtil.setProperty(newUser, "name", "feilong");
     * LOGGER.info(JsonUtil.format(newUser));
     * 
* * 返回: * *
     * {
     * "age": 0,
     * "name": "feilong"
     * }
     * 
* *
* *

注意点:

* *
* *
    *
  1. 如果 bean 是null,抛出 {@link NullPointerException}
  2. *
  3. 如果 propertyName 是null,抛出 {@link NullPointerException}
  4. *
  5. 如果 propertyName 是blank,抛出 {@link IllegalArgumentException}
  6. *
  7. 如果bean没有传入的 propertyName属性名字,会抛出异常,see * {@link com.feilong.lib.beanutils.PropertyUtilsBean#setSimpleProperty(Object, String, Object) setSimpleProperty} Line2078,转成 * {@link BeanOperationException}
  8. *
  9. 对于Date类型,不需要先注册converter
  10. *
*
* * @param bean * Bean whose property is to be modified * @param propertyName * 属性名称 (can be nested/indexed/mapped/combo),参见 propertyName * @param value * Value to which this property is to be set * @see com.feilong.lib.beanutils.BeanUtils#setProperty(Object, String, Object) * @see com.feilong.lib.beanutils.PropertyUtils#setProperty(Object, String, Object) * @see BeanUtil#setProperty(Object, String, Object) */ public static void setProperty(Object bean,String propertyName,Object value){ Validate.notNull(bean, MESSAGE_BEAN_IS_NULL); Validate.notBlank(propertyName, MESSAGE_PROPERTYNAME_IS_BLANK); //--------------------------------------------------------------- try{ PropertyUtils.setProperty(bean, propertyName, value); }catch (Exception e){ String pattern = "setProperty exception,bean:[{}],propertyName:[{}],value:[{}]"; throw new BeanOperationException(Slf4jUtil.format(pattern, bean, propertyName, value), e); } } //--------------------------------------------------------------- /** * 如果 value isNotNullOrEmpty,那么才调用 {@link #setProperty(Object, String, Object)}. * *

注意点:

* *
*
    *
  1. 如果 bean 是null,抛出 {@link NullPointerException}
  2. *
  3. 如果 propertyName 是null,抛出 {@link NullPointerException}
  4. *
  5. 如果 propertyName 是blank,抛出 {@link IllegalArgumentException}
  6. *
  7. 如果bean没有传入的 propertyName属性名字,会抛出异常,see * {@link com.feilong.lib.beanutils.PropertyUtilsBean#setSimpleProperty(Object, String, Object)} Line2078
  8. *
  9. 对于Date类型,不需要先注册converter
  10. *
*
* * @param bean * Bean whose property is to be modified * @param propertyName * 属性名称 (can be nested/indexed/mapped/combo),参见 propertyName * @param value * Value to which this property is to be set * @since 1.5.3 */ public static void setPropertyIfValueNotNullOrEmpty(Object bean,String propertyName,Object value){ if (isNotNullOrEmpty(value)){ setProperty(bean, propertyName, value); } } /** * 如果 null != value,那么才调用 {@link #setProperty(Object, String, Object)}. * *

注意点:

* *
* *
    *
  1. 如果 bean 是null,抛出 {@link NullPointerException}
  2. *
  3. 如果 propertyName 是null,抛出 {@link NullPointerException}
  4. *
  5. 如果 propertyName 是blank,抛出 {@link IllegalArgumentException}
  6. *
  7. 如果bean没有传入的 propertyName属性名字,会抛出异常,see * {@link com.feilong.lib.beanutils.PropertyUtilsBean#setSimpleProperty(Object, String, Object)} Line2078
  8. *
  9. 对于Date类型,不需要先注册converter
  10. *
*
* * @param bean * Bean whose property is to be modified * @param propertyName * 属性名称 (can be nested/indexed/mapped/combo),参见 propertyName * @param value * Value to which this property is to be set * @see #setProperty(Object, String, Object) * @since 1.5.3 */ public static void setPropertyIfValueNotNull(Object bean,String propertyName,Object value){ if (null != value){ setProperty(bean, propertyName, value); } } //--------------------------------------------------------------- // [start] getProperty /** * 使用 {@link PropertyUtils#getProperty(Object, String)} 从指定bean对象中取得指定属性名称的值. * *

说明:

*
*
    *
  1. 原样取出值,不会进行类型转换.
  2. *
*
* *

示例:

*
*

* 场景: 取list中第一个元素的id *

* *
     * User user = new User();
     * user.setId(5L);
     * user.setDate(now());
     * 
     * List{@code } list = toList(user, user, user);
     * 
     * Long id = PropertyUtil.getProperty(list, "[0].id");
     * 
* * 返回: * *
     * 5
     * 
* *
* * @param * the generic type * @param bean * Bean whose property is to be extracted * @param propertyName * 属性名称 (can be nested/indexed/mapped/combo),参见 propertyName * @return 如果 bean 是null,抛出 {@link NullPointerException}
* 如果 propertyName 是null,抛出 {@link NullPointerException}
* 如果 propertyName 是blank,抛出 {@link IllegalArgumentException}
* 否则 使用{@link PropertyUtils#getProperty(Object, String)} 从对象中取得属性值 * @see BeanUtil#getProperty(Object, String) * @see com.feilong.lib.beanutils.BeanUtils#getProperty(Object, String) * @see com.feilong.lib.beanutils.PropertyUtils#getProperty(Object, String) * @see com.feilong.lib.beanutils.PropertyUtilsBean */ public static T getProperty(Object bean,String propertyName){ Validate.notNull(bean, MESSAGE_BEAN_IS_NULL); Validate.notBlank(propertyName, MESSAGE_PROPERTYNAME_IS_BLANK); return PropertyValueObtainer.obtain(bean, propertyName); } //--------------------------------------------------------------- // [end] /** * 从指定的 obj中,查找指定类型 toBeFindedClassType 的值. * *

说明:

* *
*
    *
  1. 如果 ClassUtil.isInstance(obj, toBeFindedClassType) 直接返回 findValue
  2. *
  3. 不支持obj是isPrimitiveOrWrapper,CharSequence,Collection,Map类型,自动过滤
  4. *
  5. 调用 {@link PropertyUtil#describe(Object, String...)} 再递归查找
  6. *
  7. 目前暂不支持从集合里面找到指定类型的值,如果你有相关需求,可以调用 "org.springframework.util.CollectionUtils#findValueOfType(Collection, Class)"
  8. *
*
* *

示例:

*
* *

* 场景: 从User中找到UserInfo类型的值 *

* *
     * User user = new User();
     * user.setId(5L);
     * user.setDate(now());
     * user.getUserInfo().setAge(28);
     * 
     * LOGGER.info(JsonUtil.format(PropertyUtil.findValueOfType(user, UserInfo.class)));
     * 
* * 返回: * *
     * {"age": 28}
     * 
* *
* * @param * the generic type * @param obj * 要被查找的对象 * @param toBeFindedClassType * the to be finded class type * @return 如果 obj 是null或者是empty,返回null
* 如果 toBeFindedClassType 是null,抛出 {@link NullPointerException}
* 如果 ClassUtil.isInstance(obj, toBeFindedClassType),直接返回 obj
* 从对象中查找匹配的类型,如果找不到返回 null
* @see "org.springframework.util.CollectionUtils#findValueOfType(Collection, Class)" * @since 1.4.1 */ @SuppressWarnings("unchecked") public static T findValueOfType(Object obj,Class toBeFindedClassType){ if (isNullOrEmpty(obj)){ return null; } Validate.notNull(toBeFindedClassType, "toBeFindedClassType can't be null/empty!"); //--------------------------------------------------------------- if (ClassUtil.isInstance(obj, toBeFindedClassType)){ return (T) obj; } //--------------------------------------------------------------- if (isDonotSupportFindType(obj)){ LOGGER.trace("obj:[{}] not support find toBeFindedClassType:[{}]", obj.getClass().getName(), toBeFindedClassType.getName()); return null; } //--------------------------------------------------------------- Map describe = describe(obj); for (Map.Entry entry : describe.entrySet()){ String key = entry.getKey(); Object value = entry.getValue(); if (null != value && !"class".equals(key)){ //级联查询 T t = findValueOfType(value, toBeFindedClassType); if (null != t){ return t; } } } return null; } //--------------------------------------------------------------- /** * 一般自定义的command 里面 就是些 string int,list map等对象. * *

* 这些我们过滤掉,只取类型是 findedClassType的 *

* * @param obj * the obj * @return true, if checks if is can find type * @since 1.6.3 */ private static boolean isDonotSupportFindType(Object obj){ //一般自定义的command 里面 就是些 string int,list map等对象 //这些我们过滤掉,只取类型是 findedClassType的 return ClassUtils.isPrimitiveOrWrapper(obj.getClass())// || ClassUtil.isInstanceAnyClass(obj, CharSequence.class, Collection.class, Map.class); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy