性能微优化-反射优化
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的一种实现。
