目录
例子
- 定义一个User类
public class User {
private String name;
private Integer age;
// 省略get set
}
反射设置属性
public class Test {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("User");
testReflection(clazz);
}
/**
* 如果遇到多个类都有name属性并需要提供统一处理这时候就很好用
* @param clazz
*/
public static void testReflection(Class clazz) {
try {
if (clazz == null) {
System.out.println("空 clazz");
return;
}
//获得类的实例
Object obj = clazz.newInstance();
//获得 User 类中的指定属性对应的Field对象(每个属性对应一个Field对象)
Field field = clazz.getDeclaredField("name");
//取消属性的访问权限控制,即使private 属性也可以进行访问
field.setAccessible(true);
//调用 getter 方法获取属性值
System.out.println(field.get(obj));
//调用setter 方法给属性赋值
field.set(obj, "scott");
//调用 getter 方法获取对应属性修改后的值
System.out.println(field.get(obj));
}catch (Exception e){
e.printStackTrace();
System.out.println("异常");
}
}
}
反射执行方法
public static void main(String[] args) throws Exception {
Class clazz = User.class;
Object obj = clazz.newInstance();
//调用该对象的 setName方法
Method method = clazz.getMethod("setName", new Class[]{String.class});
Object result = method.invoke(obj, new Object[]{"scott"}); // obj.setName("scott");
System.out.println("返回值为:" + result);
//调用对象的getName()方法
Method method1 = clazz.getMethod("getName", new Class[]{});
Object obj1 = method1.invoke(obj, new Object[]{});
System.out.println("返回值为:" + obj1);
}
原理
- 代码示例
Class<?> clazz = Class.forName("TestOOM");
TestOOM testOOM = (TestOOM) clazz1.getDeclaredConstructor().newInstance();
Method method = clazz.getDeclaredMethod("test");
method.invoke(testOOM1);
- 先看下method的invoke方法,Method实例对象维护了一个root引用。当调用Method.copy()进行方法拷贝时,root指向了被拷贝的对象,通过维护root引用意图使相同的方法始终保持只有一个methodAccessor实例。ma.invoke会先通过DelegatingMethodAccessorImpl.invoke,再到NativeMethodAccessorImpl.invoke,NativeMethodAccessorImpl最终调用native方法完成invoke()任务。
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException,InvocationTargetException{
// 检查override,如果override为true,跳过检查
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
// private volatile MethodAccessor methodAccessor;
MethodAccessor ma = methodAccessor;
if (ma == null) {
// ma不存在则去获取
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
private MethodAccessor acquireMethodAccessor() {
// First check to see if one has been created yet, and take it if so
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
methodAccessor = tmp;
} else {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}
return tmp;
}
public MethodAccessor newMethodAccessor(Method method) {
checkInitted();
// 启动参数-Dsun.reflect.noInflation
if (noInflation) {
//省略部分部分
} else {
NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
// 委托模式
DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);
acc.setParent(res);
return res;
}
}
void setMethodAccessor(MethodAccessor accessor) {
methodAccessor = accessor;
// Propagate up
if (root != null) {
root.setMethodAccessor(accessor);
}
}
// 每次获取Method都是new的,类似cow思想
Method copy() {
Method res = new Method(clazz, name, parameterTypes, returnType,
exceptionTypes, modifiers, slot, signature,
annotations, parameterAnnotations, annotationDefault);
res.root = this;
res.methodAccessor = methodAccessor;
return res;
}
-
NativeMethodAccessorImpl最终调用native方法完成invoke()任务。当numInvocations数量大于配置项sun.reflect.inflationThreshold(m默认15)即类膨胀阈值时, 使用MethodAccessorGenerator创建一个代理类对象,并且将被委托的NativeMethodAccessorImpl的parent。总体来说,当调用invoke()时,按照默认配置,Method首先创建一个DelegatingMethodAccessorImpl对象,并设置一个被委托的NativeMethodAccessorImpl对象,那么method.invoke()就被转换成DelegatingMethodAccessorImpl.invoke(),然后又被委托给NativeMethodAccessorImp.invoke()实现。当NativeMethodAccessorImp.invoke()调用次数超过一定热度时(默认15次),被委托方又被转换成代理类来实现。图中DelegatingMethodAccessorImpl用到代码模式,可能代理1,也可能代理2,代理1到一定条件时,代理1通过this.parent.setDelegate更换DelegatingMethodAccessorImpl代理的类。
public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException {
if (++numInvocations > ReflectionFactory.inflationThreshold()) {
MethodAccessorImpl acc = (MethodAccessorImpl)
new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
parent.setDelegate(acc);
}
return invoke0(method, obj, args);
}
// JNI调用
private static native Object invoke0(Method m, Object obj, Object[] args);
- 接下来看看getDeclaredMethod, 这里特别注意privateGetDeclaredMethods,其中涉及软引用,如果getDeclaredMethod用到软引用缓存的,上面的MethodAccessor就是同一个,即使并发导致生成多个GeneratedMethodAccessorXXX(GeneratedMethodAccessorXXX的类加载器其实是一个DelegatingClassLoader类加载器, 这样更方便卸载)也生成的不大;但是如果非软引用而是重新生成的,那就好大量有DelegatingClassLoader加载,GeneratedMethodAccessorXXX大量加载卸载的问题。
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
checkInitted();
Method[] res;
// 软引用
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
if (res != null) return res;
}
// No cached value available; request value from VM
res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
if (rd != null) {
if (publicOnly) {
rd.declaredPublicMethods = res;
} else {
rd.declaredMethods = res;
}
}
return res;
}
- 总结: Method.invoke的逻辑,相关的method因此被SoftReference引用,因此很容易被回收,一旦被回收,那就创建一个新的Method对象,再调用其invoke方法,在调用到一定次数(15次)之后,就构建一个新的字节码类,伴随着GC的进行,同一个方法的字节码类不断构建,直到将Perm充满触发一次Full GC才得以释放
Class.forName 和 ClassLoader区别
- 在java中Class.forName()和ClassLoader都可以对类进行加载。ClassLoader就是遵循双亲委派模型最终调用启动类加载器的类加载器,实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”,获取到二进制流后放到JVM中。Class.forName()方法实际上也是调用的CLassLoader来实现的。
- Spring框架中的IOC的实现就是使用的ClassLoader,JDBC时通常是使用Class.forName()方法来加载数据库连接驱动。这是因为在JDBC规范中明确要求Driver(数据库驱动)类必须向DriverManager注册自己。
设计模式学习
- DelegatingMethodAccessorImpl就是用到委托模式,setDelegate, parent等关键字还可以改变委托的对象。委托模式是一种更高级的代理模式,委托模式可以解决一种方案的多种实现之间自由切换,而代理模式只能根据传入的被代理对象来实现功能。
- 结构型之代理模式
- 装饰器模式
反射和序列化破坏单例解决方案
优化
- 可以把查找到的缓存起来。反射调用时本身就有用弱引用进行缓存。