前语
信任大家对反射都不陌生,在日常开端的过程中,咱们也会经常用到反射来完成某些业务场景,而且许多结构Spring、Mybatis、JDBC等…底层都有靠反射来完成,先来谈谈优缺点;
优点
例如加载类的加载、办法的调用,能够很灵敏的加载类,创立目标的功能性愈加强大,调用类中特点和办法的时分,无视修饰符;经过配置能够无需修改代码,来完成对不同目标的加载。
缺点
使代码的复杂度上升、如果用不好会导致履行功率比正常创立目标的功率更低。
反射源码剖析
反射代码首要存在java.lang.reflect包下,Class类首要办法有/forName、newInstance、getMethod、getDeclaredMethod
getMethod
看源码基本是分三步; 1:checkMemberAccess办法鉴权 2:获取method 3: method办法的复制‘
getDeclaredMethod
这个办法和getMethod基本步骤是一样的,不一样的是getDeclaredMethod办法是加载私有的办法,getMethod是PUBLIC加载公共办法。
invoke
再来看invoke办法,也是分为三步 1.checkAccess鉴权查验 2.获取copy时运用的MethodAccessor,MethodAccessor 也有一个invoke办法,分别有三个完成类DelegatingMethodAccessorImpl、MethodAccessorImpl 、NativeMethodAccessorImpl,可见MethodAccessor也是靠反射来完成的。 3.调用MethodAccessor的invoke办法完成办法的调用。 以上就是对反射源码的简略剖析,有兴趣的朋友去好好看看,这儿仅仅做个简略介绍。
反射履行功率比照
下面看看几种创立目标,而且履行目标办法的功率比照。
package com.example.system.factory;
import com.example.system.domain.ReflectEntity;
import lombok.SneakyThrows;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectTest {
private Long testReflectMethod() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
Thread thread = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
Class clazz = Class.forName("com.example.system.domain.ReflectEntity");
Object ReflectEntity = clazz.newInstance();
Method method = clazz.getDeclaredMethod("getReflectMethod");
method.invoke(ReflectEntity);
}
});
}
return System.currentTimeMillis() - startTime;
}
private Long testReflectField() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
Thread thread = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
ReflectEntity reflectEntity = new ReflectEntity("Lxlxxx", 20, "coding");
Field field = null;
try {
field = reflectEntity.getClass().getDeclaredField("name");
field.set(reflectEntity, "changeLxlxxx");
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
return System.currentTimeMillis() - startTime;
}
private Long testNormMethod() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
Thread thread = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
ReflectEntity reflectEntity = new ReflectEntity("Lxlxxx", 20, "coding");
reflectEntity.setName("changeLxlxxx");
}
});
}
return System.currentTimeMillis() - startTime;
}
private Long testNormField() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
Thread thread = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
ReflectEntity reflectEntity = new ReflectEntity("Lxlxxx", 20, "coding");
reflectEntity.eName = "English";
}
});
}
return System.currentTimeMillis() - startTime;
}
public static void main(String[] args) {
ReflectTest reflectTest = new ReflectTest();
final Long aLong = reflectTest.testReflectMethod();
final Long aLong1 = reflectTest.testReflectField();
final Long aLong2 = reflectTest.testNormMethod();
final Long aLong3 = reflectTest.testNormField();
System.out.printf("testReflectMethod履行时刻:" + aLong);
System.out.printf("testReflectField履行时刻:" + aLong1);
System.out.printf("testNormMethod履行时刻:" + aLong2);
System.out.printf("testNormField履行时刻:" + aLong3);
}
}
测验结果
每个办法创立10000个线程,经过反射、非反射进行目标创立现已特点调用;
履行所需求的时刻,从上到下发现反射的履行时分用的最长,功率也是最低的;
反射功率低原因剖析
从上面反射调用的办法时刻来看testReflectMethod
testReflectField,testReflectMethod办法里面履行了 Method.invoke办法,那咱们就针对这个办法来具体剖析下;
1.每次都要查看办法的可见性 2.MethodAccessor.invoke办法每次都要查验参数 3.Method.invoke办法对入参的封装与解封 这三点我认为是影响功率比较大的三点
处理反射功率低的问题
能够挑选不用JDK的反射类,因为功率实在是太低了,能够运用hutool封装的反射工具类;
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<hutool.version>5.3.5</hutool.version>
</dependency>
下面来看看运用hutool的ReflectUtil反射工具类的履行功率怎么;
private Long testReflectUtil(){
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
Thread thread = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
ReflectEntity t = ReflectUtil.newInstance(ReflectEntity.class);
ReflectUtil.setFieldValue(t, "name","changeLxlxxx");
ReflectUtil.invoke(t, "getReflectMethod", "");
}
});
}
return System.currentTimeMillis() - startTime;
}
优化后
经过调用ReflectUtil类之后,与其他办法的履行时刻进行了比照,发现履行的功率的确很高,还不到JDK原生反射一半的时刻;
总结
在咱们的老项目中,我也是看到运用JDK的原生反射代码,才去好奇剖析下它的履行功率,果不其然功率是非常低的,存在于老项目中也是见怪不怪,往往了解底层的逻辑,才干愈加帮助咱们更好对项目进行优化。