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;
    }
相关推荐
憨子周41 分钟前
2M的带宽怎么怎么设置tcp滑动窗口以及连接池
java·网络·网络协议·tcp/ip
阿斯卡码1 小时前
jupyter添加、删除、查看内核
ide·python·jupyter
霖雨2 小时前
使用Visual Studio Code 快速新建Net项目
java·ide·windows·vscode·编辑器
SRY122404192 小时前
javaSE面试题
java·开发语言·面试
Fiercezm3 小时前
JUC学习
java
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言
ZIM学编程3 小时前
Java基础Day-Sixteen
java·开发语言·windows
我不是星海3 小时前
1.集合体系补充(1)
java·数据结构
P.H. Infinity3 小时前
【RabbitMQ】07-业务幂等处理
java·rabbitmq·java-rabbitmq
爱吃土豆的程序员3 小时前
java XMLStreamConstants.CDATA 无法识别 <![CDATA[]]>
xml·java·cdata