JMeter源码解析之NewDriver.java(二)完结

JMeter源码解析之NewDriver.java(二)完结

NewDriver.java主要作用

JMeter程序入口:JMeter的主类-设置初始类路径和加载程序。

文件路径

路径地址:...\apache-jmeter-5.1\src\core\org\apache\jmeter\NewDriver.java

关于Main内容中的代码解析

java 复制代码
 /**
     * The main program which actually runs JMeter.
     * 实际运行JMeter的主程序
     * @param args
     *            the command line arguments
     */
    public static void main(String[] args) {
        /**
        *  当启动加载的异常内容里面,没有相应的报错时,程序启动,否则报错。
         * EXCEPTIONS_IN_INIT的数据主要来源 EXCEPTIONS_IN_INIT.add(new Exception("Error adding jar:"+libJar.getAbsolutePath(), e));
        */
         if(!EXCEPTIONS_IN_INIT.isEmpty()) {
            System.err.println("Configuration error during init, see exceptions:"+exceptionsToString(EXCEPTIONS_IN_INIT)); // NOSONAR Intentional System.err use
        } else {
             //通过将 loader 设置为当前线程的上下文类加载器,可以将自定义的类加载器 loader 用于当前线程中的类加载操作,从而实现需要使用特定类加载器加载的类或资源
            Thread.currentThread().setContextClassLoader(loader);

            //设置日志环境系统属性,简单的可以连接为,当我们入参为-j D:\1\1\1.log,我们会在对应的路径D:\1\1\生成1.log日志
            setLoggingProperties(args);

            try {
                // Only set property if it has not been set explicitely
                // 设置系统属性变成noGUI模式运行
                if(System.getProperty(HEADLESS_MODE_PROPERTY) == null && shouldBeHeadless(args)) {
                    System.setProperty(HEADLESS_MODE_PROPERTY, "true");
                }
                /*
                * 这段代码通过反射加载和执行了org.apache.jmeter.JMeter类的启动方法。
                * 它首先加载主类,然后使用反射创建主类的实例,接着获取start方法并通过反射执行该方法,以启动Apache JMeter。
                * */
                Class<?> initialClass = loader.loadClass("org.apache.jmeter.JMeter");// $NON-NLS-1$
                Object instance = initialClass.getDeclaredConstructor().newInstance();
                Method startup = initialClass.getMethod("start", new Class[] { new String[0].getClass() });// $NON-NLS-1$
                startup.invoke(instance, new Object[] { args });
            } catch(Throwable e){ // NOSONAR We want to log home directory in case of exception
                e.printStackTrace(); // NOSONAR No logger at this step
                System.err.println("JMeter home directory was detected as: "+JMETER_INSTALLATION_DIRECTORY); // NOSONAR Intentional System.err use
            }
        }
    }

上述代码中我们可以发现一有趣的代码:

java 复制代码
 Class<?> initialClass = loader.loadClass("org.apache.jmeter.JMeter");// $NON-NLS-1$
                Object instance = initialClass.getDeclaredConstructor().newInstance();
                Method startup = initialClass.getMethod("start", new Class[] { new String[0].getClass() });// $NON-NLS-1$
                startup.invoke(instance, new Object[] { args });

然后就比较好奇,为啥我们不直接引用JMeter这个类,然后直接调用里调用里面的start方法呢?所以针对这个去查询了下资料,得到如下回答:

使用反射方式调用方法和直接调用类的方法有以下不同点和好处

1.动态性:通过反射,可以在运行时动态地获取类和方法的信息,而不需要在编译时确定。这样可以实现更加灵活的代码逻辑和动态的代码调用。

2.解耦合:通过反射,可以在不知道具体类的情况下调用其方法。这样可以实现松耦合的代码结构,提高代码的可维护性和可扩展性。

3.功能扩展:使用反射可以实现一些无法在编译时确定的功能,比如动态添加、修改和删除类的属性、方法等。

4.框架开发:很多框架和库都使用反射来实现通用的功能,比如Spring框架的IOC容器、Hibernate框架的ORM映射等。通过反射可以实现通用的代码模块,提高开发效率和代码复用性。

需要注意的是,使用反射可能会带来一些性能上的损失,因为反射调用的过程需要额外的计算和处理。但是在很多情况下,这种性能损失可以接受,特别是在需要动态性和灵活性的场景下。因此,在选择使用反射还是直接调用类的方法时,需要综合考虑具体的需求和性能要求。

关于setLoggingProperties内容中的代码解析

java 复制代码
/*
     * Set logging related system properties.
     * 设置与日志记录相关的系统属性
     */
    private static void setLoggingProperties(String[] args) {
        //获取NoGUI时输入的命令中-j后面的参数,如-j D:\1\1\1.log,返回的结果是:D:\1\1\1.log
        String jmLogFile = getCommandLineArgument(args, 'j', "jmeterlogfile");// $NON-NLS-1$ $NON-NLS-2$
        if (jmLogFile != null && !jmLogFile.isEmpty()) {
            jmLogFile = replaceDateFormatInFileName(jmLogFile);
            //System.setProperty()方法用于设置系统属性。系统属性是一些键值对,用于存储关于操作系统和应用程序的配置信息
            System.setProperty(JMETER_LOGFILE_SYSTEM_PROPERTY, jmLogFile);// $NON-NLS-1$
        } else if (System.getProperty(JMETER_LOGFILE_SYSTEM_PROPERTY) == null) {// $NON-NLS-1$
            System.setProperty(JMETER_LOGFILE_SYSTEM_PROPERTY, "jmeter.log");// $NON-NLS-1$ $NON-NLS-2$
        }


        //日志类型,但是目前网上搜到的资料,并没有确切的-i相关的参数进行入参,可以理解为当前就是默认的日志格式
        String jmLogConf = getCommandLineArgument(args, 'i', "jmeterlogconf");// $NON-NLS-1$ $NON-NLS-2$
        File logConfFile = null;

        if (jmLogConf != null && !jmLogConf.isEmpty()) {
            logConfFile = new File(jmLogConf);
        } else if (System.getProperty("log4j.configurationFile") == null) {// $NON-NLS-1$
            logConfFile = new File("log4j2.xml");// $NON-NLS-1$
            if (!logConfFile.isFile()) {
                logConfFile = new File(JMETER_INSTALLATION_DIRECTORY, "bin" + File.separator + "log4j2.xml");// $NON-NLS-1$ $NON-NLS-2$
            }
        }

        if (logConfFile != null) {
            System.setProperty("log4j.configurationFile", logConfFile.toURI().toString());// $NON-NLS-1$
        }
    }

关于getCommandLineArgument内容中的代码解析

java 复制代码
 /*
     * Find command line argument option value by the id and name.
     * 根据id和名称查找命令行参数选项值
     * 输入的JMeter命令比如是这样:jmeter -n -t C:\Users\Desktop\tokenZB.jmx -l D:\1\1\1.jtl -e -o D:\1\1\report -j D:\1\1\1.log
     * 然后我们可以得到一个String类型数组,内容就是这样的:
     * new String[]{"-n","-t","C:\\Users\\Desktop\\tokenZB.jmx","-l","D:\\1\\1\\1.jtl","-e","-o","D:\\1\\1\\report","-j","D:\\1\\1\\1.log"};
     */
    private static String getCommandLineArgument(String[] args, int id, String name) {
        //获取对应的命令后面的参数,比如-j,为配置日志路径,则通过传入id获取对应-j后面的参数内容,"--"也是一样的道理
        final String shortArgName = "-" + ((char) id);// $NON-NLS-1$
        final String longArgName = "--" + name;// $NON-NLS-1$

        String value = null;

        for (int i = 0; i < args.length; i++) {
            //获取某个参数后面指定的值,比如-j后面的值,并且此处是可以进行指定参数与自己的输入的参数连写,JMeter也会进行自动识别
            if ((shortArgName.equals(args[i]) && i < args.length - 1)
                    || longArgName.equals(args[i])) {
                if (!args[i + 1].startsWith("-")) {// $NON-NLS-1$
                    value = args[i + 1];
                }
                break;
                //比如原则上-j D:\1\\1\1.log,但是如果我们不小心写成了-jD:\1\\1\1.log,JMeter可以读取-j后面的参数,即:D:\1\\1\1.log
            } else if (!shortArgName.equals(args[i]) && args[i].startsWith(shortArgName)) {
                value = args[i].substring(shortArgName.length());
                break;
            }
        }

        return value;
    }
相关推荐
程序员-珍16 分钟前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
弱冠少年23 分钟前
websockets库使用(基于Python)
开发语言·python·numpy
2401_8572979142 分钟前
招联金融2025校招内推
java·前端·算法·金融·求职招聘
技术无疆1 小时前
【Python】Streamlit:为数据科学与机器学习打造的简易应用框架
开发语言·人工智能·python·深度学习·神经网络·机器学习·数据挖掘
福大大架构师每日一题1 小时前
23.1 k8s监控中标签relabel的应用和原理
java·容器·kubernetes
羊小猪~~1 小时前
机器学习/数据分析--用通俗语言讲解时间序列自回归(AR)模型,并用其预测天气,拟合度98%+
人工智能·python·机器学习·数据挖掘·数据分析·回归·时序数据库
金灰1 小时前
HTML5--裸体回顾
java·开发语言·前端·javascript·html·html5
菜鸟一皓1 小时前
IDEA的lombok插件不生效了?!!
java·ide·intellij-idea
爱上语文1 小时前
Java LeetCode每日一题
java·开发语言·leetcode
qq_273900231 小时前
解析TMalign文本文件中的转换矩阵
python·生物信息学