Java 反射

通常在程序中对象类型都是编译期就确定下来的,而 Java 反射机制的核心是 JVM 在运行时才动态加载类或调用方法、属性,这样对象的类型在编译期是未知的,也就是可以通过反射机制直接创建编译期未知的对象。

反射并没有太多理论基础,主要是熟悉各种 API ,通过反射在运行时获得程序或程序集中每一个类型成员和成员变量的信息。

基本概念

反射能做什么

  • 对于任意一个类,都能够知道这个类的所有属性和方法
  • 对于任意一个对象,都能够调用它的任意一个方法和属性

反射常见用途

  • 编译期已知类名
    如果编译器已知类名、类对象,可以通过反射简写代码(比如工厂模式中去掉条件判断等),或者获取类的私有属性、方法、构造方法等。
  • 编译期未知类名
    无法导入到当前类,可以通过反射动态加载类。通过配置文件或者泛型动态加载。
    反射最重要的用途就是开发各种通用框架,动态加载类。很多框架(比如 Spring)都是配置化的(通过 XML 文件配置 JavaBean, Action 等),为了保证框架的通用性,他们可能根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。

基本数据类型类对象

  • 基本数据类型
    boolean.class, char.class, byte.class, short.class, int.class, long.class, float.class, double.class
  • void 类型
    void.class

获取类对象的方法

  • 编译器已知类名
    Class<?> myObjectClass = MyObject.class;
  • 已有类对象
    Class<?> clazz = object.getClass();
  • 已知完整类全名反射
    Class<?> myObjectClass = Class.forName("com.simple.User");
  • 类加载器加载二进制字节流
    Class clazz = classLoader.loadClass("com.***.User");,注意类加载器的双亲委派模型确保能找到该类。只要能拿到 .class 文件对应的二进制字节流,就能通过反射获取 Class 的所有信息。
  • 类的内部类
    Class API 可以遍历内部类或者指定类。或者使用完整类全名反射,注意:内部类和外部类之间使用美元符连接 $
    Class<?> myObjectClass = Class.forName("com.simple.User$InnerClass");

每个 Class 在类加载过程中,会将类对象加载到方法区中,确保 JVM 中只存在一个类对象,它保存了类相关的类型信息,属性,方法,构造方法等等。

常见类和对应 API

AnnotatedElement

AnnotatedElement 注解元素贯穿了整个反射的基础类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface AnnotatedElement {
// 判断指定类型注解是否存在
default boolean isAnnotationPresent(Class<? extends Annotation>
annotationClass) {...}
// 返回指定类型注解,包含父类
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
// 返回所有注解
Annotation[] getAnnotations();
// 返回指定类型的注解数组,包含父类中的
default <T extends Annotation> T[] getAnnotationsByType
(Class<T> annotationClass) {...}
// 返回当前类上指定类型的注解
default <T extends Annotation> T getDeclaredAnnotation
(Class<T> annotationClass) {...}
// 返回当前类上指定类型的注解数组
default <T extends Annotation> T[] getDeclaredAnnotationsByType
(Class<T> annotationClass) {...}
// 返回当前类上注解数组
Annotation[] getDeclaredAnnotations();
}

Member

Member 表示类的成员:字段属性、构造方法、普通方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Member {
// public 成员,包含父类
int PUBLIC = 0;
// 当前类声明,包含私有的
int DECLARED = 1;
// 内部类在哪个类中声明定义
Class<?> getDeclaringClass();
String getName();
// 获取访问控制符
int getModifiers();
// 判断是否为编译器生成
boolean isSynthetic();
}

AccessibleObject

AccessibleObject 表示可访问对象,用来修改查询访问控制符。

1
2
3
4
5
6
7
8
9
public class AccessibleObject implements AnnotatedElement {
// 设置可访问性
public static void setAccessible(AccessibleObject[] array, boolean flag)
throws SecurityException {...}
public void setAccessible(boolean flag) throws SecurityException {...}
// 判断是否有权限访问
public boolean isAccessible() {...}
...
}

GenericDeclaration

GenericDeclaration 声明类型变量的接口,代表着泛型。

1
2
3
4
public interface GenericDeclaration extends AnnotatedElement {
// 返回泛型中定义的类型变量数组
public TypeVariable<?>[] getTypeParameters();
}

Field

Field 代表类中的字段、属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public final class Field extends AccessibleObject implements Member {
// 返回定义该字段的类
public Class<?> getDeclaringClass() {...}
// 判断是否为枚举常量
public boolean isEnumConstant() {...}
// 判断是否为编译器生成
public boolean isSynthetic() {...}
// 返回类型
public Class<?> getType() {...}
// 返回泛型类型
public Type getGenericType() {...}
// 返回对象该字段当前值
public Object get(Object obj)
throws IllegalArgumentException, IllegalAccessException{...}
public boolean getBoolean(Object obj)
throws IllegalArgumentException, IllegalAccessException{...}
...
public double getDouble(Object obj)
throws IllegalArgumentException, IllegalAccessException{...}
// 设置对象该字段的值;第一个表示对象,第二个表示值
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException{...}
public void setBoolean(Object obj, boolean z)
throws IllegalArgumentException, IllegalAccessException{...}
...
public void setDouble(Object obj, double d)
throws IllegalArgumentException, IllegalAccessException
// 返回该字段被注解类型
public AnnotatedType getAnnotatedType() {...}
...
}

Executable

Executable 1.8 新增的抽象类,是构造方法和普通方法的基类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class Executable extends AccessibleObject
implements Member, GenericDeclaration {
...
// 返回形参的类型数组
public abstract Class<?>[] getParameterTypes();
// 返回形参个数,由子类实现
public int getParameterCount() {...}
// 参数为泛型,获取形参类型数组,如返回形参为 {java.util.List<T>, boolean}
public Type[] getGenericParameterTypes() {...}
// 返回参数数组
public Parameter[] getParameters() {...}
// 判断是否采用可变量参数
public boolean isVarArgs() {...}
...
// 返回参数注解数组
public abstract Annotation[][] getParameterAnnotations();
// 返回方法返回值的被注解类型
public abstract AnnotatedType getAnnotatedReturnType();
// 返回方法接受者的被注解类型,通常返回值为定义该方法所在类
public AnnotatedType getAnnotatedReceiverType() {...}
// 返回方法形参的被注解类型数组
public AnnotatedType[] getAnnotatedParameterTypes() {...}
// 返回方法抛出异常,对应被注解类型数组
public AnnotatedType[] getAnnotatedExceptionTypes() {...}
}

Constructor

Constructor 代表类中的构造方法。

1
2
3
4
5
6
7
8
9
10
public final class Constructor<T> extends Executable {
// 返回定义该构造方法的类
public Class<T> getDeclaringClass() {...}
// 创建实例,对应类的空参数构造方法
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException{...}

...
}

Method

Method 代表类中的普通方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public final class Method extends Executable {
// 返回定义该方法的类
public Class<?> getDeclaringClass() {...}
...
// 返回返回值对应类
public Class<?> getReturnType() {...}
// 返回返回值泛型类型
public Type getGenericReturnType() {...}
...
// 调用对象的方法:obj 表示对象,args 表示方法需要的可变量参数
// 返回值为 Object 也可以代表返回值为数组
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException{...}
// 判断是否为桥方法
public boolean isBridge() {...}
...
// 判断是否为 default 方法
public boolean isDefault() {...}
// 返回注解元素的默认值
public Object getDefaultValue() {...}
}

Class

Class 反射基石,可以对 .class 文件全解析,获取字段、构造方法、普通方法、内部类、注解等功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
public final class Class<T> implements java.io.Serializable,
GenericDeclaration, Type, AnnotatedElement {
...
// ************* 获取名称 *******************
// 返回类全名、基本数据类型、Void
// 如果是数组则使用 [ 表示维度,类型会编码。如:int[] 返回 [I
// boolean[2][3] 返回 [[Z ;Object[] 返回 [Ljava.lang.Object
public String getName() {...}
// getName 的前面加上 class 或 interface ,如:class java.lang.String
public String toString() {...}
// 返回简单类名及数组类型,如 String, boolean[][]
public String getSimpleName() {...}
// 返回类全名,如果是数组返回 int[], boolean[][]
public String getTypeName() {...}
// 返回类全名,如果是内部类,使用 . 代替 $,如 OuterClass.InnerClass
public String getCanonicalName() {...}

// ************* 类对象、实例、加载器 ****************
// 根据类全限定名返回类对象,默认会执行类初始化
public static Class<?> forName(String className){...}
// 根据类全限定名,指定类加载器返回类对象,initialize 决定是否初始化
public static Class<?> forName(String name, boolean initialize
, ClassLoader loader)
// 类对象创建类实例
public T newInstance() throws InstantiationException,
IllegalAccessException {...}
// 返回类加载器
public ClassLoader getClassLoader() {...}

// ************* 内部类 ****************
// 返回局部内部类或者匿名内部类,具体是在哪个方法中定义的
public Method getEnclosingMethod() throws SecurityException {...}
// 返回内部类在哪个构造方法中定义
public Constructor<?> getEnclosingConstructor()
throws SecurityException {...}
// 返回内部类在哪个外部类中定义
public Class<?> getEnclosingClass() throws SecurityException {...}
// 返回成员内部类是在哪个类中定义的
public Class<?> getDeclaringClass() throws SecurityException {...}
// 判断是否为匿名类
public boolean isAnonymousClass() {...}
// 判断是否为局部内部类
public boolean isLocalClass() {...}
// 判断是否为成员内部类
public boolean isMemberClass() {...}
// 返回所有 public 成员内部类数组,包含父类定义的 public 成员内部类
public Class<?>[] getClasses() {...}
// 返回当前类定义的所有内部类,包含 private 内部类
public Class<?>[] getDeclaredClasses() throws SecurityException {...}

// *************类属性、普通方法、构造方法****************
// 返回所有 public 字段数组,包含父类定义 public 字段
public Field[] getFields() throws SecurityException {...}
// 返回所有 public 方法数组,包含父类定义 public 方法,默认继承 Object
public Method[] getMethods() throws SecurityException {...}
// 返回所有 public 构造方法数组
public Constructor<?>[] getConstructors() throws SecurityException {...}
// 返回指定名称 pulic 字段,包含父类定义的 public 字段
public Field getField(String name)
throws NoSuchFieldException, SecurityException {...}
// 返回指定名称及参数类型的 public 方法
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {...}
// 返回指定参数的 public 构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {...}
// 返回当前类定义的所有字段,包含 private 字段
public Field[] getDeclaredFields() throws SecurityException {...}
// 返回当前类定义的所有方法,包含 private 方法
public Method[] getDeclaredMethods() throws SecurityException {...}
// 返回当前类定义的所有构造方法,包含 private 构造方法
public Constructor<?>[] getDeclaredConstructors()
throws SecurityException {...}
// 返回当前类中定义的,指定名称字段,包含 private 字段
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException {...}
// 返回当前类中定义的,指定名称及参数类型的方法,包含 private 方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {...}
// 返回指定参数构造方法,包含 private 构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {...}

// ************* 注解 ****************
// 判断类对象是否为注解
public boolean isAnnotation() {...}
// 返回当前类上指定类型的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass){}
// 判断指定类型注解是否存在
public boolean isAnnotationPresent
(Class<? extends Annotation> annotationClass) {...}
// 返回指定类型的注解数组,包含父类中的
public <A extends Annotation> A[] getAnnotationsByType
(Class<A> annotationClass) {...}
// 返回所有注解
public Annotation[] getAnnotations() {...}
// 返回当前类使用的注解数组,不包含父类
public Annotation[] getDeclaredAnnotations() {...}
// 返回 extends 后的父类的被注解类型,Object、接口、基本数据类、void 都返回 null
// 即使父类没有被注解,也会返回包含父类的被注解类型
public AnnotatedType getAnnotatedSuperclass() {...}
// 返回 implements 后的接口的被注解类型数组
// 即使所有接口都没有被注解,也会返回包含这些接口的被注解类型数组
public AnnotatedType[] getAnnotatedInterfaces() {...}

// ************* 泛型 ****************
// 返回类的类型变量数组
public TypeVariable<Class<T>>[] getTypeParameters() {...}
// 返回泛型的父类
public Type getGenericSuperclass() {...}
// 返回类实现的泛型接口数组
public Type[] getGenericInterfaces() {...}

// ************* 其他 ****************
// 返回类的父类,但是 Object, 接口,基本数据类型,void 都返回 null
public native Class<? super T> getSuperclass();
// 返回类实现的接口数组
public Class<?>[] getInterfaces() {...}
// 获取数组的组件类型,如:int[] 返回 int
public native Class<?> getComponentType();
// 返回访问控制符
public native int getModifiers();
// 获取资源文件
public InputStream getResourceAsStream(String name) {...}
// 获取资源文件
public java.net.URL getResource(String name) {...}
// 判断是否为枚举
public boolean isEnum() {...}
// 返回枚举类的值数组
public T[] getEnumConstants() {...}
// 对象安全转换
public T cast(Object obj) {...}
// 类安全转换
public <U> Class<? extends U> asSubclass(Class<U> clazz) {...}
// 判断类对象是否为编译器生成类
public boolean isSynthetic() {...}
// 判断当前类对象是否表示为数组
public native boolean isArray();
...
}

get***getDeclared*** 的区别

  • getDeclared***
    获取当前类中所有的字段属性,方法等,包含私有的,但不包含父类。
  • get***
    获取公共的字段属性,方法等,包含父类的。

总结

  • 泛型中类型变量定义声明
    GenericDeclaration 表示类型变量声明的接口,其实现类为:Class, Constructor, Method,也就是说只有在类、构造方法、普通方法定义时才能声明类型变量,其他地方不允许。

0080-GenericDeclaration-impl-uml.png

  • 访问权限控制
    AccessibleObject 表示可以控制访问权限的对象,其实现类为:Field, Constructor, Method。也就是说只有在字段、构造方法、普通方法上可以设置访问权限 AccessibleObject.setAccessible(true),并访问非 public 类型。

0080-AccessibleObject-impl-uml.png

数组

辅助类 Array

Array 提供一系列静态方法用来动态创建和访问数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public final class Array {
// 实例化指定类型,指定长度的一维数组
public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {...}
// 实例化指定类型,指定维度的数组
public static Object newInstance(Class<?> componentType, int... dimensions)
throws IllegalArgumentException, NegativeArraySizeException {...}
// 返回数组长度
public static native int getLength(Object array)
throws IllegalArgumentException;
// 返回指定位置的数组,返回值 Object 也可以表示数组
public static native Object get(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
// 返回 boolean 值
public static native boolean getBoolean(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
...
public static native double getDouble(Object array, int index)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
// 对数组指定位置设置对应值
public static native void set(Object array, int index, Object value)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
// 设置 boolean 值
public static native void setBoolean(Object array, int index, boolean z)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
...
public static native void setDouble(Object array, int index, double d)
throws IllegalArgumentException, ArrayIndexOutOfBoundsException;

定义数组

Java 中可以明确类型定义数组,也可以使用 Object/Class<?> 来表示数组:

1
2
3
4
5
6
7
String[] strings = {"a", "b", "c"};
Class<?> classes = int[].class;
Object object = new int[]{1, 2, 3};
// Array 创建一维数组
Object object1 = Array.newInstance(String.class, 2);
// Array 创建三维数组
Object object2 = Array.newInstance(int.class, 2, 3, 2);

反射中通常使用 Object/Class<?> 来表示参数或返回值,注意:它们同时代表了数组类型。

判断是否为数组

判断当前类对象或实例是否表示数组,可以使用 Class.isArray() 来判断:

1
2
3
System.out.println(int[].class.isArray());          // true
System.out.println(strings.getClass().isArray()); // true
System.out.println(object.getClass().isArray()); // true

反射中转换为数组

通过反射调用方法后返回 Object/Class<?> ,可以先判断是否为数组,然后再做转换。转换可以显示强制转换为对应类型数组,也可以通过 java.lang.reflect.Array 辅助类来处理。

1
2
3
4
5
6
7
8
9
10
11
// 判断是否为数组
if (object.getClass().isArray()) {
// 显示强制转换
int[] converts = (int[]) object;
for (int i : converts) {
System.out.println(i);
}

// Array 辅助类,先获取数组长度,再获取数组最后一个值
System.out.println(Array.get(object, Array.getLength(object) - 1));
}

示例

实例化

反射创建类实例,有两种方法:

  • Constructor.newInstance()
    构造方法实例化,可以传递构造方法的参数。
  • Class.newInstance()
    直接通过类来实例化,相当于构造方法空参数来实例化。
1
2
3
4
5
6
7
8
9
Class<?> clazz = Class.forName("com.ymzs.javabase.reflect.ReflectedClass");
// 类直接实例化
Object object1 = clazz.newInstance();
System.out.println("object1 = " + object1);

// 构造方法实例化
Constructor constructor = clazz.getConstructor(String.class, byte[].class);
byte[] paras = {1, 2};
Object object2 = constructor.newInstance("s", paras);

获取和设置字段属性

先将类实例化,然后使用该实例修改字段属性;如果是 static 字段,它属于类的字段属性,所以将实例设置为 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 1. 先将类实例化
Class<?> clazz = Class.forName("com.ymzs.javabase.reflect.ReflectedClass");
// Object object = clazz.newInstance();
Object object = clazz.newInstance();

// 2. 通过 getDeclaredField 可以获取任意字段,包含私有的
Field declaredField = clazz.getDeclaredField("mPrivateStr");
System.out.println("Declared Field is: " + declaredField);
// 私有属性设置为可访问
declaredField.setAccessible(true);
String value = (String) declaredField.get(object);
System.out.println("get private: " + declaredField.getName() + " = " + value);
String changeString = "ChangePrivateString";
// 设置
declaredField.set(object, changeString);
value = (String) declaredField.get(object);
System.out.println("set private: " + declaredField.getName() + " = " + value);

// 3. 通过 getField 只能修改公共字段,包含父类的
Field publicField = clazz.getField("mSuperClassPublicStr");
// 设置
publicField.set(object, "ChangePublicString");

// 4. 静态 static 属于类,实例传入 null
Field staticPublicField = clazz.getField("sPublicStr");
// 设置
staticPublicField.set(null, "changeStaticString");

调用方法

先将类实例化,然后使用该实例调用方法;如果是 static 字段,它属于类方法,所以将实例设置为 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Class<?> clazz = Class.forName("com.ymzs.javabase.reflect.ReflectedClass");
// 1. 通个 getMethod 获取公共方法,包含父类的
Method publicMethod = clazz.getMethod("superPublicMethod", int.class);
// 2. 通过 getDeclaredMethod 获取任意方法,包含私有的
Method declaredMethod = clazz.getDeclaredMethod("testPrivateMethod"
, new Class[]{byte[].class});

// 3. 类实例化
Object object = clazz.newInstance();
// 方法的数组参数
byte[] parameters = {1, 0, 1};
// 私有方法设置为可访问
declaredMethod.setAccessible(true);
// 4. 调用方法
Object results = declaredMethod.invoke(object, parameters);
// 5. 判断方法返回结果是否为数组
if (results.getClass().isArray()){
// 如果为数组,强制转换
String[] array = (String[]) results;
String value = "";
for (String s : array){
value += s;
}
System.out.println("Declared method return value: " + value);
}

// 5. 获取 static 方法,实例传入 null
Method staticMethod = clazz.getMethod("staticMethod", String.class);
System.out.println("Static Method return value: "
// 6. 调用静态方法
+ staticMethod.invoke(null, "110"));

内部类

通过反射接口可以获取内部类是在哪个类/构造方法/普通方法中定义,以及判断它们属于成员/匿名/局部内部类的哪一种。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 内部类定义
public class OuterClass{
public Runnable runnable;

// 成员内部类
public class MemberInnerClass {

}

// 匿名内部类
public OuterClass(){
runnable = new Runnable() {
@Override
public void run() {
//
}
};
}

// 局部内部类
public Object testMethodClass(){
class MethodClass{

}
return new MethodClass();
}
}

// 测试代码
private void testInnerClass(){
System.out.println("***********testInnerClass***********");
OuterClass outerClass = new OuterClass();
Class<?> clazz = outerClass.testMethodClass().getClass();
System.out.println("Method class from : " + clazz.getEnclosingMethod());
System.out.println("Outer class is : " + clazz.getEnclosingClass());
System.out.println("is Method class: " + clazz.isLocalClass()); // true

clazz = OuterClass.MemberInnerClass.class;
System.out.println("Member class from: " + clazz.getDeclaringClass());
System.out.println("Outer class is : " + clazz.getEnclosingClass());
System.out.println("is Member class: " + clazz.isMemberClass()); // true

clazz = outerClass.runnable.getClass();
System.out.println("Anonymous class from : "
+ clazz.getEnclosingConstructor());
System.out.println("Outer class is : " + clazz.getEnclosingClass());
System.out.println("is Anonymous class = "
+ clazz.isAnonymousClass()); // true
System.out.println("***********testInnerClass***********");
}

// 测试结果
Method class from :public java.lang.Object com.*.OuterClass.testMethodClass()
Outer class is : class com.***.OuterClass
is Method class: true
Member class from: class com.***.OuterClass
Outer class is : class com.ymzs.***.OuterClass
is Member class: true
Anonymous class from : public com.***.OuterClass()
Outer class is : class com.***.OuterClass
is Anonymous class = true

反射常见场景

  • 反射与泛型
    反射与泛型的混合使用在很多框架中都会出现,应用非常广泛。基础知识点可以参考Java Type类型 ,主要是通过反射获取泛型相关信息。
  • 反射与注解
    反射是注解解析方式的一种,在运行时解析注解并实现对应功能。
  • 动态代理
    代理模式的一种,通过反射动态生成代理对象。设计模式参考:代理模式

动态代理

代理模式:为其他对象提供一种代理以控制对这个对象的访问。 这是设计模式中对代理模式的介绍,代理模式分为静态代理和动态代理。静态代理即编译期前代码及代理关系就已经明确存在;动态代理是通过反射机制动态地生成代理对象,也就是代码编译中并不知道代理关系,动态代理将代理和被代理对象进行了解耦。Java 反射技术是在内存中,动态生成一个新类来实现动态代理。

Java 中只能为接口 interface 实现动态代理

基础类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 动态代理必须通过这个接口来实现代理方法调用
public interface InvocationHandler {
/**
* 调用被代理对象的方法
* Object: 被代理的真实对象
* method:被代理实例对应的方法
* args:传入的参数数组
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

// Proxy 辅助类,用来创建代理对象类或者实例
public class Proxy implements Serializable {
// 创建动态代理对应的 class 对象
// ClassLoader 动态代理类的类加载器;interfaces 为接口数组
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces) throws IllegalArgumentException

/**
* JVM 运行时动态创建代理对象,对应类名为 com.sun.proxy.$Proxy**
* ClassLoader: 代理对象的类加载器
* Class<?>[]:被代理对象对应的接口数组(多态)
* InvocationHandler:我们实现的动态代理对象
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException {...}
...
}

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 1. 目标接口,被动态代理
public interface Subject {
void operation();
}

// 2. RealSubject,真实对象
public class RealSubject implements Subject{
public void operation(){
System.out.println("RealSubject::operation.");
}
}

// 3. DynamicProxy,实现动态代理
public class DynamicProxy implements InvocationHandler{
private Object object;

public DynamicProxy(Object object) {
this.object = object;
}

@Override
public Object invoke(Object o, Method method, Object[] objects)
throws Throwable {
// 可以在这里增加权限控制,或者添加其他功能
...
Object result = method.invoke(object, objects);
...
return result;
}
}

// 4. Test
public class TestDynamicProxy {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
InvocationHandler dynamicProxy = new DynamicProxy(realSubject);

Subject subject = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),
new Class[]{Subject.class}, dynamicProxy);
System.out.println(subject.getClass());
subject.operation();
}
}

// 5. Result
class com.sun.proxy.$Proxy0
RealSubject::operation.
  • InvocationHandler.invoke
    在这里实现动态代理,同时可以在动态代理前增加权限检查,或者添加功能(相当于装饰模式)。
  • com.sun.proxy.$Proxy0
    从输出的 Log 可以看出,动态代理是重新生成了一个新的类 com.sun.proxy.$Proxy0,并实现了动态代理的功能。新类命名格式:包名 + $Proxy + id 序号

ClassDump 工具

工具介绍

ClassDumpHotSpot 虚拟机特有的,它是 HotSpot SA: Serviceability Agent 中的一个工具。ClassDump 可以在运行时 dump 类文件,特别是动态生成或者运行时被修改了字节码的类。
HotSpot 有一套私有 API 提供了对 JVM 内部数据结构的审视功能,称为 Serviceability Agent。可以通过 API 直接写 Java 代码来查看一个跑在 HotSpot 上的 Java 进程的内部状态。它也提供了一些封装好的工具,可以直接在命令行上跑,包括 ClassDump 工具。SA 的一个重要特征是它是“进程外审视工具”。也就是说 SA 并不运行在要审视的目标进程中,而是运行在一个独立的 Java 进程中,通过操作系统上提供的调试 API 来连接到目标进程上。这样 SA 的运行不会受到目标进程状态的影响,因而可以用于审视一个已经挂起的 Java 进程。一个被调试器连接上的进程会被暂停下来。所以在 SA 连接到目标进程时,目标进程也是一直处于暂停状态的,直到 SA 解除连接。如果需要在线上使用SA的话需要小心,不要通过 SA 做过于耗时的分析,宁可先把数据都抓出来,把连接解除掉之后再离线分析。目前的使用经验是,连接上一个小 Java 进程的话很快就好了,但连接上一个“大”的 Java 进程(堆比较大、加载的类比较多)可能会在连接阶段卡住好几分钟,线上需要慎用。
ClassDump 的特点:需要目标 Java 进程必须在运行中;连接时会导致目标进程暂停。

示例过滤器

示例:使用 ClassDump 工具 Dump 出动态代理生成的类,其类特点是文件名都是 com.sun.proxy.$Proxy 开头的 class 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
import sun.jvm.hotspot.oops.InstanceKlass;
import sun.jvm.hotspot.tools.jcore.ClassFilter;

public class MyClassNameFilter implements ClassFilter {

private static final String CLASSNAME_PREFIX = "com/sun/proxy/$Proxy";

@Override
public boolean canInclude(InstanceKlass instanceKlass) {
String klassName = instanceKlass.getName().asString();
return klassName.startsWith(CLASSNAME_PREFIX);
}
}

注意:过滤器中需要过滤的类名是以 / 分割的而不是 . ,如:com/sun/proxy/$Proxy

执行过程

当前是在 Ubuntu 环境中运行的,执行时必须使用 root 权限(使用 sudo 也会报错)。

修改动态代理源文件,确保进程持续运行

1
2
3
4
5
6
7
8
9
public class TestDynamicProxy {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
...
subject.operation();
// 添加这一句,使进程阻塞等待,不会退出
System.in.read();
}
}

运行该测试程序,并查看对应的进程名:

1
2
3
4
5
// 找到目标进程 id: 81249
root@server005:~/classdump# jps
83496 Jps
119412 ServerLauncher
81249 TestDynamicProxy

MyClassNameFilter.java 文件并不需要编译(也编译不过导入的包名找不到),在 MyClassNameFilter.java 文件所在目录运行,命令使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 0. root 用户下执行
// 1. 指定过滤器类文件为 MyClassNameFilter
// 2. 指定 classdump 进程 id:81249
root@server005:~/classdump# java -classpath "$JAVA_HOME/lib/sa-jdi.jar" -Dsun.jvm.hotspot.tools.jcore.filter=MyClassNameFilter sun.jvm.hotspot.tools.jcore.ClassDump 81249
Warning: Can not create class filter!
Attaching to process ID 81249, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.121-b00
root@server005:~/classdump# tree com/
com/
└── sun
└── proxy
└── $Proxy0.class

2 directories, 1 file

执行完毕后,会生成三个代码目录:com, java, sun,其中 com 为动态代理中生成的类,其他为加载的系统类。

没有使用 root 账号运行出现的错误:

1
2
3
4
5
6
7
8
// 1. 直接使用其他账户运行,无法 attach 到目标进程
Warning: Can not create class filter!
Attaching to process ID 80662, please wait...
Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process

// 2. sudo 运行,无法打开对应库文件
Attaching to process ID 81297, please wait...
Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: cannot open binary file

结果分析

使用 jd-gui 打开 $Proxy0.class 文件分析动态代理生成的类实现了哪些功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// 继承了 Proxy 类,并实现了 Subject 接口,
// 动态代理前提条件就要求被代理是接口
public final class $Proxy0
extends Proxy
implements Subject
{
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;

public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}

// 静态代码块,类加载时执行
static
{
try
{
// 使用了反射技术
// 获取 Object 的默认方法:equals, toString, hashCode
// 获取 Subject 定义的方法:operation
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
ew Class[0]);
m3 = Class.forName("Subject").getMethod("operation", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
return;
}
..
}

public final boolean equals(Object paramObject)
{
..
return ((Boolean)this.h.invoke(this, m1,
new Object[] { paramObject })).booleanValue();
...
}

public final String toString()
{
...
return (String)this.h.invoke(this, m2, null);
...
}

public final int hashCode()
{
...
return ((Integer)this.h.invoke(this, m0, null)).intValue();
...
}

// 通过反射 InvocationHandler.invoke 调用目标方法
public final void operation()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

可以看出生成的代理类,最终是通过 InvocationHandler 来调用目标方法的。

参考文档

0%