前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >根据路径获取指定类实例并执行指定的方法

根据路径获取指定类实例并执行指定的方法

作者头像
每天学Java
发布2020-06-02 09:59:18
2.7K0
发布2020-06-02 09:59:18
举报
文章被收录于专栏:每天学Java

最近在OA项目上和第三方做集成,我需要提供一些接口给供第三方调用,在这个过程中觉得自己测试接口很麻烦,所以想写一个JSP界面来界面化测试自己写的一些接口。

在我的实际项目上,当我将接口部署到测试环境的时候,我们需要先自己测试一下接口,然后才会让第三方进行调用,这个时候测试就是一个很麻的事情,因为通常来说接口跟流程绑定,我们需要通过走流程去测试接口,自己觉得很麻烦,不如自己写一个简单的测试界面,供自己测试使用,这里就需要用到类加载器和反射的相关知识了。

PS:网上找的代码编辑器最近排版效果很差,下面展示的代码大家大致的看下,然后可以去小程序中下载代码到本地去浏览,主要聊一下思路。

01

效果

我们先看一下效果,然后在叙述过程,

接口

提交前界面

点击提交后的界面:

其中hello world!就是返回的数据。

这中间经历了什么呢?

我传入了接口的路径,名称,方法,参数,点击提交时,后台逻辑首先根据路径,通过类加载器获取所有的Class的物理路径,然后通过File来将Class文件存入到集合,此时我们通过传入的名称取到对应Class文件,紧接着再找到指定方法名执行对应方法,再将接口返回的数据展示到界面上。

02

获取Class

根据包路径获取Class离不开类加载器,在加载资源时的ClassLoader可以有多种选择

1. 系统类加载器SystemClassLoader,可通过ClassLoader.getSystemClassLoader()获得; 2. 当前ClassLoader:加载了当前类的ClassLoader; 3. 线程上下文类加载器ContextClassLoader:Thread.currentThread().getContextClassLoader(); 4. 自定义类加载器;

因为SystemClassLoader只能加载classpath路径下的资源,有局限性。加载了当前类的ClassLoader也不满足当前需求,ContextClassLoader没有局限性,可以在应用程序中将其设为任意ClassLoader,加载任意目录下的类和资源,所以这里我们选用ContextClassLoader获取资源。

代码语言:javascript
复制
public static Set<Class<?>> getClasses(String pack) {
    // 第一个class类的集合
    Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
    // 是否循环迭代
    boolean recursive = true;
    // 获取包的名字 并进行替换
    String packageName = pack;
    String packageDirName = packageName.replace('.', '/');
    // 定义一个枚举的集合 并进行循环来处理这个目录下的things
    Enumeration<URL> dirs;
    try {
        dirs = Thread.currentThread()
        .getContextClassLoader()
        .getResources(
                packageDirName);
     }
     catch(IOException e){
        e.printStackTrace();
      }

获取到集合目录时,我们开始迭代获取Class的物理文件路径,file是class文件存储形式,如果存在jar包我们还需要特殊处理,这里酒不沾湿了,完整代码可进入进入小程序查看。

代码语言:javascript
复制
while (dirs.hasMoreElements()) {
    // 获取下一个元素
    URL url = dirs.nextElement();
    System.out.println(url);
    // 得到协议的名称
    String protocol = url.getProtocol();
    // 如果是以文件的形式保存在服务器上
    if ("file".equals(protocol)) {
        System.err.println("file类型的扫描");
        // 获取包的物理路径
        String filePath = URLDecoder.decode(
        url.getFile(), "UTF-8");
        // 以文件的方式扫描整个包下的文件 并添加到集合中
        findAndAddClassesInPackageByFile(
        packageName, filePath,
        recursive, classes);
    }
 }

其中findAndAddClassesInPackageByFile方法是将物理路径的class文件放入到Set集合中。

代码语言:javascript
复制
// 循环所有文件
for (File file : dirfiles) {
    // 如果是目录 则继续扫描
    if (file.isDirectory()) {
        findAndAddClassesInPackageByFile(
                packageName + "."
                  + file.getName(), 
                file.getAbsolutePath(),
                recursive,
                classes);
    } else {
        // 如果是java类文件 去掉后面的.class 只留下类名
        String className = file.getName().substring(0,
                file.getName().length() - 6);
        try {
            // 添加到集合中去
            // classes.add(Class.forName(packageName + '.' 
            // +className));
            // 这里用forName有一些不好,会触发static方法,
            //没有使用classLoader的load干净
            classes.add(Thread.
                    currentThread().
                    getContextClassLoader()
                    .loadClass(packageName + '.'
                            + className));
        } catch (ClassNotFoundException e) {
         
            e.printStackTrace();
        }
    }
}

实际上,在findAndAddClassesInPackageByFile中我们就可以对于class文件进行过滤了,而不用二次循环,这里我就不处理了。我们看下最后如何执行方法:

getClasses方法就是上面第一个方法。callMethod.getParameterCount()这个方法是获取参数个数,防止wrong number of arguments的错误。

代码语言:javascript
复制
public String exe(String packageName, 
                  String objName, 
                  String methodName,
                  Object... param) {
    Object o = "";
    Set<Class<?>> classSet = getClasses(packageName);
    for (Class<?> class1 : classSet) {
        if (objName.equals(class1.getSimpleName())) {
            try {
                Method[] methods =
                        class1.getDeclaredMethods();
                Method callMethod = null;
                for (Method method : methods) {
                    if (method.
                            getName().
                            equals(methodName)) {
                        callMethod = method;
                        break;
                    }
                }
                //如果方法add是私有的private方法,
                // 按照上面的方法去调用则会产生异常NoSuchMethodException,
                // 这时必须改变其访问属性
                int number = callMethod.getParameterCount();
                if (number > 0) {
                    o = callMethod.invoke(
                            class1.newInstance(), param);
                } else {
                    o = callMethod.invoke(
                            class1.newInstance());
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }
    return o.toString();
}

完整的代码示例大家可以去小程序代码库中进行复制:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-02-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 每天学Java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档