Java获取目标对象的Getter、Setter方法列表的两种方式对比

726 阅读3分钟

前言

关于Java中,获取对象属性Getter、Setter方法列表的方式耗时对比

  1. 方式一:使用正则匹配目标对象的Getter、Setter方法
  2. 方式二:使用对象属性描述器获取对应的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方法

  • 获取方法
    1. 通过正则匹配的方法获取目标类的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;
      }
      
    2. 通过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());
      }
      

测试

方式一

  • 测试类

    这边为了方便,直接用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方法列表为空 image.png 所以,无论使用哪一种方式获取,都需要目标实体类存在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方法列表的方式,并且使用属性描述器时需要抛出异常

总结

方式二需要进行异常处理,耗时远超方式一