高手的存在,就是让服务10亿人的时候,你感觉只是为你一个人服务......

NGrinder 脚本不执行finally

目录

try..catch..finally,一般情况下fianly代码块中的代码一定会执行。

但finally也有不执行的情况

1
System.exit(0);

System.exit()的作用是中止当前虚拟机,虚拟机都被中止了,finally代码块自然不会执行。


最近写了个NGrinder脚本,未用到System.exit(),在try..catch..finally的时候,finally代码块竟然没有执行,让人费解。

先来看一段简单的Junit测试代码的执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.junit.Test;

import static org.junit.Assert.assertTrue;

public class JunitTest {

@Test
public void test() {
int i = 0;
try {
System.out.println("false");
assertTrue(false);
} catch (Exception e) {
System.out.println("catch");

} finally {
System.out.println("finally");
}
}
}

执行结果:

1
2
false
finally

抛出的异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:86)
at org.junit.Assert.assertTrue(Assert.java:41)
at org.junit.Assert.assertTrue(Assert.java:52)
at JunitTest.test(JunitTest.java:11)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Junit的执行流程参考Junit4的执行流程

assertTrue(false)案例执行失败的时候,finally代码块理所当然的执行了。

再先来看一段简单的Ngrinder测试代码的执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import org.junit.Test;
import org.junit.runner.RunWith;
import net.grinder.script.GTest;
import net.grinder.script.NonInstrumentableTypeException;
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess;
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread;

import static org.junit.Assert.*;


@RunWith(net.grinder.scriptengine.groovy.junit.GrinderRunner.class)
public class NgrinderTest {
public static GTest test; // 定义事务对象test

@BeforeProcess
public static void beforeProcess() { // 在BeforeProcess方法体内进行事务初始化定义
test = new GTest(1, "ngrinder test"); // 1代表执行顺利,中文字符代表事务名称
}

@BeforeThread
public void beforeThread() throws NonInstrumentableTypeException { // 在BeforeThread方法体内进行事务注册
test.record(this, "test"); // 事件注册,名称要和下面定义的方法名称一致
}

@Test
public void test() {
try {
System.out.println("false");
assertTrue(false);
} catch (Exception e) {
System.out.println("catch");
} finally {
System.out.println("finally");
}
}
}

执行结果:

1
false

抛出的异常:

1
2
3
4
5
6
7
8
9
10
java.lang.AssertionError
at NgrinderTest.test(NgrinderTest.java:29)
at net.grinder.scriptengine.groovy.junit.RepetitionStatement.evaluate(RepetitionStatement.java:67)
at net.grinder.scriptengine.groovy.junit.RunBeforeThreads.evaluate(RunBeforeThreads.java:62)
at net.grinder.scriptengine.groovy.junit.RunAfterThreads.evaluate(RunAfterThreads.java:56)
at net.grinder.scriptengine.groovy.junit.GrinderRunner.run(GrinderRunner.java:170)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

奇怪的事情出现了,assertTrue(false)案例执行失败的时候,finally代码块没有执行!!

当然,如果assertTrue(true)案例执行成功的时候,finally代码块依然会执行。


遂进行debug,最终发现finally不执行的关键在于:

1
test.record(this, "test"); // 事件注册,名称要和下面定义的方法名称一致

record方法中会通过ASM,在test方法的入口和出口分别注入enter,exit方法。

enter,exit方法在RecorderLocator类中实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static void enter(Object target, String location) {
if (target != null) {
try {
Iterator i$ = s_instance.getRecorderList(target, location).iterator();

while(i$.hasNext()) {
Recorder recorder = (Recorder)i$.next();
recorder.start();
}

} catch (EngineException var4) {
throw new RecorderLocator.RecordingFailureException(var4);
}
}
}

public static void exit(Object target, String location, boolean success) {
if (target != null) {
List<Recorder> recorders = s_instance.getRecorderList(target, location);![Alt text](./enter.PNG)

ListIterator i = recorders.listIterator(recorders.size());

try {
while(i.hasPrevious()) {
Recorder recorder = (Recorder)i.previous();
recorder.end(success);
}

} catch (EngineException var6) {
throw new RecorderLocator.RecordingFailureException(var6);
}
}
}

当执行到assertTrue(false)的时候,直接进入exit方法结束执行流程。

所以,于人于己,在写NGrinder脚本的时候少用finally,避免采坑。