通常在程序中对象类型都是编译期就确定下来的,而 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 21 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 { 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 () {...} 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 () {...} ... public Object invoke (Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {...} public boolean isBridge () {...} ... 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 { ... public String getName () {...} public String toString () {...} public String getSimpleName () {...} public String getTypeName () {...} public String getCanonicalName () {...} public static Class<?> forName(String className){...} 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 Class<?>[] getClasses() {...} public Class<?>[] getDeclaredClasses() throws SecurityException {...} public Field[] getFields() throws SecurityException {...} public Method[] getMethods() throws SecurityException {...} public Constructor<?>[] getConstructors() throws SecurityException {...} public Field getField (String name) throws NoSuchFieldException, SecurityException {...} public Method getMethod (String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException {...} public Constructor<T> getConstructor (Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException {...} public Field[] getDeclaredFields() throws SecurityException {...} public Method[] getDeclaredMethods() throws SecurityException {...} public Constructor<?>[] getDeclaredConstructors() throws SecurityException {...} public Field getDeclaredField (String name) throws NoSuchFieldException, SecurityException {...} public Method getDeclaredMethod (String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException {...} 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() {...} public AnnotatedType getAnnotatedSuperclass () {...} public AnnotatedType[] getAnnotatedInterfaces() {...} public TypeVariable<Class<T>>[] getTypeParameters() {...} public Type getGenericSuperclass () {...} public Type[] getGenericInterfaces() {...} public native Class<? super T> getSuperclass(); public Class<?>[] getInterfaces() {...} 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
,也就是说只有在类、构造方法、普通方法 定义时才能声明类型变量,其他地方不允许。
访问权限控制AccessibleObject
表示可以控制访问权限的对象,其实现类为:Field, Constructor, Method
。也就是说只有在字段、构造方法、普通方法 上可以设置访问权限 AccessibleObject.setAccessible(true)
,并访问非 public
类型。
数组 辅助类 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 ; public static native Object get (Object array, int index) throws IllegalArgumentException, ArrayIndexOutOfBoundsException ; 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 ; 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 }; Object object1 = Array.newInstance(String.class, 2 ); Object object2 = Array.newInstance(int .class, 2 , 3 , 2 );
反射中通常使用 Object/Class<?>
来表示参数或返回值,注意:它们同时代表了数组类型。
判断是否为数组 判断当前类对象或实例是否表示数组,可以使用 Class.isArray()
来判断:
1 2 3 System.out.println(int [].class.isArray()); System.out.println(strings.getClass().isArray()); System.out.println(object.getClass().isArray());
反射中转换为数组 通过反射调用方法后返回 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); } 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 Class<?> clazz = Class.forName("com.ymzs.javabase.reflect.ReflectedClass" ); Object object = clazz.newInstance(); 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); Field publicField = clazz.getField("mSuperClassPublicStr" ); publicField.set(object, "ChangePublicString" ); 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" ); Method publicMethod = clazz.getMethod("superPublicMethod" , int .class); Method declaredMethod = clazz.getDeclaredMethod("testPrivateMethod" , new Class[]{byte [].class}); Object object = clazz.newInstance(); byte [] parameters = {1 , 0 , 1 };declaredMethod.setAccessible(true ); Object results = declaredMethod.invoke(object, parameters); if (results.getClass().isArray()){ String[] array = (String[]) results; String value = "" ; for (String s : array){ value += s; } System.out.println("Declared method return value: " + value); } Method staticMethod = clazz.getMethod("staticMethod" , String.class); System.out.println("Static Method return value: " + 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()); 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()); 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()); 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 { public Object invoke (Object proxy, Method method, Object[] args) throws Throwable ;} public class Proxy implements Serializable { public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException 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
工具工具介绍 ClassDump
是 HotSpot
虚拟机特有的,它是 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 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 { 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(); ... } 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
来调用目标方法的。
参考文档