diff --git a/stream-core/src/main/java/org/dromara/streamquery/stream/core/bean/BeanHelper.java b/stream-core/src/main/java/org/dromara/streamquery/stream/core/bean/BeanHelper.java index c85454938e8e781fdb14a455e2ca16891d91b11f..c08ef21492dc934da7fa0a06df191667f843262d 100644 --- a/stream-core/src/main/java/org/dromara/streamquery/stream/core/bean/BeanHelper.java +++ b/stream-core/src/main/java/org/dromara/streamquery/stream/core/bean/BeanHelper.java @@ -22,10 +22,16 @@ import org.dromara.streamquery.stream.core.lambda.function.SerFunc; import org.dromara.streamquery.stream.core.optional.Opp; import org.dromara.streamquery.stream.core.reflect.ReflectHelper; +import java.beans.Introspector; import java.io.Serializable; +import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BiFunction; @@ -178,4 +184,76 @@ public class BeanHelper { } return copyProperties(source, target); } + + /** + * 验证一个类的属性是否符合Java Bean规范,即每个属性都有相应的getter和setter方法。 + * getter方法应以 "get" 或 "is" 前缀开头,setter方法应以 "set" 前缀开头。 + * 此方法只验证那些声明在当前类中的方法,不包括从父类继承的方法。 + * + * @param beanClass 要验证的类的Class对象 + * @return 如果类的所有属性都符合Java Bean规范则返回true,否则返回false + * @throws SecurityException 如果安全管理器不允许访问类的方法信息 + */ + public static boolean validatePropertyNames(Class beanClass) { + Method[] methods = beanClass.getDeclaredMethods(); + Set properties = new HashSet<>(); + + for (Method method : methods) { + String methodName = method.getName(); + if (methodName.startsWith(GETTER_PREFIX) && method.getParameterCount() == 0 && method.getReturnType() != void.class) { + String property = Introspector.decapitalize(methodName.substring(3)); + properties.add(property); + } else if (methodName.startsWith(GETTER_BOOLEAN_PREFIX) && method.getParameterCount() == 0 && method.getReturnType() == boolean.class) { + String property = Introspector.decapitalize(methodName.substring(2)); + properties.add(property); + } else if (methodName.startsWith(SETTER_PREFIX) && method.getParameterCount() == 1) { + String property = Introspector.decapitalize(methodName.substring(3)); + properties.add(property); + } + } + + for (String property : properties) { + String capitalizedProperty = capitalize(property); + String getterMethod = GETTER_PREFIX + capitalizedProperty; + String booleanGetterMethod = GETTER_BOOLEAN_PREFIX + capitalizedProperty; + String setterMethod = SETTER_PREFIX + capitalizedProperty; + + Opp getterOpp = Opp.ofTry(() -> beanClass.getDeclaredMethod(getterMethod), NoSuchMethodException.class); + + if (!getterOpp.isPresent()) { + getterOpp = Opp.ofTry(() -> { + Method method = beanClass.getDeclaredMethod(booleanGetterMethod); + if (method.getReturnType() != boolean.class) { + throw new NoSuchMethodException("Expected boolean return type for method: " + booleanGetterMethod); + } + return method; + }, NoSuchMethodException.class); + } + + if (!getterOpp.isPresent()) { + return false; + } + + Method getter = getterOpp.get(); + + Opp setterOpp = Opp.ofTry(() -> beanClass.getDeclaredMethod(setterMethod, getter.getReturnType()), NoSuchMethodException.class); + + if (!setterOpp.isPresent()) { + return false; + } + } + + return true; + } + + private static String capitalize(String str) { + if (str == null || str.isEmpty()) { + return str; + } + char[] chars = str.toCharArray(); + chars[0] = Character.toUpperCase(chars[0]); + return new String(chars); + } + + } diff --git a/stream-core/src/test/java/org/dromara/streamquery/stream/core/bean/BeanHelperTest.java b/stream-core/src/test/java/org/dromara/streamquery/stream/core/bean/BeanHelperTest.java index 788942645fb33fb4a8aeea9f8d5a4febeabfcacc..5b41c47ae5c994b19e9490465fd3a9b220fd158c 100644 --- a/stream-core/src/test/java/org/dromara/streamquery/stream/core/bean/BeanHelperTest.java +++ b/stream-core/src/test/java/org/dromara/streamquery/stream/core/bean/BeanHelperTest.java @@ -16,7 +16,10 @@ */ package org.dromara.streamquery.stream.core.bean; +import java.util.List; + import lombok.Data; +import lombok.Getter; import lombok.experimental.Accessors; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -63,4 +66,58 @@ class BeanHelperTest { Person target = BeanHelper.copyProperties(source, Person.class); Assertions.assertEquals(source.getName(), target.getName()); } + + @Test + void testBeanPropertiesValidation() { + Assertions.assertTrue(BeanHelper.validatePropertyNames(ValidBean.class)); + + Assertions.assertFalse(BeanHelper.validatePropertyNames(BeanWithMissingSetter.class)); + + Assertions.assertFalse(BeanHelper.validatePropertyNames(BeanWithMissingGetter.class)); + + Assertions.assertTrue(BeanHelper.validatePropertyNames(BeanWithBooleanIsGetter.class)); + } + + public static class ValidBean { + private String name; + private boolean active; + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public boolean isActive() { + return active; + } + public void setActive(boolean active) { + this.active = active; + } + } + + public static class BeanWithMissingSetter { + private String value; + public String getValue() { + return value; + } + // Setter is missing + } + + public static class BeanWithMissingGetter { + private String value; + // Getter is missing + public void setValue(String value) { + this.value = value; + } + } + + public static class BeanWithBooleanIsGetter { + private boolean valid; + public boolean isValid() { + return valid; + } + public void setValid(boolean valid) { + this.valid = valid; + } + } }