前言
反射是Java的一种独特的设定,它遵循了Java声明的万物皆对象的理念。反射就是面向类的一种工具,可以帮助程序员直接操作类本身。
Demo
public class ReflectSample {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
// 获得 Class 类对象,显示加载类
Class rc = Class.forName("learn.macro.mall.jvm.reflect.Robbot");
// 通过反射构建对象,构建出对象
Robbot r = (Robbot) rc.newInstance();
// getDeclaredMethod 获取类的所有方法
Method sayHi = rc.getDeclaredMethod("sayHi", String.class);
// 通过反射修改方法权限
sayHi.setAccessible(true);
// 执行修改
Object str = sayHi.invoke(r, "qpm2");
}
}
如上述代码,可以根据这些方法,通过反射的方法处理类信息。这种灵活的方式,也为Java多种框架提供的可能。
// Demo Spring + 反射,轻易地就可以构建一个处理的转发框架
// 从sping上下文取出所有消息处理器
Map<String, IPacketProcessor> handlerMap = ctx.getBeansOfType(IPacketProcessor.class);
for (Object obj : handlerMap.values()) {
Class<?> clazz = AopTargetUtils.getTarget(obj).getClass();
// 找到所有处理方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
Packet c = method.getAnnotation(Packet.class);
if (c == null) {
continue;
}
Class<?>[] paramsTypes = method.getParameterTypes();
if(paramsTypes.length != 2){
throw new RuntimeException("packetId=" + c.id() + "\t Method : " + method.getName() + " param length must 2.");
}
// 把处理方法加入缓存中
PacketHolder holder = new PacketHolder((IPacketProcessor)obj, method);
handlers.put((short)holder.getCmdId(), holder);
}
}
Spring MVC 中,应该也会使用类似的做法。
// doInvoke:190, InvocableHandlerMethod (org.springframework.web.method.support)
/**
* Invoke the handler method with the given argument values.
*/
@Nullable
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 通过反射调用
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}
关于反射的性能问题
不少人在认知上,都知道反射是比较慢的,那如何解释里面的快慢问题呢?
public class slow {
class B {}
public static long timeDiff(long old) {
return System.currentTimeMillis() - old;
}
public static void main(String args[]) throws Exception {
long numTrials = (long) Math.pow(10, 8);
long millis;
millis = System.currentTimeMillis();
for (int i=0; i<numTrials; i++) {
new B();
}
System.out.println("Normal instaniation took: "
+ timeDiff(millis) + "ms");
millis = System.currentTimeMillis();
Class<B> c = B.class;
for (int i=0; i<numTrials; i++) {
c.newInstance();
}
System.out.println("Reflecting instantiation took:"
+ timeDiff(millis) + "ms");
}
}
执行的结果,大概保持在1倍的差距。网上大量的说法都是100x差距,估计Java本身已经对反射不断地在做优化了。具体如何从100x缩短成2x,暂时不是很清楚。
Normal instaniation took: 563ms
Reflecting instantiation took:1119ms
原因找了一些博客查看,大概如下:
- 反射类似解释型执行,作为静态语言的编译器,无法对其进行优化。JIT即时编译也同样;
- 所有的调用,都需要先通过名字找到找到方法区的内存字节码位置;
- 调用的时候还会进行一系列的检查(权限等等),包括box和unbox,参数数组化;