Java如何加载外部Jar的类并通过反射调用类的方法

 更新时间:2024年06月17日 09:24:33   作者:编程经验分享  
这篇文章主要介绍了Java如何加载外部Jar的类并通过反射调用类的方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

问题

工作中遇到一个需求,客户端将第三方的 jar 包上传到服务器中,系统需要解析出上传的 jar 中所有类以及类下的方法(方法名,方法输入参数类型,方法返回值类型),并将这些类以及方法提供给配置人员选择,由配置人员自由配置需要用到的方法。

类加载器

Java 虚拟机在加载类文件时,默认情况下使用自带的三个类加载器

  • BootstrapClassLoader:加载 %JRE_HOME%\jre\lib 下的 jar 和 class
  • ExtentionClassLoader:加载 %JRE_HOME%\jre\lib\ext 下的 jar 和 class
  • AppClassLoader:加载当前应用路径下的 class

在 JDK 1.8 的 rt.jar 中,Launcher 类里定义了这三个类加载器

可以从源码中看到,三个加载器分别指定了不同的加载路径来加载类

  • BootstrapClassLoader
private static String bootClassPath = System.getProperty("sun.boot.class.path");
  • ExtentionClassLoader
System.getProperty("java.class.path")
  • AppClassLoader
System.getProperty("java.ext.dirs")

三个加载器通过双亲委派机制来加载类文件,每个加载器都只负责加载自己负责的加载路径,显然,如果要自定义加载路径,从而实现加载外部 jar 包,就必须自定义加载器来实现。

在 JDK 1.8 中,可以看到 AppClassLoader 和 ExtClassLoader 都是继承 URLClassLoader 来实现的。

若要加载外部的 jar 包,只需拿到外部 jar 包的 URL 路径,用 URLClassLoader 便可以加载。

获取外部 jar 包中的类以及方法

public class JarUtils {
    /**
     * 这些默认方法不打印
     */
    private static List<String> DEFAULT_METHODS = Arrays.asList("wait", "equals", "notify", "notifyAll", "toString", "hashCode" , "getClass");

    public static void parseJar(File file) throws Exception {
        URL url = file.toURI().toURL();
        try (URLClassLoader myClassLoader = new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader())) {
            //通过jarFile和JarEntry得到所有的类
            JarFile jar = new JarFile(file);
            //返回zip文件条目的枚举
            Enumeration<JarEntry> enumFiles = jar.entries();
            JarEntry entry;
            //测试此枚举是否包含更多的元素
            while (enumFiles.hasMoreElements()) {
                entry = enumFiles.nextElement();
                if (entry.getName().indexOf("META-INF") < 0) {
                    String classFullName = entry.getName();
                    if (classFullName.endsWith(".class")) {
                        //去掉后缀.class
                        String className = classFullName.substring(0, classFullName.length() - 6)
                                .replace("/", "."); //类名
                        Class<?> myclass = myClassLoader.loadClass(className);

                        System.out.println(String.format("类名:%s", className));

                        //得到类中包含的属性
                        Method[] methods = myclass.getMethods();
                        for (Method method : methods) {
                            String methodName = method.getName();
                            if (DEFAULT_METHODS.contains(methodName)) {
                                continue;
                            }

                            //方法名
                            System.out.print(String.format("方法名:%s; ", methodName));

                            Class<?>[] parameterTypes = method.getParameterTypes(); //方法参数类型

                            System.out.print(String.format("方法参数类型:%s; ", Arrays.stream(parameterTypes)
                                    .map(Class::getSimpleName).collect(joining(","))));

                            System.out.println(String.format("方法返回类型:%s; ", method.getReturnType().getSimpleName())); //方法返回值类型
                        }
                        System.out.println("***************************");
                    }
                }
            }
        } catch (IOException e) {
            throw e;
        }
    }

}

调用外部 jar 包中的方法

public class JarUtils {

    public static void executeMethod(File f, String className, String methodName, Class[] classes, Object[] objects) {
        //通过URLClassLoader加载外部jar
        try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{f.toURI().toURL()})) {
            //获取外部jar里面的具体类对象
            Class<?> targetClass = urlClassLoader.loadClass(className);
            //创建对象实例
            Object instance = targetClass.newInstance();
            //获取实例当中的方法名为show,参数只有一个且类型为string的public方法
            Method method = targetClass.getMethod(methodName, classes);
            //传入实例以及方法参数信息执行这个方法
            Object returnObj = method.invoke(instance, objects);
            System.out.println(returnObj);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java 获取Zookeeper节点下所有数据详细步骤

    Java 获取Zookeeper节点下所有数据详细步骤

    本文介绍了如何使用Java获取ZooKeeper节点下所有数据,实际应用示例中,我们演示了如何从ZooKeeper节点下获取配置信息并输出到控制台,ZooKeeper是一个开源的分布式协调服务,适用于分布式系统中的数据同步、配置管理、命名服务等功能,感兴趣的朋友一起看看吧
    2024-11-11
  • java实现合并两个已经排序的列表实例代码

    java实现合并两个已经排序的列表实例代码

    这篇文章主要介绍了java实现合并两个已经排序的列表实例代码,有需要的朋友可以参考一下
    2013-12-12
  • SpringBoot集成echarts实现k线图功能

    SpringBoot集成echarts实现k线图功能

    ECharts是一款基于JavaScript的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表,本文给大家介绍了SpringBoot集成echarts实现k线图功能,文中有详细的代码示例供大家参考,需要的朋友可以参考下
    2024-07-07
  • SpringBoot生成License的实现示例

    SpringBoot生成License的实现示例

    License指的是版权许可证,那么对于SpringBoot项目,如何增加License呢?本文就来介绍一下,感兴趣的可以了解一下
    2021-06-06
  • Spring注解@DependsOn解析

    Spring注解@DependsOn解析

    今天要分享得是Spring的@DependsOn注解,对于@DependsOn,我们从它的名称里面就能看出意思是“依赖于”,那么在Spring中,它的作用就是解决Bean的创建依赖,感兴趣的小伙伴快来阅读吧
    2023-04-04
  • Java连接MongoDB的常用方法详解

    Java连接MongoDB的常用方法详解

    这篇文章主要为大家详细介绍一下Java语言连接MongoDB的常用方法以及实现增删改查功能的示例代码,感兴趣的小伙伴可以跟随小编一起了解一下
    2022-07-07
  • 排序算法图解之Java插入排序

    排序算法图解之Java插入排序

    插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。本文通过图片合示例讲解了插入排序的实现,需要的可以了解一下
    2022-11-11
  • SpringSecurity获取当前登录用户的信息的几种方法实现

    SpringSecurity获取当前登录用户的信息的几种方法实现

    本文主要介绍了SpringSecurity中获取当前登录用户信息的多种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-03-03
  • static关键字有何魔法?竟让Spring Boot搞出那么多静态内部类(推荐)

    static关键字有何魔法?竟让Spring Boot搞出那么多静态内部类(推荐)

    这篇文章主要介绍了static关键字有何魔法?竟让Spring Boot搞出那么多静态内部类,本文通过实例代码图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • Java实现读取不同格式的文件的示例详解

    Java实现读取不同格式的文件的示例详解

    在 Java 开发中,我们经常需要读取不同类型的文件,包括 Excel 表格文件、"doc" 等,本文将介绍如何使用 Java 读取这些不同类型的文件,需要的可以参考下
    2024-01-01

最新评论