com.xiaoleilu.hutool.bean.BeanUtil Maven / Gradle / Ivy
package com.xiaoleilu.hutool.bean;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.xiaoleilu.hutool.bean.BeanDesc.PropDesc;
import com.xiaoleilu.hutool.bean.copier.BeanCopier;
import com.xiaoleilu.hutool.bean.copier.CopyOptions;
import com.xiaoleilu.hutool.bean.copier.ValueProvider;
import com.xiaoleilu.hutool.exceptions.UtilException;
import com.xiaoleilu.hutool.map.CaseInsensitiveMap;
import com.xiaoleilu.hutool.map.MapUtil;
import com.xiaoleilu.hutool.util.ArrayUtil;
import com.xiaoleilu.hutool.util.ClassUtil;
import com.xiaoleilu.hutool.util.ReflectUtil;
import com.xiaoleilu.hutool.util.StrUtil;
/**
* Bean工具类
*
*
* 把一个拥有对属性进行set和get方法的类,我们就可以称之为JavaBean。
*
*
* @author Looly
* @since 3.1.2
*/
public class BeanUtil {
/**
* 判断是否为Bean对象
* 判定方法是是否存在只有一个参数的setXXX方法
*
* @param clazz 待测试类
* @return 是否为Bean对象
*/
public static boolean isBean(Class> clazz) {
if (ClassUtil.isNormalClass(clazz)) {
final Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.getParameterTypes().length == 1 && method.getName().startsWith("set")) {
// 检测包含标准的setXXX方法即视为标准的JavaBean
return true;
}
}
}
return false;
}
/**
* 创建动态Bean
*
* @param bean 普通Bean或Map
* @return {@link DynaBean}
* @since 3.0.7
*/
public static DynaBean createDynaBean(Object bean) {
return new DynaBean(bean);
}
/**
* 查找类型转换器 {@link PropertyEditor}
*
* @param type 需要转换的目标类型
* @return {@link PropertyEditor}
*/
public static PropertyEditor findEditor(Class> type) {
return PropertyEditorManager.findEditor(type);
}
public static boolean hasNull(Object bean, boolean ignoreError) {
final Field[] fields = ClassUtil.getDeclaredFields(bean.getClass());
Object fieldValue = null;
for (Field field : fields) {
field.setAccessible(true);
try {
fieldValue = field.get(bean);
} catch (Exception e) {
}
if (null == fieldValue) {
return true;
}
}
return false;
}
/**
* 获取{@link BeanDesc} Bean描述信息
*
* @param clazz Bean类
* @return {@link BeanDesc}
* @since 3.1.2
*/
public static BeanDesc getBeanDesc(Class> clazz) {
BeanDesc beanDesc = BeanDescCache.INSTANCE.getBeanDesc(clazz);
if (null == beanDesc) {
beanDesc = new BeanDesc(clazz);
BeanDescCache.INSTANCE.putBeanDesc(clazz, beanDesc);
}
return beanDesc;
}
// --------------------------------------------------------------------------------------------------------- PropertyDescriptor
/**
* 获得Bean字段描述数组
*
* @param clazz Bean类
* @return 字段描述数组
* @throws IntrospectionException 获取属性异常
*/
public static PropertyDescriptor[] getPropertyDescriptors(Class> clazz) throws IntrospectionException {
return Introspector.getBeanInfo(clazz).getPropertyDescriptors();
}
/**
* 获得字段名和字段描述Map,获得的结果会缓存在 {@link BeanInfoCache}中
*
* @param clazz Bean类
* @param ignoreCase 是否忽略大小写
* @return 字段名和字段描述Map
* @throws IntrospectionException 获取属性异常
*/
public static Map getPropertyDescriptorMap(Class> clazz, boolean ignoreCase) throws IntrospectionException {
Map map = BeanInfoCache.INSTANCE.getPropertyDescriptorMap(clazz, ignoreCase);
if (null == map) {
map = internalGetPropertyDescriptorMap(clazz, ignoreCase);
BeanInfoCache.INSTANCE.putPropertyDescriptorMap(clazz, map, ignoreCase);
}
return map;
}
/**
* 获得字段名和字段描述Map。内部使用,直接获取Bean类的PropertyDescriptor
*
* @param clazz Bean类
* @param ignoreCase 是否忽略大小写
* @return 字段名和字段描述Map
* @throws IntrospectionException 获取属性异常
*/
private static Map internalGetPropertyDescriptorMap(Class> clazz, boolean ignoreCase) throws IntrospectionException {
final PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(clazz);
final Map map = ignoreCase ? new CaseInsensitiveMap(propertyDescriptors.length, 1)
: new HashMap((int) (propertyDescriptors.length), 1);
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
map.put(propertyDescriptor.getName(), propertyDescriptor);
}
return map;
}
/**
* 获得Bean类属性描述,大小写敏感
*
* @param clazz Bean类
* @param fieldName 字段名
* @return PropertyDescriptor
* @throws IntrospectionException 获取属性异常
*/
public static PropertyDescriptor getPropertyDescriptor(Class> clazz, final String fieldName) throws IntrospectionException {
return getPropertyDescriptor(clazz, fieldName, false);
}
/**
* 获得Bean类属性描述
*
* @param clazz Bean类
* @param fieldName 字段名
* @param ignoreCase 是否忽略大小写
* @return PropertyDescriptor
* @throws IntrospectionException 获取属性异常
*/
public static PropertyDescriptor getPropertyDescriptor(Class> clazz, final String fieldName, boolean ignoreCase) throws IntrospectionException {
final Map map = getPropertyDescriptorMap(clazz, ignoreCase);
return (null == map) ? null : map.get(fieldName);
}
/**
* 获得字段值,通过反射直接获得字段值,并不调用getXXX方法
* 对象同样支持Map类型,fieldName即为key
*
* @param bean Bean对象
* @param fieldName 字段名
* @return 字段值
*/
public static Object getFieldValue(Object bean, String fieldName) {
if (null == bean || StrUtil.isBlank(fieldName)) {
return null;
}
if (bean instanceof Map) {
return ((Map, ?>) bean).get(fieldName);
} else if (bean instanceof List) {
return ((List>) bean).get(Integer.parseInt(fieldName));
} else if (bean instanceof Collection) {
return ((Collection>) bean).toArray()[Integer.parseInt(fieldName)];
} else if (ArrayUtil.isArray(bean)) {
return Array.get(bean, Integer.parseInt(fieldName));
} else {// 普通Bean对象
Field field;
try {
field = ClassUtil.getDeclaredField(bean.getClass(), fieldName);
if (null != field) {
field.setAccessible(true);
return field.get(bean);
}
} catch (Exception e) {
throw new UtilException(e);
}
}
return null;
}
/**
* 解析Bean中的属性值
*
* @param bean Bean对象,支持Map、List、Collection、Array
* @param expression 表达式,例如:person.friend[5].name
* @return Bean属性值
* @see BeanResolver#resolveBean(Object, String)
* @since 3.0.7
*/
public static Object getProperty(Object bean, String expression) {
return BeanResolver.resolveBean(bean, expression);
}
// --------------------------------------------------------------------------------------------- mapToBean
/**
* Map转换为Bean对象
*
* @param Bean类型
* @param map {@link Map}
* @param beanClass Bean Class
* @param isIgnoreError 是否忽略注入错误
* @return Bean
*/
public static T mapToBean(Map, ?> map, Class beanClass, boolean isIgnoreError) {
return fillBeanWithMap(map, ReflectUtil.newInstance(beanClass), isIgnoreError);
}
/**
* Map转换为Bean对象
* 忽略大小写
*
* @param Bean类型
* @param map Map
* @param beanClass Bean Class
* @param isIgnoreError 是否忽略注入错误
* @return Bean
*/
public static T mapToBeanIgnoreCase(Map, ?> map, Class beanClass, boolean isIgnoreError) {
return fillBeanWithMapIgnoreCase(map, ReflectUtil.newInstance(beanClass), isIgnoreError);
}
/**
* Map转换为Bean对象
*
* @param Bean类型
* @param map {@link Map}
* @param beanClass Bean Class
* @param copyOptions 转Bean选项
* @return Bean
*/
public static T mapToBean(Map, ?> map, Class beanClass, CopyOptions copyOptions) {
return fillBeanWithMap(map, ReflectUtil.newInstance(beanClass), copyOptions);
}
// --------------------------------------------------------------------------------------------- fillBeanWithMap
/**
* 使用Map填充Bean对象
*
* @param Bean类型
* @param map Map
* @param bean Bean
* @param isIgnoreError 是否忽略注入错误
* @return Bean
*/
public static T fillBeanWithMap(Map, ?> map, T bean, boolean isIgnoreError) {
return fillBeanWithMap(map, bean, false, isIgnoreError);
}
/**
* 使用Map填充Bean对象,可配置将下划线转换为驼峰
*
* @param Bean类型
* @param map Map
* @param bean Bean
* @param isToCamelCase 是否将下划线模式转换为驼峰模式
* @param isIgnoreError 是否忽略注入错误
* @return Bean
*/
public static T fillBeanWithMap(Map, ?> map, T bean, boolean isToCamelCase, boolean isIgnoreError) {
return fillBeanWithMap(map, bean, isToCamelCase, CopyOptions.create().setIgnoreError(isIgnoreError));
}
/**
* 使用Map填充Bean对象,忽略大小写
*
* @param Bean类型
* @param map Map
* @param bean Bean
* @param isIgnoreError 是否忽略注入错误
* @return Bean
*/
public static T fillBeanWithMapIgnoreCase(Map, ?> map, T bean, boolean isIgnoreError) {
return fillBeanWithMap(map, bean, CopyOptions.create().setIgnoreCase(true).setIgnoreError(isIgnoreError));
}
/**
* 使用Map填充Bean对象
*
* @param Bean类型
* @param map Map
* @param bean Bean
* @param copyOptions 属性复制选项 {@link CopyOptions}
* @return Bean
*/
public static T fillBeanWithMap(Map, ?> map, T bean, CopyOptions copyOptions) {
return fillBeanWithMap(map, bean, false, copyOptions);
}
/**
* 使用Map填充Bean对象
*
* @param Bean类型
* @param map Map
* @param bean Bean
* @param isToCamelCase 是否将Map中的下划线风格key转换为驼峰风格
* @param copyOptions 属性复制选项 {@link CopyOptions}
* @return Bean
* @since 3.3.1
*/
public static T fillBeanWithMap(Map, ?> map, T bean, boolean isToCamelCase, CopyOptions copyOptions) {
if(MapUtil.isEmpty(map)) {
return bean;
}
if(isToCamelCase) {
map = MapUtil.toCamelCaseMap(map);
}
return BeanCopier.create(map, bean, copyOptions).copy();
}
// --------------------------------------------------------------------------------------------- fillBean
/**
* ServletRequest 参数转Bean
*
* @param Bean类型
* @param beanClass Bean Class
* @param valueProvider 值提供者
* @param copyOptions 拷贝选项,见 {@link CopyOptions}
* @return Bean
*/
public static T toBean(Class beanClass, ValueProvider valueProvider, CopyOptions copyOptions) {
return fillBean(ReflectUtil.newInstance(beanClass), valueProvider, copyOptions);
}
/**
* 填充Bean的核心方法
*
* @param Bean类型
* @param bean Bean
* @param valueProvider 值提供者
* @param copyOptions 拷贝选项,见 {@link CopyOptions}
* @return Bean
*/
public static T fillBean(T bean, ValueProvider valueProvider, CopyOptions copyOptions) {
if (null == valueProvider) {
return bean;
}
return BeanCopier.create(valueProvider, bean, copyOptions).copy();
}
// --------------------------------------------------------------------------------------------- beanToMap
/**
* 对象转Map,不进行驼峰转下划线,不忽略值为空的字段
*
* @param bean bean对象
* @return Map
*/
public static Map beanToMap(Object bean) {
return beanToMap(bean, false, false);
}
/**
* 对象转Map
*
* @param bean bean对象
* @param isToUnderlineCase 是否转换为下划线模式
* @param ignoreNullValue 是否忽略值为空的字段
* @return Map
*/
public static Map beanToMap(Object bean, boolean isToUnderlineCase, boolean ignoreNullValue) {
return beanToMap(bean, new HashMap(), isToUnderlineCase, ignoreNullValue);
}
/**
* 对象转Map
*
* @param bean bean对象
* @param targetMap 目标的Map
* @param isToUnderlineCase 是否转换为下划线模式
* @param ignoreNullValue 是否忽略值为空的字段
* @return Map
* @since 3.2.3
*/
public static Map beanToMap(Object bean, Map targetMap, boolean isToUnderlineCase, boolean ignoreNullValue) {
if (bean == null) {
return null;
}
final Collection props = BeanUtil.getBeanDesc(bean.getClass()).getProps();
String key;
Method getter;
Object value;
for (PropDesc prop : props) {
key = prop.getFieldName();
// 过滤class属性
// 得到property对应的getter方法
getter = prop.getGetter();
if (null != getter) {
// 只读取有getter方法的属性
try {
value = getter.invoke(bean);
} catch (Exception ignore) {
continue;
}
if (false == ignoreNullValue || (null != value && false == value.equals(bean))) {
targetMap.put(isToUnderlineCase ? StrUtil.toUnderlineCase(key) : key, value);
}
}
}
return targetMap;
}
// --------------------------------------------------------------------------------------------- copyProperties
/**
* 复制Bean对象属性
*
* @param source 源Bean对象
* @param target 目标Bean对象
*/
public static void copyProperties(Object source, Object target) {
copyProperties(source, target, CopyOptions.create());
}
/**
* 复制Bean对象属性
* 限制类用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
*
* @param source 源Bean对象
* @param target 目标Bean对象
* @param ignoreProperties 不拷贝的的属性列表
*/
public static void copyProperties(Object source, Object target, String... ignoreProperties) {
copyProperties(source, target, CopyOptions.create().setIgnoreProperties(ignoreProperties));
}
/**
* 复制Bean对象属性
* 限制类用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
*
* @param source 源Bean对象
* @param target 目标Bean对象
* @param copyOptions 拷贝选项,见 {@link CopyOptions}
*/
public static void copyProperties(final Object source, Object target, CopyOptions copyOptions) {
copyProperties(source, target, false, copyOptions);
}
/**
* 复制Bean对象属性
* 限制类用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将CopyOptions.editable设置为父类
*
* @param source 源Bean对象
* @param target 目标Bean对象
* @param ignoreCase 是否忽略大小写
* @param copyOptions 拷贝选项,见 {@link CopyOptions}
*/
public static void copyProperties(final Object source, Object target, boolean ignoreCase, CopyOptions copyOptions) {
if (null == copyOptions) {
copyOptions = new CopyOptions();
}
BeanCopier.create(source, target, copyOptions).copy();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy