前言
关于Java中,获取对象属性Getter、Setter方法列表的方式耗时对比
- 方式一:使用正则匹配目标对象的Getter、Setter方法
- 方式二:使用对象属性描述器获取对应的Getter、Setter方法
准备
- 实体类对象A
一个很简单的实体类,只有属性,没有Get Set方法
public class DomainA { private String name1; private String name2; private String name3; private String name4; private String name5; private String name6; private String name7; private String name8; private String name9; private String name10; private String name11; private String name12; private String name13; private String name14; private String name15; private String name16; private String name17; private String name18; private String name19; private String name20; private String name21; private String name22; private String name23; private String name24; private String name25; private String name26; private String name27; private String name28; private String name29; private String name30; private String name31; private String name32; private boolean people; } - 实体对象B
在对象A的基础上,使用IDEA快生生成对应的Getter、Setter方法
- 获取方法
- 通过正则匹配的方法获取目标类的Getter、Setter方法列表
private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); private static final Pattern IS_PATTERN = Pattern.compile("is(\\p{javaUpperCase}\\w*)"); /** * 获取对象的getter方法。 * * @param obj 对象 * @return 对象的getter方法列表 */ public static List<Method> getGetterMethods(Object obj) { // getter方法列表 List<Method> getterMethods = new ArrayList<>(); // 获取所有方法 Method[] methods = obj.getClass().getMethods(); // 查找getter方法 for (Method method : methods) { // 因任何类都集成于Object,获取获取到的get方法中会包含getClass方法,需要移除;对于IDEA生成boolean类型的Get方法,名称会变成isXxx if (!"getClass".equalsIgnoreCase(method.getName()) && (GET_PATTERN.matcher(method.getName()).matches() && (method.getParameterTypes().length == 0) || IS_PATTERN.matcher(method.getName()).matches() && (method.getParameterTypes().length == 0))) { getterMethods.add(method); } } // 返回getter方法列表 return getterMethods; } /** * 获取对象的setter方法。 * * @param obj 对象 * @return 对象的setter方法列表 */ public static List<Method> getSetterMethods(Object obj) { // setter方法列表 List<Method> setterMethods = new ArrayList<>(); // 获取所有方法 Method[] methods = obj.getClass().getMethods(); // 查找setter方法 for (Method method : methods) { Matcher m = SET_PATTERN.matcher(method.getName()); if (m.matches() && (method.getParameterTypes().length == 1)) { setterMethods.add(method); } } // 返回setter方法列表 return setterMethods; } - 通过
PropertyDescriptor对象获取对应的Getter、Setter方法列表public static List<Method> getSetterMethodsV2(Object obj) throws IntrospectionException { Class<?> targetClass = obj.getClass(); Field[] fields = targetClass.getDeclaredFields(); return Arrays.stream(fields).map(field -> { try { PropertyDescriptor pd = new PropertyDescriptor(field.getName().toLowerCase(), targetClass); return pd.getWriteMethod(); } catch (IntrospectionException e) { throw new RuntimeException(e); } }).filter(Objects::nonNull).collect(Collectors.toList()); } public static List<Method> getGetterMethodsV2(Object obj) throws IntrospectionException { Class<?> targetClass = obj.getClass(); Field[] fields = targetClass.getDeclaredFields(); return Arrays.stream(fields).map(field -> { try { PropertyDescriptor pd = new PropertyDescriptor(field.getName().toLowerCase(), targetClass); return pd.getReadMethod(); } catch (IntrospectionException e) { throw new RuntimeException(e); } }).filter(Objects::nonNull).collect(Collectors.toList()); }
- 通过正则匹配的方法获取目标类的Getter、Setter方法列表
测试
方式一
- 测试类
这边为了方便,直接用main方法了;未导入log,用sout输出
public static void main(String[] args) throws IntrospectionException { StopWatch stopWatch = new StopWatch(); stopWatch.start(); List<Method> setterMethods = getSetterMethods(new DomainA()); stopWatch.stop(); System.out.println("通过正则匹配获取对象Set方法列表耗时: " + stopWatch.getTotalTimeNanos() + "ns"); stopWatch.start(); List<Method> getterMethods = getGetterMethods(new DomainA()); stopWatch.stop(); System.out.println("通过正则匹配获取对象Get方法列表耗时: " + stopWatch.getTotalTimeNanos() + "ns"); stopWatch.start(); List<Method> setterMethodsV2 = getSetterMethodsV2(new DomainA()); stopWatch.stop(); System.out.println("通过属性描述器获取对象Set方法列表耗时: " + stopWatch.getTotalTimeNanos() + "ns"); stopWatch.start(); List<Method> getterMethodsV2 = getGetterMethodsV2(new DomainA()); stopWatch.stop(); System.out.println("通过属性描述器获取对象Get方法列表耗时: " + stopWatch.getTotalTimeNanos() + "ns"); } - 结果
通过正则匹配获取对象Set方法列表耗时: 346417ns 通过正则匹配获取对象Get方法列表耗时: 372584ns Exception in thread "main" java.lang.RuntimeException: java.beans.IntrospectionException: Method not found: isName1 at com.alvaro.core.core.utils.BeanUtil.lambda$getSetterMethodsV2$0(BeanUtil.java:85) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566) at com.alvaro.core.core.utils.BeanUtil.getSetterMethodsV2(BeanUtil.java:87) at com.alvaro.core.core.utils.BeanUtil.main(BeanUtil.java:116) Caused by: java.beans.IntrospectionException: Method not found: isName1 at java.beans.PropertyDescriptor.<init>(PropertyDescriptor.java:107) at java.beans.PropertyDescriptor.<init>(PropertyDescriptor.java:71) at com.alvaro.core.core.utils.BeanUtil.lambda$getSetterMethodsV2$0(BeanUtil.java:82) ... 9 more可以看到,对于没有生成Getter Setter方法的类,使用属性描述器来获取时会报错
debug后也可以看到使用正则匹配的方式获取Getter和Setter方法列表为空
所以,无论使用哪一种方式获取,都需要目标实体类存在Getter、Setter方法
方式二
- 测试方法
public static void main(String[] args) throws IntrospectionException { StopWatch stopWatch = new StopWatch(); stopWatch.start(); List<Method> setterMethods = getSetterMethods(new DomainB()); stopWatch.stop(); System.out.println("通过正则匹配获取对象Set方法列表耗时: " + stopWatch.getTotalTimeNanos() + "ns"); stopWatch.start(); List<Method> getterMethods = getGetterMethods(new DomainB()); stopWatch.stop(); System.out.println("通过正则匹配获取对象Get方法列表耗时: " + stopWatch.getTotalTimeNanos() + "ns"); stopWatch.start(); List<Method> setterMethodsV2 = getSetterMethodsV2(new DomainB()); stopWatch.stop(); System.out.println("通过属性描述器获取对象Set方法列表耗时: " + stopWatch.getTotalTimeNanos() + "ns"); stopWatch.start(); List<Method> getterMethodsV2 = getGetterMethodsV2(new DomainB()); stopWatch.stop(); System.out.println("通过属性描述器获取对象Get方法列表耗时: " + stopWatch.getTotalTimeNanos() + "ns"); } - 结果
通过正则匹配获取对象Set方法列表耗时: 858833ns 通过正则匹配获取对象Get方法列表耗时: 1073041ns 通过属性描述器获取对象Set方法列表耗时: 42585333ns 通过属性描述器获取对象Get方法列表耗时: 44054874ns从这个测试结果中可以非常明显的看到,使用属性描述器获取Getter、Setter方法列表的方式耗时远超使用正则来获取Getter、Setter方法列表的方式,并且使用属性描述器时需要抛出异常
总结
方式二需要进行异常处理,耗时远超方式一