Skip to content

性能微优化-反射优化

About 853 wordsAbout 3 min

性能新书

2026-08-20

6.6 反射优化

通过属性名称动态获取对象的属性,或者是动态设置对象属性,是大部分Java框架需要的一个基础功能,比如DAO框架,都需要将根据JDBC查询返回的结果集赋值到目标对象上。JSON工具识别JSON字符串中的Key,并赋值Value到对象相应的属性上。微服务系统和各种序列化协议,如Fury,MessagePack、Kyro、Hessian、FST等也需要通过反射完成序列化和反序列化。通常来说,框架需要实现两个基本方法,定义如下:

public interface  ReflectTool {
  /**
   * 获取对象target的指定属性的值
   * @param target
   * @param attrName
   * @return
   */
  public Object getValue(Object target,String attrName);
 
  /**
   * 设置对象target的属性,attr是属性名,value是属性值
   * @param target
   * @param attr
   * @param value
   */
  public void setValue(Object target,String attrName,Object value);
}

如果按照JDK提供的java.lang.reflect 实现反射,性能非常糟糕。下面是一个实现的代码片段

public class JavaRelectTool implements  ReflectTool {

  Object[] EMPTY_PARA = new Object[]{};
  ClassEMPTY_CLASS = new Class[0];
  @Override
  public Object getValue(Object target, String attrName) {
    //获取属性对应的getter方法
    String methodName = buildGetterName(attrName);
    try{
      //查找此方法
      Method method = target.getClass().getMethod(methodName,EMPTY_CLASS);
      //调用反射接口
      Object value = method.invoke(target,EMPTY_PARA);
      return value;
    }catch(Exception ex){
      throw new IllegalArgumentException(ex);
    }

  }
 
  private String buildGetterName(String attr){
    //根据属性名得到getter方法,实际上情况比此复杂,比如针对Boolean属性,是is开头
    return "get"+Character.toUpperCase(attr.charAt(0))+attr.substring(1);
  }
}

允许缓存method而不必每次查找方法,但性能仍然较慢

高性能的框架通常会预先为target对象生成一个属性访问工具类,实现接口ReflectTool,比如对于User对象,生成一个UserDirectAccessTool类

public class User {
  private String  name;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

UserDirectAccessTool类根据传入的attrName来直接调用其对应的getter方法

public class UserDirectAccessTool  implements ReflectTool{
  @Override
  public Object getValue(Object target, String attrName) {
    //前提是知道传入的对象是User对象
    if(attr.equals("name"))  return ((User)target).getName();
   
  }
}

或者根据属性名称的hashCode,使用switch提升性能

public class UserDirectAccessTool  implements ReflectTool{
  static int NAME_CODE = "name".hashCode();
  @Override
  public Object getValue(Object target, String attrName) {
    siwtch(attrName.hashCode()){
        case NAME_CODE:return ((User)target).getName();
    }
   
  }
}

UserDirectAccessTool此类是动态生成,通常基于ASM技术在运行期间生成。

如下AsmFactory类用于生成User对象的属性访问工具,并缓存

ReflectTool reflectTool = AsmFactory.get(User.class);
Object value = reflectTool.getValue(user,"name");

生成动态类的具体技术,可以使用ow2.asm,ByteBuddy等字节码工具,限于篇幅不再书里描述,但可以从本书附带例子中找到。关于通过属性名访问属性值的工具类,有如下

  • Beetl 模版工具:org.beetl.core.om.AsmAAFactory 生成目标对象的属性访问类,使用ow2.asm直接生成class。
  • Fory 序列化工具 org.apache.fory.codegen.CodeGenerator,先生成Java Source然后调用janino工具,将源码编译成class。
  • RelectASM,通过属性名访问类的属性值的工具,使用asm技术实现,实现类是com.esotericsoftware.reflectasm.MethodAccess

动态生成代码还有一种方式是在编译时期生成,采用JSR269

JSR269 全称是 Pluggable Annotation Processing API(可插拔式注解处理API),允许在编译源码期间提供额外的功能,比如修改源码或者生成新的代码,Lombok框架即是JSR269的一种实现。

知行合一