BeanShell:检查变量是否为null的正确方式

文章讨论了BeanShell解释器在处理未定义变量与null比较时的非预期行为。BeanShell作为弱类型语言,允许引用未声明的变量,这导致null==v1返回false而非报错。为正确判断变量是否为null,文章提供了两种解决方案:一是使用`void!=v1`来检测变量是否定义,然后进行null检查;二是通过自定义方法`isNull()`,引用未定义变量时会触发解释器异常。这两种方法分别适用于不同场景的需求。

BeanShell是一个小型、免费、可嵌入的 Java 源代码 具有对象脚本语言功能的解释器,用 Java 编写。 BeanShell动态执行标准Java语法,并使用通用语法对其进行扩展 脚本编写便利性,适用于 Java 的轻量级脚本。
在Java中判断一个变量是否为null很简单的事儿 null == v1,
BeanShell是一个支持Java语法的的脚本解释器,按道理null == v1也是支持的,我一直是这么理解的, 大量测试也没问题。
但是今天在写单元测试时,因为手误,在解释器执行脚本时没有为变量v1定义值。
按我的理解,解释器对没有定义的变量应该会报错,但是没有,解释器正常执行了,对于null==v1返回了false
你可以在BeanShell命令行解释器中执行 print(null ==v1);验证此现象结果就是输出false

这就颠覆我对BeanShell的理解了,于是赶紧去BeanShell的官方网站翻资料,
大概理解了原因:

BeanShell 虽然支持标准的Java语法,但与Java强类型语言特性不同,BeanShell是个弱类型的脚本语言,弱类型的语言的一个特点就是变量可以不需要预声明,这也是 BeanShell的语言特性—就是支持松散类型的 Java 语法,也就是说,你可以引用变量,而不需要先声明它们,也不指定任何类型。

因为BeanShell这个未声明即可引用的特性,所以对于没有声明的变量在执行与null==比较时恒为false,也就是说未声明的变量恒不为空,且不会报错。

所以如果在BeanShell中简单使用null != v1null == v1对变量判null或非null时,如果变量v1没有被定义得到的结果是不正确的。

解决方案一

根据BeanShell官方文档《Undefined Variables》说明,可以通过如下判断变量是否等于内置变量void在判断变量是否被定义了。

if ( foobar == void ){
	// 为true时变量未定义
}

所以我们要判断一个变量是否为null的时候,应该先判断它是否有被定义这样才能得到正确的结果
如下:

if (void != v1 && null == v1){
	// 为true时v1有定义且为null
}

单元测试示例:

public BshTest{
	@Test
	public void test13UndefinedVariable() {
		try {
			Interpreter interpreter = new Interpreter();  // Construct an interpreter
			interpreter.eval("b1=(null == v1);print(b1);"
					+ "b2=(void == v1);print(b2);");
			assertTrue(Boolean.FALSE.equals( interpreter.get("b1")));
			assertTrue(Boolean.TRUE.equals( interpreter.get("b2")));
		} catch (EvalError e) {
			e.printStackTrace();
			assertFalse(true);
		}
	}
}

解决方案二

上一个解决方案确实解决了当变量未定义时判null或非null表达式可以得到正确结果,但是,在实际开发中我们往往要求所有的变量都预定义的,不允许未定义的变量情况存在,那么这时方案一就会隐藏变量未定义的错误,因为它不会报错抛出异常。在我的项目中就是这样的需求:变量未定义的情况是一种设计中的缺陷必须报错。
所以要求在判断变量是否为null的时候,如果变量未定义直接抛出异常。
怎么解决呢?其实在BeanShell脚本执行时真正引用到未定义的变量的值的时候,解释器就会抛出异常,所以我们可以把判null和非null封装成一个方法,将变量做为参数传递给方法,这样未定义的符号就会导致解释器在调用方法时抛出异常,如下为单元测试示例:

public BshTest{
	@Test
	public void test14BuiltinIsNull() {
		try {
			Interpreter interpreter = new Interpreter();  // Construct an interpreter
			// 将当前对象添加到namespace,这样脚本中才可以访问对象中的方法,isNull,isNonull
			interpreter.getNameSpace().importObject(this);
			interpreter.eval("print(isNull(v1));");
			fail();
		} catch (EvalError e) {
			// v1没有定义肯定会执行到这里抛出异常
			System.out.println(e.getMessage());
			assertFalse(false);
		}
	}
	/** 内置判断null方法 */
	public boolean isNull(Object value) {
		return null == value;
	}
	/** 内置判断非null方法 */
	public boolean isNonull(Object value) {
		return null != value;
	}
}

参考资料

《Undefined Variables》

### 使用 JMeter BeanShell 后置处理器修改变量 在 JMeter 中,BeanShell 后置处理器可以用于执行自定义脚本逻辑,在采样器之后运行。这使得可以在测试过程中动态地处理和修改变量。 #### 获取并设置线程组内的局部变量 为了访问或更改当前线程中的局部变量,`vars` 对象提供了相应的方法: - `vars.get(String key)` 方法用来获取指定名称的变量;如果该键对应的变量不存在,则返回 null[^1]。 - 若要更新现有变量或将新赋给某个特定名字的空间内,可调用 `vars.put(String key, String value)` 来实现这一点。 下面是具体的代码实例展示如何通过 BeanShell 脚本来改变名为 "exampleVar" 的变量的内容: ```java // 读取现有的 exampleVar 或者初始化它为空字符串 String oldValue = vars.get("exampleVar") != null ? vars.get("exampleVar") : ""; // 打印旧以便调试目的 log.info("Old Value of 'exampleVar': " + oldValue); // 设置新的 variableValue 给 exampleVar String newValue = "new_value"; vars.put("exampleVar", newValue); ``` 这段脚本首先尝试从 `vars` 集合中检索已存在的 `"exampleVar"` 变量,并打印其原始作为日志记录的一部分。接着,将一个新的字符串 `"new_value"` 存储回同一个位置覆盖之前的。 对于希望在整个测试计划范围内共享的数据(即跨多个线程),应该考虑使用属性而不是本地变量。可以通过如下方式完成此操作: ```java // 将 thread-specific 属性提升至全局级别 props.setProperty("globalExampleProp", "globallyAccessibleValue"); ``` 这里展示了怎样利用 `${__setProperty}` 函数配合 BeanShell 实现相同的效果[^5]: ```java bsh.args[0]="cookie_token"; // 参数传递给 __setProperty 函数的第一个参数名 bsh.args[1]=vars.get("token"); // 第二个参数是要保存的实际数据 ${__setProperty(bsh.args[0], bsh.args[1])}; ``` 上述例子说明了当需要把某些信息暴露成全局可用资源时所采取的做法——先捕获目标数据存入临时变量里,再经由内置函数将其转换为持久化形式供后续环节引用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值