• Groovy反射invokeMethod传参实践


    最近在做分布式性能测试拓展的过程,其中一个思路就是通过Groovy反射执行方法。但是在创建groovy.lang.GroovyObject对象之后,通过调用groovy.lang.GroovyObject#invokeMethod方法执行类方法的时候遇到一个问题,就是groovy.lang.GroovyObject#invokeMethod只有两个参数,一个是String name方法名,另外一个是Object args方法参数。

    源码如下:

        /**
         * Invokes the given method.
         *
         * @param name the name of the method to call
         * @param args the arguments to use for the method call
         * @return the result of invoking the method
         */
        Object invokeMethod(String name, Object args);
    

    但是在性能测试脚本中一般至少有三个参数:1、用来控制线程数或者QPS;2、用来控制测试次数和测试时长;3、用来控制软启动时间。

    这还不包括后期的自定义参数,一下子就犯难了。

    在尝试搜索资料未果之后,我觉得自己动手测试一下。

    String[] args参数

    首先我的想法就是测试String[] args,当做一个参数。因为我的性能测试脚本都是写成了Groovy类的com.funtest.javatest.FunTester#main方法里面的,其实这也只有一个参数。

    测试代码

        public static void main(String[] args) throws IOException {
            ExecuteGroovy.executeMethod("/Users/oker/IdeaProjects/funtester/src/test/groovy/com/funtest/groovytest/perf2.groovy", "test", new String[]{"32", "23"});
        }
    

    被测方法

        static void test(String[] a) {
            output("FunTester成功了!${a[0]}   ${a[1]}");
        }
    

    控制台输出

    INFO-> 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
    INFO-> FunTester成功了!32 23
    
    Process finished with exit code 0
    
    

    成功了!最起码路没有被堵死。

    多参数

    接下来,我就放心多了,应该可以直接使用多个参数来验证猜想,把多个参数当做某个方法的一组参数。

    测试代码

        public static void main(String[] args) throws IOException {
            ExecuteGroovy.executeMethod("/Users/oker/IdeaProjects/funtester/src/test/groovy/com/funtest/groovytest/perf2.groovy", "test", "32", "23");
        }
    

    被测方法

        static void test(String a, String b) {
            output("FunTester成功了!$a $b");
        }
    

    控制台输出

    INFO-> 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
    INFO-> FunTester成功了!32 23
    
    Process finished with exit code 0
    
    

    依然成功了,这下前途光明了!

    方法封装

    相比Java的反射执行,Groovy明显就简单多了。

        /**
         * 获取groovy对象和执行类
         *
         * @param path 类文件路径
         * @param name 方法名
         * @param args 貌似只支持一个参数,这里默认{@link String}
         */
        public static void executeMethod(String path, String name, Object... args) {
            try {
                Class<?> groovyClass = loader.parseClass(new File(path));//创建类
                GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();//创建类对象
                groovyObject.invokeMethod(name, args);
            } catch (IOException | ReflectiveOperationException e) {
                logger.warn("获取类对象 {} 失败!", path, e);
                fail();
            }
        }
    

    下面看一下Java的代码:

        /**
         * 执行具体的某一个方法,提供内部方法调用
         *
         * @param path
         */
        public static void executeMethod(String path, Object... paramsTpey) {
            int length = paramsTpey.length;
            if (length % 2 == 1) FailException.fail("参数个数错误,应该是偶数");
            String className = path.substring(0, path.lastIndexOf("."));
            String methodname = path.substring(className.length() + 1);
            Class<?> c = null;
            Object object = null;
            try {
                c = Class.forName(className);
                object = c.newInstance();
            } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
                logger.warn("创建实例对象时错误:{}", className, e);
            }
            Method[] methods = c.getDeclaredMethods();
            for (Method method : methods) {
                if (!method.getName().equalsIgnoreCase(methodname)) continue;
                try {
                    Class[] classs = new Class[length / 2];
                    for (int i = 0; i < paramsTpey.length; i = +2) {
                        classs[i / 2] = Class.forName(paramsTpey[i].toString());//此处基础数据类型的参数会导致报错,但不影响下面的调用
                    }
                    method = c.getMethod(method.getName(), classs);
                } catch (NoSuchMethodException | ClassNotFoundException e) {
                    logger.warn("方法属性处理错误!", e);
                }
                try {
                    Object[] ps = new Object[length / 2];
                    for (int i = 1; i < paramsTpey.length; i = +2) {
                        String name = paramsTpey[i - 1].toString();
                        String param = paramsTpey[i].toString();
                        Object p = param;
                        if (name.contains("Integer")) {
                            p = Integer.parseInt(param);
                        } else if (name.contains("JSON")) {
                            p = JSON.parseObject(param);
                        }
                        ps[i / 2] = p;
                    }
                    method.invoke(object, ps);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    logger.warn("反射执行方法失败:{}", path, e);
                }
                break;
            }
        }
    

    仔细看这两个方法,其实Java主要是因为传了参数的类型,由于传进来的都是String类型,所以要进行类型转换。

    PS:最后说一下惊天大秘密,JavaGroovy反射执行方法居然是兼容的。哎,又多写了一份功能。


    FunTester腾讯云年度作者Boss直聘签约作者GDevOps官方合作媒体,非著名测试开发。

  • 相关阅读:
    where T: class的解释
    调用钉钉的WebAPI接口实现与ERP数据的同步
    Json序列化和反序列化的方式
    Log4Net日志处理
    MVC项目中异常处理
    FindBI商业智能报表工具
    权限列表实现
    委托,匿名,lambda
    [经典贪心算法]贪心算法概述
    [zt]手把手教你写对拍程序(PASCAL)
  • 原文地址:https://www.cnblogs.com/FunTester/p/14718109.html
Copyright © 2020-2023  润新知