前序
java的反射机制增加程序的灵活性,作为许多开源框架的基础,我们有必要去了解一下它的实现,下面讲解Method
- 代码
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.reflect.Method;
public class TestProxyMethod {
public static interface ISubject {
void sayHi();
}
public static class Subject implements ISubject {
public void sayHi() {
System.out.println("Subject-sayHi");
}
}
public static class SubjectEx extends Subject {
public void sayHi() {
System.out.println("SubjectEx-sayHi");
}
}
public static void main(String[] args) throws Exception {
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
System.out.println("pid=" + runtimeMXBean.getName().split("@")[0]);
Subject subject = new Subject();
SubjectEx subjectEx = new SubjectEx();
//接口的实现调用
Method sayHiI = ISubject.class.getDeclaredMethod("sayHi", new Class[]{});
sayHiI.invoke(subject);
sayHiI.invoke(subjectEx);
Method sayHi = Subject.class.getDeclaredMethod("sayHi", new Class[]{});
sayHi.invoke(subjectEx);
Method sayHiEx = SubjectEx.class.getDeclaredMethod("sayHi", new Class[]{});
sayHiEx.invoke(subject);
}
}
运行结果
pid=2724
Subject-sayHi
SubjectEx-sayHi
Subject-sayHi
SubjectEx-sayHi
Exception in thread "main" java.lang.IllegalArgumentException: java.lang.ClassCastException@5f184fc6
at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Method.java:497)
at TestProxyMethod.main(TestProxyMethod.java:42)
- 分析运行结果
- 当我们获取接口ISubject 的方法sayHi(),执行invoke传入子类时,打印了子类的执行结果
- 当我们获取类Subject 的方法sayHi(),执invoke传入当前定义方法的类和子类,打印出正常的结果
- 当我们获取子类SubjectEx的方法sayHi(),执行invoke传入父类subject,结果确抛出异常ClassCastException,这个异常不就是subject强转SubjectEx报的错么?
- 提出疑问
反射为什么会出现多态的效果?为什么会报ClassCastException错误? - 分析
//Method的执行
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
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);
}
//acquireMethodAccessor最终生成的方法
public MethodAccessor newMethodAccessor(Method var1) {
checkInitted();
if(noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
} else {
NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
var2.setParent(var3);
return var3;
}
}
通过代码可以看出Method的最终执行交个了MethodAccessor的Invoke方法,而MethodAccessor的生成有两种方式:第一种是MethodAccessorGenerator使用asm字节码生成,第二种是NativeMethodAccessorImpl通过jni底层jvm的实现方式,这里我们使用第一种方式进行分析,需要的条件是noInflation为true & 方法的申明类不是内部类
private static void checkInitted() {
if(!initted) {
...
if(System.out == null) {
return null;
} else {
String var1 = System.getProperty("sun.reflect.noInflation");
if(var1 != null && var1.equals("true")) {
ReflectionFactory.noInflation = true;
}
var1 = System.getProperty("sun.reflect.inflationThreshold");
if(var1 != null) {
try {
ReflectionFactory.inflationThreshold = Integer.parseInt(var1);
} catch (NumberFormatException var3) {
throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", var3);
}
}
ReflectionFactory.initted = true;
return null;
}
.....
}
}
- 通过使用工具HSDB获取MethodAccessorGenerator生成的字节码文件,执行方法设置-Dsun.reflect.noInflation=true,获取到类GeneratedMethodAccessor4
package sun.reflect;
import TestProxyMethod.SubjectEx;
import java.lang.reflect.InvocationTargetException;
public class GeneratedMethodAccessor3 extends MethodAccessorImpl {
public Object invoke(Object var1, Object[] var2) throws InvocationTargetException {
if(var1 == null) {
throw new NullPointerException();
} else {
SubjectEx var10000;
try {
var10000 = (SubjectEx)var1;
if(var2 != null && var2.length != 0) {
throw new IllegalArgumentException();
}
} catch (NullPointerException | ClassCastException var4) {
throw new IllegalArgumentException(var4.toString());
}
try {
var10000.sayHi();
return null;
} catch (Throwable var3) {
throw new InvocationTargetException(var3);
}
}
}
public GeneratedMethodAccessor3() {
}
}
有没有发现原来反射是这么实现的呀,恍然大雾了吧,var10000 = (SubjectEx)var1;这个的SubjectEx为方法定义的类,为什么会报错也就一目了然了吧
- 注意
- 反射固然好用,但是会导致老年代的class字节码占用的内存越来越多
- 相同的方法MethodAccessor不会无穷创建,Method.root中会保留一份,但是有并发创建的问题
{
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}
void setMethodAccessor(MethodAccessor accessor) {
methodAccessor = accessor;
// Propagate up
if (root != null) {
root.setMethodAccessor(accessor);
}
}
- 为了生成的class不会发生冲突,java单独使用了类加载隔离,自定义DelegatingClassLoader,这也会导致老年代DelegatingClassLoader数量增多
class ClassDefiner {
static final Unsafe unsafe = Unsafe.getUnsafe();
ClassDefiner() {
}
static Class<?> defineClass(String var0, byte[] var1, int var2, int var3, final ClassLoader var4) {
ClassLoader var5 = (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return new DelegatingClassLoader(var4);
}
});
return unsafe.defineClass(var0, var1, var2, var3, var5, (ProtectionDomain)null);
}
}
class DelegatingClassLoader extends ClassLoader {
DelegatingClassLoader(ClassLoader var1) {
super(var1);
}
}
网友评论