tClass, String... ignoreProperties) {
- T target = ReflectUtil.newInstanceIfPossible(tClass);
- copyProperties(source, target, CopyOptions.create().setIgnoreProperties(ignoreProperties));
- return target;
- }
-
- /**
- * 复制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对象属性
- *
- * @param source 源Bean对象
- * @param target 目标Bean对象
- * @param ignoreCase 是否忽略大小写
- */
- public static void copyProperties(Object source, Object target, boolean ignoreCase) {
- BeanCopier.create(source, target, CopyOptions.create().setIgnoreCase(ignoreCase)).copy();
- }
-
- /**
- * 复制Bean对象属性
- * 限制类用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
- *
- * @param source 源Bean对象
- * @param target 目标Bean对象
- * @param copyOptions 拷贝选项,见 {@link CopyOptions}
- */
- public static void copyProperties(Object source, Object target, CopyOptions copyOptions) {
- if (null == copyOptions) {
- copyOptions = new CopyOptions();
- }
- BeanCopier.create(source, target, copyOptions).copy();
- }
-
- /**
- * 给定的Bean的类名是否匹配指定类名字符串
- * 如果isSimple为{@code false},则只匹配类名而忽略包名,例如:cn.hutool.TestEntity只匹配TestEntity
- * 如果isSimple为{@code true},则匹配包括包名的全类名,例如:cn.hutool.TestEntity匹配cn.hutool.TestEntity
- *
- * @param bean Bean
- * @param beanClassName Bean的类名
- * @param isSimple 是否只匹配类名而忽略包名,true表示忽略包名
- * @return 是否匹配
- * @since 4.0.6
- */
- public static boolean isMatchName(Object bean, String beanClassName, boolean isSimple) {
- return ClassUtil.getClassName(bean, isSimple).equals(isSimple ? StrUtil.upperFirst(beanClassName) : beanClassName);
- }
-
- /**
- * 把Bean里面的String属性做trim操作。此方法直接对传入的Bean做修改。
- *
- * 通常bean直接用来绑定页面的input,用户的输入可能首尾存在空格,通常保存数据库前需要把首尾空格去掉
- *
- * @param Bean类型
- * @param bean Bean对象
- * @param ignoreFields 不需要trim的Field名称列表(不区分大小写)
- * @return 处理后的Bean对象
- */
- public static T trimStrFields(T bean, String... ignoreFields) {
- if (bean == null) {
- return null;
- }
-
- final Field[] fields = ReflectUtil.getFields(bean.getClass());
- for (Field field : fields) {
- if (ModifierUtil.isStatic(field)) {
- continue;
- }
- if (ignoreFields != null && ArrayUtil.containsIgnoreCase(ignoreFields, field.getName())) {
- // 不处理忽略的Fields
- continue;
- }
- if (String.class.equals(field.getType())) {
- // 只有String的Field才处理
- final String val = (String) ReflectUtil.getFieldValue(bean, field);
- if (null != val) {
- final String trimVal = StrUtil.trim(val);
- if (false == val.equals(trimVal)) {
- // Field Value不为null,且首尾有空格才处理
- ReflectUtil.setFieldValue(bean, field, trimVal);
- }
- }
- }
- }
-
- return bean;
- }
-
- /**
- * 判断Bean是否为非空对象,非空对象表示本身不为{@code null}或者含有非{@code null}属性的对象
- *
- * @param bean Bean对象
- * @param ignoreFiledNames 忽略检查的字段名
- * @return 是否为空,{@code true} - 空 / {@code false} - 非空
- * @since 5.0.7
- */
- public static boolean isNotEmpty(Object bean, String... ignoreFiledNames) {
- return false == isEmpty(bean, ignoreFiledNames);
- }
-
- /**
- * 判断Bean是否为空对象,空对象表示本身为{@code null}或者所有属性都为{@code null}
- * 此方法不判断static属性
- *
- * @param bean Bean对象
- * @param ignoreFiledNames 忽略检查的字段名
- * @return 是否为空,{@code true} - 空 / {@code false} - 非空
- * @since 4.1.10
- */
- public static boolean isEmpty(Object bean, String... ignoreFiledNames) {
- if (null != bean) {
- for (Field field : ReflectUtil.getFields(bean.getClass())) {
- if (ModifierUtil.isStatic(field)) {
- continue;
- }
- if ((false == ArrayUtil.contains(ignoreFiledNames, field.getName()))
- && null != ReflectUtil.getFieldValue(bean, field)) {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * 判断Bean是否包含值为{@code null}的属性
- * 对象本身为{@code null}也返回true
- *
- * @param bean Bean对象
- * @param ignoreFiledNames 忽略检查的字段名
- * @return 是否包含值为null的属性,{@code true} - 包含 / {@code false} - 不包含
- * @since 4.1.10
- */
- public static boolean hasNullField(Object bean, String... ignoreFiledNames) {
- if (null == bean) {
- return true;
- }
- for (Field field : ReflectUtil.getFields(bean.getClass())) {
- if (ModifierUtil.isStatic(field)) {
- continue;
- }
- if ((false == ArrayUtil.contains(ignoreFiledNames, field.getName()))
- && null == ReflectUtil.getFieldValue(bean, field)) {
- return true;
- }
- }
- return false;
- }
+ /**
+ * 判断是否为可读的Bean对象,判定方法是:
+ *
+ *
+ * 1、是否存在只有无参数的getXXX方法或者isXXX方法
+ * 2、是否存在public类型的字段
+ *
+ *
+ * @param clazz 待测试类
+ * @return 是否为可读的Bean对象
+ * @see #hasGetter(Class)
+ * @see #hasPublicField(Class)
+ */
+ public static boolean isReadableBean(Class> clazz) {
+ return hasGetter(clazz) || hasPublicField(clazz);
+ }
+
+ /**
+ * 判断是否为Bean对象,判定方法是:
+ *
+ *
+ * 1、是否存在只有一个参数的setXXX方法
+ * 2、是否存在public类型的字段
+ *
+ *
+ * @param clazz 待测试类
+ * @return 是否为Bean对象
+ * @see #hasSetter(Class)
+ * @see #hasPublicField(Class)
+ */
+ public static boolean isBean(Class> clazz) {
+ return hasSetter(clazz) || hasPublicField(clazz);
+ }
+
+ /**
+ * 判断是否有Setter方法
+ * 判定方法是是否存在只有一个参数的setXXX方法
+ *
+ * @param clazz 待测试类
+ * @return 是否为Bean对象
+ * @since 4.2.2
+ */
+ public static boolean hasSetter(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对象
+ * 判定方法是是否存在只有一个参数的setXXX方法
+ *
+ * @param clazz 待测试类
+ * @return 是否为Bean对象
+ * @since 4.2.2
+ */
+ public static boolean hasGetter(Class> clazz) {
+ if (ClassUtil.isNormalClass(clazz)) {
+ for (Method method : clazz.getMethods()) {
+ if (method.getParameterTypes().length == 0) {
+ if (method.getName().startsWith("get") || method.getName().startsWith("is")) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 指定类中是否有public类型字段(static字段除外)
+ *
+ * @param clazz 待测试类
+ * @return 是否有public类型字段
+ * @since 5.1.0
+ */
+ public static boolean hasPublicField(Class> clazz) {
+ if (ClassUtil.isNormalClass(clazz)) {
+ for (Field field : clazz.getFields()) {
+ if (ModifierUtil.isPublic(field) && false == ModifierUtil.isStatic(field)) {
+ //非static的public字段
+ 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);
+ }
+
+ /**
+ * 获取{@link BeanDesc} Bean描述信息
+ *
+ * @param clazz Bean类
+ * @return {@link BeanDesc}
+ * @since 3.1.2
+ */
+ public static BeanDesc getBeanDesc(Class> clazz) {
+ return BeanDescCache.INSTANCE.getBeanDesc(clazz, () -> new BeanDesc(clazz));
+ }
+
+ /**
+ * 遍历Bean的属性
+ *
+ * @param clazz Bean类
+ * @param action 每个元素的处理类
+ * @since 5.4.2
+ */
+ public static void descForEach(Class> clazz, Consumer super PropDesc> action) {
+ getBeanDesc(clazz).getProps().forEach(action);
+ }
+
+ // --------------------------------------------------------------------------------------------------------- PropertyDescriptor
+
+ /**
+ * 获得Bean字段描述数组
+ *
+ * @param clazz Bean类
+ * @return 字段描述数组
+ * @throws BeanException 获取属性异常
+ */
+ public static PropertyDescriptor[] getPropertyDescriptors(Class> clazz) throws BeanException {
+ BeanInfo beanInfo;
+ try {
+ beanInfo = Introspector.getBeanInfo(clazz);
+ } catch (IntrospectionException e) {
+ throw new BeanException(e);
+ }
+ return ArrayUtil.filter(beanInfo.getPropertyDescriptors(), (Filter) t -> {
+ // 过滤掉getClass方法
+ return false == "class".equals(t.getName());
+ });
+ }
+
+ /**
+ * 获得字段名和字段描述Map,获得的结果会缓存在 {@link BeanInfoCache}中
+ *
+ * @param clazz Bean类
+ * @param ignoreCase 是否忽略大小写
+ * @return 字段名和字段描述Map
+ * @throws BeanException 获取属性异常
+ */
+ public static Map getPropertyDescriptorMap(Class> clazz, boolean ignoreCase) throws BeanException {
+ return BeanInfoCache.INSTANCE.getPropertyDescriptorMap(clazz, ignoreCase, () -> internalGetPropertyDescriptorMap(clazz, ignoreCase));
+ }
+
+ /**
+ * 获得字段名和字段描述Map。内部使用,直接获取Bean类的PropertyDescriptor
+ *
+ * @param clazz Bean类
+ * @param ignoreCase 是否忽略大小写
+ * @return 字段名和字段描述Map
+ * @throws BeanException 获取属性异常
+ */
+ private static Map internalGetPropertyDescriptorMap(Class> clazz, boolean ignoreCase) throws BeanException {
+ 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 BeanException 获取属性异常
+ */
+ public static PropertyDescriptor getPropertyDescriptor(Class> clazz, final String fieldName) throws BeanException {
+ return getPropertyDescriptor(clazz, fieldName, false);
+ }
+
+ /**
+ * 获得Bean类属性描述
+ *
+ * @param clazz Bean类
+ * @param fieldName 字段名
+ * @param ignoreCase 是否忽略大小写
+ * @return PropertyDescriptor
+ * @throws BeanException 获取属性异常
+ */
+ public static PropertyDescriptor getPropertyDescriptor(Class> clazz, final String fieldName, boolean ignoreCase) throws BeanException {
+ final Map map = getPropertyDescriptorMap(clazz, ignoreCase);
+ return (null == map) ? null : map.get(fieldName);
+ }
+
+ /**
+ * 获得字段值,通过反射直接获得字段值,并不调用getXXX方法
+ * 对象同样支持Map类型,fieldNameOrIndex即为key
+ *
+ * @param bean Bean对象
+ * @param fieldNameOrIndex 字段名或序号,序号支持负数
+ * @return 字段值
+ */
+ public static Object getFieldValue(Object bean, String fieldNameOrIndex) {
+ if (null == bean || null == fieldNameOrIndex) {
+ return null;
+ }
+
+ if (bean instanceof Map) {
+ return ((Map, ?>) bean).get(fieldNameOrIndex);
+ }
+
+ // 是否是索引
+ boolean isIndex = fieldNameOrIndex.matches("\\d+");
+
+ if (isIndex) {
+ if (bean instanceof Collection) {
+ return CollUtil.get((Collection>) bean, Integer.parseInt(fieldNameOrIndex));
+ }
+ if (ArrayUtil.isArray(bean)) {
+ return ArrayUtil.get(bean, Integer.parseInt(fieldNameOrIndex));
+ }
+ } else {
+ // 不是索引 则将集合每个对象的属性拿出来封装成新的集合
+ if (bean instanceof Collection) {
+ Collection> collection = (Collection>) bean;
+ List