目录
Java 反射机制
Java 反射(Reflection)是 Java 非常紧张的动态特性。在运行状态中,通过 Java 的反射机制,我们可以或许判断一个对象所属的类。了解任意一个类的全部属性和方法。可以或许调用任意一个对象的任意方法和属性。
Java 反射机制可以无视类方法、变量去访问权限修饰符,而且可以调用任何类的任意方法、访问并修改成员变量值。
对于一半的程序员来说反射的意义不大,对于框架开发人员来说,反射作用就非常大了,它是各种容器实现的核心。
获取 Class 对象
Java 反射利用的是 java.lang.Class 对象,以是我们需要先想办法获取到 Class 对象。
1、类字面常量来获取- Class<?> name = MyClass.class;
复制代码 2、通过对象获取 getClass() 方法- MyClass obj = new MyClass();
- Class<?> name = obj.getClass();
复制代码 3、通过全限定名获取 Class.forName() 方法- Class<?> name = Class.forName("java.lang.Runtime");
复制代码 4、使用 getSystemClassLoader().loadClass() 方法- Class<?> name = ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime");
复制代码 loadClass() 与 forName() 的区别:
forName() 的静态方法 JVM 会装载类,而且执行 static() 中的代码。而getSystemClassLoader().loadClass() 不会执行 static() 中的代码。
获取类成员变量
1、getDeclaredFields 方法
得到类的成员变量数组,包括 public、private 和 proteced,但是不包括父类的申明字段。- Field[] fields = classname.getDeclaredFields();
复制代码 2、getDeclaredField 方法
该方法与 getDeclaredFields 的区别是只能得到类的单个成员变量。- Field field = classname.getDeclaredField("变量名");
复制代码 3、getFields 方法
getFields 可以或许得到某个类的全部的 public 字段,包括父类中的字段。- Field[] fields = classname.getFields();
复制代码 4、getField 方法
与 getFields 类似,getField 方法可以或许得到某个类特定的 public 字段,包括父类中的字段。- Field field = classname.getField(("变量名");
复制代码 获取类方法
1、getDeclaredMethods 方法
返回类或接口声明的全部方法,包括 public、protected、private 和默认方法,但不包括继承的方法。- Method[] methods = classname.getDeclaredMethods()
复制代码 2、getDeclaredMethod 方法
也只能返回一个特定的方法,该方法的第一个参数为方法名,第二个参数名是方法参数。- Method methods = classname.getDeclaredMethods("方法名")
复制代码 3、getMethods 方法
返回某个类的全部 public 方法,包括其继承类的 public 方法。- Method[] methods = classname.getMethods();
复制代码 4、getMethod 方法
只能返回一个特定的方法,该方法的第一个参数为方法名称,后面的参数为方法的参数对应 Class 的对象。- Method method = clazz.getMethod("方法名");
复制代码 反射 java.lang.Runtime
java.lang.Runtime 有一个 exec 方法,以是可以反射调用 Runtime 类来执行本地体系命令。
不使用反射执行本地命令:- import java.io.IOException;
- public class Exec {
- public static void main(String[] args) throws IOException {
- Runtime.getRuntime().exec("calc");
- }
- }
复制代码 反射 Runtime 执行本地命令:- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- public class ReflectionExec {
- public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
- InvocationTargetException, IllegalAccessException {
- // 获取 Runtime 类
- Class<?> clazz = Class.forName("java.lang.Runtime");
- // 获取 Runtime 类的 getRuntime() 方法
- Method getRuntimeMethod = clazz.getMethod("getRuntime");
- // 调用 getRuntime() 方法,获取 Runtime 对象
- Object runtimeObject = getRuntimeMethod.invoke(null);
- // 获取 exec(String command) 方法
- Method execMethod = clazz.getMethod("exec", String.class);
- // 执行系统命令
- execMethod.invoke(runtimeObject, "clac");
- }
- }
复制代码 间接性的调用 Runtime 的 exec 方法执行本地体系命令。
不安全的反射大概会带来致命的漏洞。
ClassLoader 类加载机制
Java 是编译型语言,编写的 java 文件需要编译成后 class 文件后才可以或许被 JVM 运行。类加载器 ClassLoader 负责加载类文件,生成对应的 Class 对象。
JVM 提供的三种类加载器
- Bootstrap ClassLoader(启动类加载器)
负责加载 Java 的核心类,好比 java.lang.Object 等。它是由 C++ 实现的,而且不是 Java 类。
- Extension ClassLoader(扩展类加载器)
负责加载 Java 的扩展类,位于 /lib/ext 目录下的JAR包或类。
- System ClassLoader(体系类加载器)
也称为应用类加载器,负责加载应用程序的类,通常从classpath中加载类。
值得留意的是,Bootstrap ClassLoader 它是 JVM 自身的一部分,并不是 ClassLoader 的子类,无法直接获取对其的引用,以是尝试获取被 Bootstrap ClassLoader 类加载器所加载的类的 ClassLoader 时候都会返回 null。
除了这三种,还可以自定义类加载器。
ClassLoader 类中和加载类相关的方法
- getParent() 返回该类加载器的父类加载器
- loadClass() 加载指定的类
- findClass() 查找指定的类
- findLoadedClass() 查找已经被加载过的类
- defineClass() 定义一个类
- resolveClass() 链接指定的Java类
ClassLoader类加载流程
在加载类之前,会首先使用 findLoadedClass() 方法判断该类是否已经被加载,如果已经加载过,则直接返回对应的 Class 对象。
如果未被加载,则优先使用加载器的父类加载器进行加载,如果加载成功,则返回对应的 Class 对象。
如果父类加载器无法加载该类,大概父类加载器为空,则会调用自身的 findClass() 方法尝试自行加载该类。
在成功加载类之后,类加载器会对其进行链接和初始化利用。
返回一个被 JVM 加载后的 java.lang.Class 类对象。
ClassLoader 的 loadClass 方法核心逻辑代码:- protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- synchronized (getClassLoadingLock(name)) {
- Class<?> c = findLoadedClass(name);
- if (c == null) {
- long t0 = System.nanoTime();
- try {
- if (parent != null) {
- c = parent.loadClass(name, false);
- } else {
- c = findBootstrapClassOrNull(name);
- }
- } catch (ClassNotFoundException e) {
- }
- if (c == null) {
- c = findClass(name);
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
- }
复制代码 自定义的类加载器
通过重写 findClass() 方法,利用 defineClass() 方法来将字节码转换成 java.lang.class 类对象,就可以实现自定义的类加载器。
URLClassLoader
URLClassLoader 类是 ClassLoader 的一个实现,拥有从远程服务器上加载类的能力。
通过 URLClassLoader 可以实现远程的类方法调用,可以实现对一些 WebShell 的远程加载。
例如:通过 URLClassLoader 来加载一个远程的 jar 包执行本地命令- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.lang.reflect.InvocationTargetException;
- import java.net.URL;
- import java.net.URLClassLoader;
- public class TestURLClassLoader {
- public static void main(String[] args) throws IOException,
- ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
- IllegalAccessException {
- // 定义远程加载的jar的URL路径
- URL url = new URL("http://192.168.88.150/CMD.jar");
- // 创建URLClassLoader对象,并加载远程jar包
- URLClassLoader ucl = new URLClassLoader(new URL[]{url});
- // 通过URLClassLoader加载远程jar包中的CMD类
- Class<?> cmdClass = ucl.loadClass("CMD");
- String cmd = "ls";
- // 调用CMD类中的exec方法
- Process process = (Process) cmdClass.getMethod("exec", String.class).invoke(null, cmd);
- // 获取命令执行结果的输入流
- InputStream in = process.getInputStream();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- byte[] b = new byte[1024];
- int a = -1;
- // 读取命令执行结果
- while ((a = in.read(b)) != -1) {
- baos.write(b, 0, a);
- }
- // 输出命令执行结果
- System.out.println(baos.toString());
- }
- }
复制代码 远程的 CMD.jar 中就一个 CMD.class 文件,对应的 CMD.java 如下:- import java.io.IOException;
- public class CMD {
- public static Process exec(String cmd) throws IOException {
- return Runtime.getRuntime().exec(cmd);
- }
- }
复制代码 成功调用 CMD 类中的 exec 方法,执行了 ls 命令。
loadClass()方法与 Class.forName 的区别?
loadClass() 方法和 Class.forName() 方法都可以用于在运行时加载类。
重要区别:
- loadClass() 方法是 ClassLoader 类的一个方法,通过指定的类加载器加载类。它在加载类时不会主动执行类的静态初始化代码。
- Class.forName() 方法是 java.lang.Class 类的一个静态方法,它在加载类时会主动执行类的静态初始化代码。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |