mirror of
https://gitee.com/dromara/hutool.git
synced 2025-10-07 07:04:40 +08:00
Compare commits
4 Commits
8ef76cc56b
...
2f6662b4aa
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2f6662b4aa | ||
![]() |
f9ad66c934 | ||
![]() |
745ebacb4d | ||
![]() |
944e914454 |
@@ -69,7 +69,7 @@ public class BeanUtil {
|
||||
* @since 3.0.7
|
||||
*/
|
||||
public static DynaBean createDynaBean(final Object bean) {
|
||||
return new DynaBean(bean);
|
||||
return DynaBean.of(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,6 +183,7 @@ public class BeanUtil {
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region ----- beanPath
|
||||
/**
|
||||
* 获取Bean中的属性值
|
||||
*
|
||||
@@ -200,8 +201,7 @@ public class BeanUtil {
|
||||
}
|
||||
|
||||
// 先尝试直接获取属性
|
||||
if (bean instanceof Map) {
|
||||
final Map<?, ?> map = (Map<?, ?>) bean;
|
||||
if (bean instanceof Map<?, ?> map) {
|
||||
if (map.containsKey(expression)) {
|
||||
return (T) map.get(expression);
|
||||
}
|
||||
@@ -222,6 +222,7 @@ public class BeanUtil {
|
||||
public static void setProperty(final Object bean, final String expression, final Object value) {
|
||||
BeanPath.of(expression).setValue(bean, value);
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region ----- toBean
|
||||
|
||||
@@ -563,11 +564,11 @@ public class BeanUtil {
|
||||
|
||||
/**
|
||||
* 给定的Bean的类名是否匹配指定类名字符串<br>
|
||||
* 如果isSimple为{@code true},则只匹配类名而忽略包名,例如:cn.hutool.v7.TestEntity只匹配TestEntity<br>
|
||||
* 如果isSimple为{@code true},则只匹配类名而忽略包名,例如:cn.hutool.v7.TestEntity只匹配TestEntity或者testEntity<br>
|
||||
* 如果isSimple为{@code false},则匹配包括包名的全类名,例如:cn.hutool.v7.TestEntity匹配cn.hutool.v7.TestEntity
|
||||
*
|
||||
* @param bean Bean
|
||||
* @param beanClassName Bean的类名
|
||||
* @param beanClassName Bean的类名,首字母忽略大小写
|
||||
* @param isSimple 是否只匹配类名而忽略包名,true表示忽略包名
|
||||
* @return 是否匹配
|
||||
* @since 4.0.6
|
||||
@@ -579,31 +580,7 @@ public class BeanUtil {
|
||||
return ClassUtil.getClassName(bean, isSimple).equals(isSimple ? StrUtil.upperFirst(beanClassName) : beanClassName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑Bean的字段,static字段不会处理<br>
|
||||
* 例如需要对指定的字段做判空操作、null转""操作等等。
|
||||
*
|
||||
* @param bean bean
|
||||
* @param editor 编辑器函数
|
||||
* @param <T> 被编辑的Bean类型
|
||||
* @return bean
|
||||
* @since 5.6.4
|
||||
*/
|
||||
public static <T> T edit(final T bean, final UnaryOperator<Field> editor) {
|
||||
if (bean == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Field[] fields = FieldUtil.getFields(bean.getClass());
|
||||
for (final Field field : fields) {
|
||||
if (ModifierUtil.isStatic(field)) {
|
||||
continue;
|
||||
}
|
||||
editor.apply(field);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
// region ----- edit
|
||||
/**
|
||||
* 把Bean里面的String属性做trim操作。此方法直接对传入的Bean做修改。
|
||||
* <p>
|
||||
@@ -635,6 +612,33 @@ public class BeanUtil {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑Bean的字段,static字段不会处理<br>
|
||||
* 例如需要对指定的字段做判空操作、null转""操作等等。
|
||||
*
|
||||
* @param bean bean
|
||||
* @param editor 编辑器函数
|
||||
* @param <T> 被编辑的Bean类型
|
||||
* @return bean
|
||||
* @since 5.6.4
|
||||
*/
|
||||
public static <T> T edit(final T bean, final UnaryOperator<Field> editor) {
|
||||
if (bean == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Field[] fields = FieldUtil.getFields(bean.getClass());
|
||||
for (final Field field : fields) {
|
||||
if (ModifierUtil.isStatic(field)) {
|
||||
continue;
|
||||
}
|
||||
editor.apply(field);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region ----- check
|
||||
/**
|
||||
* 判断Bean是否为空对象,空对象表示本身为{@code null}或者所有属性都为{@code null}<br>
|
||||
* 此方法不判断static属性
|
||||
@@ -727,7 +731,30 @@ public class BeanUtil {
|
||||
return hasSetter(clazz) || hasPublicField(clazz);
|
||||
}
|
||||
|
||||
// region ----- hasXXX
|
||||
/**
|
||||
* 检查Bean<br>
|
||||
* 遍历Bean的字段并断言检查字段,当某个字段:
|
||||
* 断言为{@code true} 时,返回{@code true}并不再检查后续字段;<br>
|
||||
* 断言为{@code false}时,继续检查后续字段
|
||||
*
|
||||
* @param bean Bean
|
||||
* @param predicate 断言
|
||||
* @return 是否触发断言为真
|
||||
*/
|
||||
public static boolean checkBean(final Object bean, final Predicate<Field> predicate) {
|
||||
if (null == bean) {
|
||||
return true;
|
||||
}
|
||||
for (final Field field : FieldUtil.getFields(bean.getClass())) {
|
||||
if (ModifierUtil.isStatic(field)) {
|
||||
continue;
|
||||
}
|
||||
if (predicate.test(field)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有Setter方法<br>
|
||||
@@ -826,31 +853,6 @@ public class BeanUtil {
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 检查Bean<br>
|
||||
* 遍历Bean的字段并断言检查字段,当某个字段:
|
||||
* 断言为{@code true} 时,返回{@code true}并不再检查后续字段;<br>
|
||||
* 断言为{@code false}时,继续检查后续字段
|
||||
*
|
||||
* @param bean Bean
|
||||
* @param predicate 断言
|
||||
* @return 是否触发断言为真
|
||||
*/
|
||||
public static boolean checkBean(final Object bean, final Predicate<Field> predicate) {
|
||||
if (null == bean) {
|
||||
return true;
|
||||
}
|
||||
for (final Field field : FieldUtil.getFields(bean.getClass())) {
|
||||
if (ModifierUtil.isStatic(field)) {
|
||||
continue;
|
||||
}
|
||||
if (predicate.test(field)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Getter或Setter方法名对应的字段名称,规则如下:
|
||||
* <ul>
|
||||
|
@@ -26,6 +26,7 @@ import cn.hutool.v7.core.reflect.ClassUtil;
|
||||
import cn.hutool.v7.core.reflect.ConstructorUtil;
|
||||
import cn.hutool.v7.core.reflect.method.MethodUtil;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -39,6 +40,7 @@ import java.util.Map;
|
||||
* @since 3.0.7
|
||||
*/
|
||||
public class DynaBean implements Cloneable, Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
|
@@ -120,7 +120,7 @@ public class BeanPath<T> implements Iterator<BeanPath<T>> {
|
||||
}
|
||||
// 每一个边界符之前的表达式是一个完整的KEY,开始处理KEY
|
||||
}
|
||||
if (builder.length() > 0) {
|
||||
if (!builder.isEmpty()) {
|
||||
this.node = NodeFactory.createNode(builder.toString());
|
||||
// 如果以[结束,表示后续还有表达式,需保留'[',如name[0]
|
||||
this.child = StrUtil.nullIfEmpty(expression.substring(c == CharUtil.BRACKET_START ? i : i + 1));
|
||||
|
@@ -187,7 +187,8 @@ public class AnnotationUtilTest {
|
||||
|
||||
@Test
|
||||
public void testIsInherited() {
|
||||
Assertions.assertFalse(AnnotationUtil.isInherited(AnnotationForTest.class));
|
||||
Assertions.assertTrue(AnnotationUtil.isInherited(AnnotationForTest.class));
|
||||
Assertions.assertFalse(AnnotationUtil.isInherited(MetaAnnotationForTest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -16,11 +16,6 @@
|
||||
|
||||
package cn.hutool.v7.core.bean;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import cn.hutool.v7.core.annotation.Alias;
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import cn.hutool.v7.core.bean.copier.CopyOptions;
|
||||
@@ -34,16 +29,26 @@ import cn.hutool.v7.core.map.MapUtil;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.core.thread.ThreadUtil;
|
||||
import cn.hutool.v7.core.util.ObjUtil;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.beans.PropertyEditor;
|
||||
import java.beans.PropertyEditorManager;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Type;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
@@ -71,17 +76,15 @@ public class BeanUtilTest {
|
||||
|
||||
@Test
|
||||
public void fillBeanTest() {
|
||||
final Person person = BeanUtil.fillBean(new Person(), new ValueProvider<String>() {
|
||||
final Person person = BeanUtil.fillBean(new Person(), new ValueProvider<>() {
|
||||
|
||||
@Override
|
||||
public Object value(final String key, final Type valueType) {
|
||||
switch (key) {
|
||||
case "name":
|
||||
return "张三";
|
||||
case "age":
|
||||
return 18;
|
||||
}
|
||||
return null;
|
||||
return switch (key) {
|
||||
case "name" -> "张三";
|
||||
case "age" -> 18;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -686,6 +689,7 @@ public class BeanUtilTest {
|
||||
|
||||
@Data
|
||||
public static class HllFoodEntity implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String bookId;
|
||||
@@ -791,6 +795,7 @@ public class BeanUtilTest {
|
||||
|
||||
@Data
|
||||
public static class PrivilegeIClassification implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String id;
|
||||
@@ -891,6 +896,7 @@ public class BeanUtilTest {
|
||||
|
||||
@Data
|
||||
public static class Student implements Serializable{
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
String name;
|
||||
@@ -987,11 +993,13 @@ public class BeanUtilTest {
|
||||
final Map<String, String> filedMap= new HashMap<>();
|
||||
filedMap.put("name", "sourceId");
|
||||
copyOptions.setFieldMapping(filedMap);
|
||||
final TestPojo pojo = BeanUtil.fillBean(new TestPojo(), new ValueProvider<String>() {
|
||||
final TestPojo pojo = BeanUtil.fillBean(new TestPojo(), new ValueProvider<>() {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
|
||||
{
|
||||
map.put("sourceId", "123");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object value(final String key, final Type valueType) {
|
||||
return map.get(key);
|
||||
@@ -1011,4 +1019,138 @@ public class BeanUtilTest {
|
||||
final boolean b = BeanUtil.hasGetter(Object.class);
|
||||
assertFalse(b);
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkBean_withNullBean_shouldReturnTrue() {
|
||||
Predicate<Field> predicate = field -> true;
|
||||
assertTrue(BeanUtil.checkBean(null, predicate));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkBean_withNoMatchingFields_shouldReturnFalse() {
|
||||
Person bean = new Person();
|
||||
Predicate<Field> predicate = field -> false;
|
||||
assertFalse(BeanUtil.checkBean(bean, predicate));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkBean_withMatchingField_shouldReturnTrue() {
|
||||
Person bean = new Person();
|
||||
Predicate<Field> predicate = field -> "name".equals(field.getName());
|
||||
assertTrue(BeanUtil.checkBean(bean, predicate));
|
||||
|
||||
predicate = field -> "age".equals(field.getName());
|
||||
assertTrue(BeanUtil.checkBean(bean, predicate));
|
||||
}
|
||||
|
||||
@Test
|
||||
void findEditor_shouldReturnEditorForRegisteredType() {
|
||||
// Given
|
||||
Class<?> type = Integer.class;
|
||||
// Register editor for Integer if not already registered (though usually it's pre-registered)
|
||||
PropertyEditor expectedEditor = PropertyEditorManager.findEditor(type);
|
||||
|
||||
// When
|
||||
PropertyEditor actualEditor = BeanUtil.findEditor(type);
|
||||
|
||||
// Then
|
||||
assertNotNull(actualEditor);
|
||||
assertEquals(expectedEditor.getClass(), actualEditor.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findEditor_shouldReturnNullForUnregisteredType() {
|
||||
// Given
|
||||
class CustomUnregisteredType {}
|
||||
Class<?> type = CustomUnregisteredType.class;
|
||||
|
||||
// When
|
||||
PropertyEditor editor = BeanUtil.findEditor(type);
|
||||
|
||||
// Then
|
||||
assertNull(editor);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findEditor_shouldHandlePrimitiveTypes() {
|
||||
// Given
|
||||
Class<?> type = int.class;
|
||||
|
||||
// When
|
||||
PropertyEditor editor = BeanUtil.findEditor(type);
|
||||
|
||||
// Then
|
||||
assertNotNull(editor);
|
||||
}
|
||||
|
||||
@Test
|
||||
void descForEach_shouldProcessAllProperties() {
|
||||
List<String> processedProperties = new ArrayList<>();
|
||||
|
||||
// 执行
|
||||
BeanUtil.descForEach(Person.class, propDesc -> processedProperties.add(propDesc.getFieldName()));
|
||||
|
||||
// 验证
|
||||
assertEquals(3, processedProperties.size());
|
||||
assertTrue(processedProperties.contains("name"));
|
||||
assertTrue(processedProperties.contains("age"));
|
||||
assertTrue(processedProperties.contains("openid"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsMatchName_SimpleMatch() {
|
||||
String testObj = "test";
|
||||
assertTrue(BeanUtil.isMatchName(testObj, "String", true));
|
||||
assertTrue(BeanUtil.isMatchName(testObj, "string", true));
|
||||
|
||||
final Person person = new Person();
|
||||
assertTrue(BeanUtil.isMatchName(person, "Person", true));
|
||||
assertTrue(BeanUtil.isMatchName(person, "person", true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsMatchName_FullMatch() {
|
||||
Integer testObj = 123;
|
||||
assertTrue(BeanUtil.isMatchName(testObj, "java.lang.Integer", false));
|
||||
assertFalse(BeanUtil.isMatchName(testObj, "Integer", false));
|
||||
|
||||
final Person person = new Person();
|
||||
assertTrue(BeanUtil.isMatchName(person, "cn.hutool.v7.core.bean.BeanUtilTest$Person", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPropertyDescriptor_ExistingField() {
|
||||
PropertyDescriptor pd = BeanUtil.getPropertyDescriptor(Person.class, "name");
|
||||
assertNotNull(pd);
|
||||
assertEquals("name", pd.getName());
|
||||
assertEquals(String.class, pd.getPropertyType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPropertyDescriptor_NonExistingField() {
|
||||
final PropertyDescriptor nonExistingField = BeanUtil.getPropertyDescriptor(Person.class, "nonExistingField");
|
||||
assertNull(nonExistingField);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBeanToMap_IgnoreNullValue() {
|
||||
Person person = new Person();
|
||||
person.setName(null);
|
||||
person.setAge(25);
|
||||
|
||||
// 忽略空值
|
||||
Map<String, Object> map = BeanUtil.beanToMap(person, false, true);
|
||||
assertEquals(1, map.size());
|
||||
assertEquals(25, map.get("age"));
|
||||
assertFalse(map.containsKey("name"));
|
||||
|
||||
// 不忽略空值
|
||||
map = BeanUtil.beanToMap(person, false, false);
|
||||
assertEquals(3, map.size());
|
||||
assertTrue(map.containsKey("name"));
|
||||
assertNull(map.get("name"));
|
||||
assertEquals(25, map.get("age"));
|
||||
assertTrue(map.containsKey("openid"));
|
||||
assertNull(map.get("openid"));
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,9 @@ import lombok.Data;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* {@link DynaBean}单元测试
|
||||
*
|
||||
@@ -27,6 +30,21 @@ import org.junit.jupiter.api.Test;
|
||||
*/
|
||||
public class DynaBeanTest {
|
||||
|
||||
@Test
|
||||
void createDynaBean_withNullBean_shouldThrowException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> BeanUtil.createDynaBean(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDynaBean_withPlainObject_shouldReturnDynaBean() {
|
||||
BeanUtilTest.Person bean = new BeanUtilTest.Person();
|
||||
DynaBean dynaBean = BeanUtil.createDynaBean(bean);
|
||||
|
||||
assertNotNull(dynaBean);
|
||||
assertEquals(bean, dynaBean.getBean());
|
||||
assertEquals(BeanUtilTest.Person.class, dynaBean.getBeanClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanTest() {
|
||||
final User user = new User();
|
||||
|
Reference in New Issue
Block a user