java反射 效率_JAVA反射-3(性能)

1. 善于使用api

1.1 使用合适的方法获取反射对象

使用反射时,例如尽量不要使用getMethods()等方法后再遍历筛选,而是直接使用getMethod(methodName)等方法根据方法名来获取方法。

1.2 取消安全检查

即使是调用public方法,也需要使用 field.setAccessible(true)来取消安全检查。

Java中通过反射执行一个方法的过程如下:

public class Foo {

private void doStuff() {

System.out.println("hello world");

}

}

public class TestFoo {

@Test

public void test() throws Exception {

String methodName = "doStuff";

Method declaredMethod = Foo.class.getDeclaredMethod(methodName);

//由开发者决定是否要逃避安全体系的检查(是否可以快速获取)

System.out.println(declaredMethod.isAccessible());

if(declaredMethod.isAccessible()){

declaredMethod.setAccessible(true);

}

declaredMethod.invoke(new Foo());

}

}

实际上,在通过反射执行方法时,必须在invoke之前检查Accessible属性。但是方法对象的Accessible属性并不是用来决定是否可以访问的。

public class Foo {

public void doStuff() {

System.out.println("hello world");

}

public static void main(String[] args) throws NoSuchMethodException {

String methodName = "doStuff";

Method declaredMethod = Foo.class.getDeclaredMethod(methodName);

System.out.println(declaredMethod.isAccessible());

}

}

实际上,即使反射的方法访问修饰符为public,isAccessible()的最终结果依旧是false。而且即使为false,还是可以使用invoke()方法执行。

故Accessible的属性并不是我们语法层次理解的访问权限,而是指是否更加容易获得,是否进行安全检查。

我们知道,动态修改一个类或者执行方法都会受到java安全体系的制约,而安全处理是非常消耗资源的(性能非常低),因此对于运行期要执行的方法或要修改的属性提供了Accessible可选项:由开发者来决定是否逃避安全体系的检查。

在源码java.lang.reflect.AccessibleObject#isAccessible中,该方法默认返回值为false。

AccessibleObject是Field、Method、Constructor的父类,决定其是否可以快速访问而不进行访问控制检查。在AccessibleObject中是以override变量保存该值的,但是具体是否快速执行是在Method类的invoke方法中决定的。

public Object invoke(Object obj, Object... args)

throws IllegalAccessException, IllegalArgumentException,

InvocationTargetException{

//检查是否可以快速获取,其值是父类的AccessibleObject的override变量

if (!override) {

//不能快速获取,要进行安全检查

if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {

Class> caller = Reflection.getCallerClass();

checkAccess(caller, clazz, obj, modifiers);

}

}

MethodAccessor ma = methodAccessor; // read volatile

if (ma == null) {

ma = acquireMethodAccessor();

}

//直接执行方法

return ma.invoke(obj, args);

}

Accessible属性只是用来判断是否需要进行安全检查的,如果不需要则直接执行,这样可以大幅度地提升系统性能(当然了,由于取消了安全检查,也可以运行private方法,获取private私有属性)。

2. 善于使用缓存

将反射得到的元数据保存起来,使用时,只需要从内存中获取即可。

元数据包含class、field、method、constructor。

比如对class对象的缓存:

private Map classMap = new HashMap<>();

Class getClass(String className) throws ClassNotFoundException {

Class> clazz = classMap.get(className);

if (className == null) {

clazz = Class.forName(className);

classMap.put(className, clazz);

}

return clazz;

}

使用内省时,也可以将PropertyDescriptor缓存起来,也可以提高效率。

public abstract class IntrospectorUtils {

public static final ConcurrentHashMap introspectorCache

= new ConcurrentHashMap<>(16);

//存储字段

public static PropertyDescriptor persistencePropertyDescriptor(String name, Class clazz) throws IntrospectionException {

if (StringUtils.isBlank(name) || clazz == null) {

throw new BusinessException("业务参数有误,系统异常!");

}

String key = clazz.getSimpleName() + name;

PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, clazz);

//存在返回,不存在去填充(线程安全)

return introspectorCache.computeIfAbsent(key, V -> propertyDescriptor);

}

}

3. 善于使用框架

例如:copy对象时,可以使用CGLib的BeanCopier(其原理是运行时动态生成了用于复制某个类的字节码),其性能比反射框架org.springframework.beans.BeanUtils性能要高。

/**

* @param source 原始对象

* @param targetClass 拷贝的对象类型

*/

public static T2 createCopy(T1 source, Class targetClass) {

if (source == null) {

throw new RuntimeException("参数异常");

} else {

T2 target;

try {

target = targetClass.newInstance();

} catch (Exception e) {

throw new RuntimeException(e);

}

BeanCopier beanCopier = BeanCopier.create(source.getClass(), targetClass, false);

beanCopier.copy(source, target, null);

return target;

}

}

要提高Method.invoke性能,可以使用JDK7的MethodHandler(由于Method.invoke的JIT优化,差距不大)。使用高版本的JDK也很重要,反射性能在不断提高。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值