Răsfoiți Sursa

feature #I7YYLE 完善测试用例

Dale Lee 1 an în urmă
părinte
comite
c422761aef
74 a modificat fișierele cu 2395 adăugiri și 14 ștergeri
  1. 2 6
      liteflow-core/src/main/java/com/yomahub/liteflow/annotation/FallbackCmp.java
  2. 2 1
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/NodeOperator.java
  3. 1 0
      liteflow-core/src/main/java/com/yomahub/liteflow/exception/FallbackCmpNotFoundException.java
  4. 0 3
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java
  5. 1 1
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/FallbackNodeProxy.java
  6. 1 1
      liteflow-solon-plugin/src/main/resources/META-INF/liteflow-default.properties
  7. 225 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELDeclSpringbootTest.java
  8. 16 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java
  9. 16 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java
  10. 20 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java
  11. 18 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java
  12. 15 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java
  13. 20 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java
  14. 18 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java
  15. 20 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java
  16. 21 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java
  17. 23 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java
  18. 18 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java
  19. 20 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java
  20. 31 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java
  21. 20 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java
  22. 2 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/fallback/application.properties
  23. 136 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/fallback/flow.el.xml
  24. 225 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/FallbackTest.java
  25. 12 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java
  26. 12 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java
  27. 13 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java
  28. 14 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java
  29. 11 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java
  30. 13 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java
  31. 11 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java
  32. 13 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java
  33. 14 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java
  34. 16 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java
  35. 11 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java
  36. 13 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java
  37. 24 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java
  38. 13 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java
  39. 153 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/fallback/flow.el.xml
  40. 222 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSolonTest.java
  41. 14 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java
  42. 14 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java
  43. 15 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java
  44. 16 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java
  45. 13 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java
  46. 15 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java
  47. 13 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java
  48. 15 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java
  49. 16 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java
  50. 18 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java
  51. 13 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java
  52. 15 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java
  53. 26 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java
  54. 15 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java
  55. 2 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/fallback/application.properties
  56. 136 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/fallback/flow.el.xml
  57. 4 2
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSpringbootTest.java
  58. 221 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSpringTest.java
  59. 14 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java
  60. 14 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java
  61. 15 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java
  62. 16 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java
  63. 13 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java
  64. 15 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java
  65. 13 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java
  66. 15 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java
  67. 16 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java
  68. 18 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java
  69. 13 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java
  70. 15 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java
  71. 26 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java
  72. 15 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java
  73. 24 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/fallback/application.xml
  74. 136 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/fallback/flow.el.xml

+ 2 - 6
liteflow-core/src/main/java/com/yomahub/liteflow/annotation/FallbackCmp.java

@@ -11,17 +11,13 @@ import java.lang.annotation.Target;
 
 /**
  * 降级组件
+ *
  * @author DaleLee
+ * @since 2.11.1
  */
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Inherited
 public @interface FallbackCmp {
-    
-    /**
-     * 节点类型
-     * @return NodeTypeEnum
-     */
-    NodeTypeEnum type() default NodeTypeEnum.COMMON;
 }

+ 2 - 1
liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/NodeOperator.java

@@ -12,7 +12,8 @@ import com.yomahub.liteflow.property.LiteflowConfigGetter;
 /**
  * EL规则中的node的操作符
  *
- * @author Bryan.Zhang,DaleLee
+ * @author Bryan.Zhang
+ * @author DaleLee
  * @since 2.8.3
  */
 public class NodeOperator extends BaseOperator<Node> {

+ 1 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/exception/FallbackCmpNotFoundException.java

@@ -4,6 +4,7 @@ package com.yomahub.liteflow.exception;
  * 没有找到降级组件异常
  *
  * @author DaleLee
+ * @since 2.11.1
  */
 public class FallbackCmpNotFoundException extends RuntimeException {
 

+ 0 - 3
liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java

@@ -294,9 +294,6 @@ public class FlowBus {
 		}
 
 		NodeTypeEnum nodeType = node.getType();
-		if (nodeType == null) {
-			nodeType = fallbackCmp.type();
-		}
 		fallbackNodeMap.put(nodeType, node);
 	}
 

+ 1 - 1
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/FallbackNodeProxy.java

@@ -21,7 +21,7 @@ import com.yomahub.liteflow.slot.Slot;
  * 降级组件代理
  *
  * @author DaleLee
- * @since
+ * @since 2.11.1
  */
 public class FallbackNodeProxy extends Node {
 

+ 1 - 1
liteflow-solon-plugin/src/main/resources/META-INF/liteflow-default.properties

@@ -13,8 +13,8 @@ liteflow.retry-count=0
 liteflow.support-multiple-type=false
 liteflow.node-executor-class=com.yomahub.liteflow.flow.executor.DefaultNodeExecutor
 liteflow.print-execution-log=true
-liteflow.substitute-cmp-class=
 liteflow.monitor.enable-log=false
 liteflow.monitor.queue-limit=200
 liteflow.monitor.delay=300000
 liteflow.monitor.period=300000
+liteflow.fallback-cmp-enable=false

+ 225 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELDeclSpringbootTest.java

@@ -0,0 +1,225 @@
+package com.yomahub.liteflow.test.fallback;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+
+import javax.annotation.Resource;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * SpringBoot 降级组件测试
+ *
+ * @author DaleLee
+ * @since 2.11.1
+ */
+@TestPropertySource(value = "classpath:/fallback/application.properties")
+@SpringBootTest(classes = FallbackELDeclSpringbootTest.class)
+@EnableAutoConfiguration
+@ComponentScan({"com.yomahub.liteflow.test.fallback.cmp"})
+public class FallbackELDeclSpringbootTest extends BaseTest {
+
+    @Resource
+    private FlowExecutor flowExecutor;
+
+    @Test
+    public void testThen1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("then1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testThen2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("then2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhen1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("when1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String executeStepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("b==>c".equals(executeStepStr) || "c==>b".equals(executeStepStr));
+    }
+
+    @Test
+    public void testIf1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("if1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIf2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("if2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn1==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testFor1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("for1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("for1==>a==>a==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testFor2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("for2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("LOOP_3==>c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhile1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("while1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhile2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("while2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn1==>c==>wn1==>c==>wn1==>c==>wn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIterator1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("iterator1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIterator2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("iterator2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn1==>c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("LOOP_3==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn1==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak3() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break3", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn1==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testSwitch1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("switch1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("swn2==>b", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testSwitch2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("switch2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("swn1==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testAnd1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("and1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testOr1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("or1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>ifn1==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testNot1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("not1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testCatch1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("catch1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>d==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>c==>ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>ifn1==>a==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti3() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi3", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("for1==>b==>c==>b==>c==>b==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testConcurrent1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("concurrent1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String stepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr));
+    }
+
+    @Test
+    public void testConcurrent2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("concurrent2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String stepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr));
+    }
+
+    @Test
+    public void testConcurrent3() throws ExecutionException, InterruptedException {
+        // 执行多条 chain
+        Future<LiteflowResponse> future1 = flowExecutor.execute2Future("concurrent1", "arg", new Object());
+        Future<LiteflowResponse> future2 = flowExecutor.execute2Future("concurrent2", "arg", new Object());
+        Thread.sleep(1000);
+        LiteflowResponse response1 = future1.get();
+        LiteflowResponse response2 = future2.get();
+        Assertions.assertTrue(response1.isSuccess());
+        String stepStr1 = response1.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr1) || "ifn2==>c".equals(stepStr1));
+        Assertions.assertTrue(response2.isSuccess());
+        String stepStr2 = response2.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr2) || "ifn2==>c".equals(stepStr2));
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+
+@LiteflowComponent("a")
+public class ACmp {
+
+    @LiteflowMethod(LiteFlowMethodEnum.PROCESS)
+    public void process(NodeComponent bindCmp) {
+        System.out.println("ACmp executed!");
+    }
+
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+
+@LiteflowComponent("b")
+public class BCmp {
+
+    @LiteflowMethod(LiteFlowMethodEnum.PROCESS)
+    public void process(NodeComponent bindCmp) {
+        System.out.println("BCmp executed!");
+    }
+
+}

+ 20 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java

@@ -0,0 +1,20 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+
+@LiteflowComponent("bn1")
+@LiteflowCmpDefine(NodeTypeEnum.BREAK)
+@FallbackCmp
+public class BreakCmp {
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_BREAK)
+    public boolean processBreak(NodeComponent bindCmp) throws Exception {
+        return true;
+    }
+}

+ 18 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java

@@ -0,0 +1,18 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+
+@LiteflowComponent("c")
+@FallbackCmp
+public class CCmp {
+
+    @LiteflowMethod(LiteFlowMethodEnum.PROCESS)
+    public void process(NodeComponent bindCmp) {
+        System.out.println("CCmp executed!");
+    }
+
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+
+@LiteflowComponent("d")
+public class DCmp {
+
+    @LiteflowMethod(LiteFlowMethodEnum.PROCESS)
+    public void process(NodeComponent bindCmp) throws Exception {
+        throw new RuntimeException("component[d]");
+    }
+}

+ 20 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java

@@ -0,0 +1,20 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+
+@LiteflowComponent("for1")
+@LiteflowCmpDefine(NodeTypeEnum.FOR)
+@FallbackCmp
+public class ForCmp {
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_FOR)
+    public int processFor(NodeComponent bindCmp) throws Exception {
+        return 3;
+    }
+}

+ 18 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java

@@ -0,0 +1,18 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+
+@LiteflowComponent("ifn1")
+@LiteflowCmpDefine(NodeTypeEnum.IF)
+public class IfCmp1 {
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_IF)
+    public boolean processIf(NodeComponent bindCmp) throws Exception {
+        return true;
+    }
+}

+ 20 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java

@@ -0,0 +1,20 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+
+@LiteflowComponent("ifn2")
+@LiteflowCmpDefine(NodeTypeEnum.IF)
+@FallbackCmp
+public class IfCmp2 {
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_IF)
+    public boolean processIf(NodeComponent bindCmp) throws Exception {
+        return false;
+    }
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java

@@ -0,0 +1,21 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+@LiteflowComponent("itn1")
+@LiteflowCmpDefine(NodeTypeEnum.ITERATOR)
+public class IteratorCmp1 {
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_ITERATOR)
+    public Iterator<?> processIterator(NodeComponent bindCmp) throws Exception {
+        return Arrays.asList("a", "b", "c").iterator();
+    }
+}

+ 23 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java

@@ -0,0 +1,23 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+@LiteflowComponent("itn2")
+@LiteflowCmpDefine(NodeTypeEnum.ITERATOR)
+@FallbackCmp
+public class IteratorCmp2 {
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_ITERATOR)
+    public Iterator<?> processIterator(NodeComponent bindCmp) throws Exception {
+        return Collections.emptyIterator();
+    }
+}

+ 18 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java

@@ -0,0 +1,18 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+
+@LiteflowComponent("swn1")
+@LiteflowCmpDefine(NodeTypeEnum.SWITCH)
+public class SwitchCmp1 {
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH)
+    public String processSwitch(NodeComponent bindCmp) throws Exception {
+        return "a";
+    }
+}

+ 20 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java

@@ -0,0 +1,20 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+
+@LiteflowComponent("swn2")
+@LiteflowCmpDefine(NodeTypeEnum.SWITCH)
+@FallbackCmp
+public class SwitchCmp2 {
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH)
+    public String processSwitch(NodeComponent bindCmp) throws Exception {
+        return "b";
+    }
+}

+ 31 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java

@@ -0,0 +1,31 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@LiteflowComponent("wn1")
+@LiteflowCmpDefine(NodeTypeEnum.WHILE)
+public class WhileCmp1 {
+    private int count = 0;
+
+    // 执行过的 chain
+    Set<String> executedChain = new HashSet<>();
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_WHILE)
+    public boolean processWhile(NodeComponent bindCmp) throws Exception {
+        // 判断是否切换了 chain
+        if (!executedChain.contains(bindCmp.getCurrChainId())) {
+            count = 0;
+            executedChain.add(bindCmp.getCurrChainId());
+        }
+        count++;
+        return count <= 3;
+    }
+}

+ 20 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java

@@ -0,0 +1,20 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+
+@LiteflowComponent("wn2")
+@LiteflowCmpDefine(NodeTypeEnum.WHILE)
+@FallbackCmp
+public class WhileCmp2 {
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_WHILE)
+    public boolean processWhile(NodeComponent bindCmp) throws Exception {
+        return false;
+    }
+}

+ 2 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/fallback/application.properties

@@ -0,0 +1,2 @@
+liteflow.rule-source=fallback/flow.el.xml
+liteflow.fallback-cmp-enable=true

+ 136 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/fallback/flow.el.xml

@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <!-- THEN 普通组件降级 -->
+    <chain name="then1">
+        THEN(a, node("x"));
+    </chain>
+    
+    <chain name="then2">
+        THEN(PRE(node("x1")), node("x2"), FINALLY(node("x3")));
+    </chain>
+
+    <!-- WHEN 普通组件降级 -->
+    <chain name="when1">
+        WHEN(b, node("x"));
+    </chain>
+
+    <!-- IF 条件组件降级 -->
+    <chain name="if1">
+        IF(node("x"), a)
+    </chain>
+    
+    <!-- IF 普通组件降级 -->
+    <chain name="if2">
+        IF(ifn1, node("x"))
+    </chain>
+    
+    <!-- FOR 次数循环组件降级 -->
+    <chain name="for1">
+        FOR(node("x")).DO(a);
+    </chain>
+    
+    <!-- FOR 普通组件降级 -->
+    <chain name="for2">
+        FOR(3).DO(node("x"));
+    </chain>
+    
+    <!-- WHILE 条件循环组件降级 -->
+    <chain name="while1">
+        WHILE(node("x")).DO(a)
+    </chain>
+    
+    <!-- WHILE 普通组件降级 -->
+    <chain name="while2">
+        WHILE(wn1).DO(node("x"))
+    </chain>
+    
+    <!-- ITERATOR 迭代组件降级 -->
+    <chain name="iterator1">
+        ITERATOR(node("x")).DO(a)
+    </chain>
+    
+    <!-- ITERATOR 普通组件降级 -->
+    <chain name="iterator2">
+        ITERATOR(itn1).DO(node("x"))
+    </chain>
+    
+    <!-- BREAK 退出循环组件降级 -->
+    <chain name="break1">
+        FOR(3).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <chain name="break2">
+        WHILE(wn1).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <chain name="break3">
+        ITERATOR(itn1).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <!-- SWITCH 选择组件降级 -->
+    <chain name="switch1">
+        SWITCH(node("x")).to(a,b);
+    </chain>
+    
+    <!-- SWITCH 普通组件降级 -->
+    <chain name="switch2">
+        SWITCH(swn1).to(node("x"),a);
+    </chain>
+    
+    <!-- AND 条件组件降级 -->
+    <chain name="and1">
+        IF(AND(node("x"),ifn1), a);
+    </chain>
+    
+    <!-- OR 条件组件降级 -->
+    <chain name="or1">
+        IF(OR(node("x"),ifn1), a);
+    </chain>
+    
+    <!-- NOT 条件组件降级 -->
+    <chain name="not1">
+        IF(NOT(node("x")), a);
+    </chain>
+    
+    <!-- CATCH 普通组件降级 -->
+    <chain name="catch1">
+            CATCH(THEN(a, d)).DO(node("x"))
+    </chain>
+    
+    <!-- 多个组件降级 -->
+    <chain name="multi1">
+        THEN(
+            a,
+            node("x1"),
+            IF(node("x2"), b)
+        );
+    </chain>
+    
+    <chain name="multi2">
+        IF(
+            OR(node("x1"), ifn1),
+            THEN(a, node("x2"))
+        );
+    </chain>
+    
+    <chain name="multi3">
+        FOR(node("x1")).DO(
+            THEN(b, node("x2"))
+        );
+    </chain>
+
+    <!-- 并发降级测试 -->
+    <chain name="concurrent1">
+        WHEN(
+            THEN(node("x1")),
+            IF(node("x2"), b)
+        ).maxWaitSeconds(10000);
+    </chain>
+
+    <chain name="concurrent2">
+        WHEN(
+            node("x1"),
+            IF(node("x2"), b)
+        ).maxWaitSeconds(10000);
+    </chain>
+</flow>

+ 225 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/FallbackTest.java

@@ -0,0 +1,225 @@
+package com.yomahub.liteflow.test.fallback;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.core.FlowExecutorHolder;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.property.LiteflowConfig;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * 非 Spring 环境下组件降级测试
+ *
+ * @author DaleLee
+ * @since 2.11.1
+ */
+public class FallbackTest extends BaseTest {
+    private static FlowExecutor flowExecutor;
+
+    @BeforeAll
+    public static void init() {
+        LiteflowConfig config = new LiteflowConfig();
+        config.setRuleSource("fallback/flow.el.xml");
+        config.setFallbackCmpEnable(true);
+        flowExecutor = FlowExecutorHolder.loadInstance(config);
+    }
+
+    @Test
+    public void testThen1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("then1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testThen2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("then2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhen1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("when1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String executeStepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("b==>c".equals(executeStepStr) || "c==>b".equals(executeStepStr));
+    }
+
+    @Test
+    public void testIf1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("if1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIf2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("if2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn1==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testFor1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("for1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("for1==>a==>a==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testFor2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("for2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("LOOP_3==>c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhile1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("while1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhile2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("while2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn1==>c==>wn1==>c==>wn1==>c==>wn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIterator1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("iterator1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIterator2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("iterator2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn1==>c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("LOOP_3==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn1==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak3() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break3", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn1==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testSwitch1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("switch1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("swn2==>b", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testSwitch2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("switch2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("swn1==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testAnd1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("and1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testOr1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("or1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>ifn1==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testNot1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("not1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testCatch1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("catch1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>d==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>c==>ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>ifn1==>a==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti3() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi3", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("for1==>b==>c==>b==>c==>b==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testConcurrent1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("concurrent1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String stepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr));
+    }
+
+    @Test
+    public void testConcurrent2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("concurrent2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String stepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr));
+    }
+
+    @Test
+    public void testConcurrent3() throws ExecutionException, InterruptedException {
+        // 执行多条 chain
+        Future<LiteflowResponse> future1 = flowExecutor.execute2Future("concurrent1", "arg", new Object());
+        Future<LiteflowResponse> future2 = flowExecutor.execute2Future("concurrent2", "arg", new Object());
+        Thread.sleep(1000);
+        LiteflowResponse response1 = future1.get();
+        LiteflowResponse response2 = future2.get();
+        Assertions.assertTrue(response1.isSuccess());
+        String stepStr1 = response1.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr1) || "ifn2==>c".equals(stepStr1));
+        Assertions.assertTrue(response2.isSuccess());
+        String stepStr2 = response2.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr2) || "ifn2==>c".equals(stepStr2));
+    }
+}

+ 12 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java

@@ -0,0 +1,12 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+
+public class ACmp extends NodeComponent {
+
+    @Override
+    public void process() {
+        System.out.println("ACmp executed!");
+    }
+
+}

+ 12 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java

@@ -0,0 +1,12 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+
+public class BCmp extends NodeComponent {
+
+    @Override
+    public void process() {
+        System.out.println("BCmp executed!");
+    }
+
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.core.NodeBreakComponent;
+
+@FallbackCmp
+public class BreakCmp extends NodeBreakComponent {
+
+    @Override
+    public boolean processBreak() throws Exception {
+        return true;
+    }
+}

+ 14 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java

@@ -0,0 +1,14 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.core.NodeComponent;
+
+@FallbackCmp
+public class CCmp extends NodeComponent {
+
+    @Override
+    public void process() {
+        System.out.println("CCmp executed!");
+    }
+
+}

+ 11 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java

@@ -0,0 +1,11 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+
+public class DCmp extends NodeComponent {
+
+    @Override
+    public void process() throws Exception {
+        throw new RuntimeException("component[d]");
+    }
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.core.NodeForComponent;
+
+@FallbackCmp
+public class ForCmp extends NodeForComponent {
+
+    @Override
+    public int processFor() throws Exception {
+        return 3;
+    }
+}

+ 11 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java

@@ -0,0 +1,11 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.core.NodeIfComponent;
+
+public class IfCmp1 extends NodeIfComponent {
+
+    @Override
+    public boolean processIf() throws Exception {
+        return true;
+    }
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.core.NodeIfComponent;
+
+@FallbackCmp
+public class IfCmp2 extends NodeIfComponent {
+
+    @Override
+    public boolean processIf() throws Exception {
+        return false;
+    }
+}

+ 14 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java

@@ -0,0 +1,14 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+public class IteratorCmp1 extends NodeIteratorComponent {
+
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        return Arrays.asList("a", "b", "c").iterator();
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+@FallbackCmp
+public class IteratorCmp2 extends NodeIteratorComponent {
+
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        return Collections.emptyIterator();
+    }
+}

+ 11 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java

@@ -0,0 +1,11 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+
+public class SwitchCmp1 extends NodeSwitchComponent {
+
+    @Override
+    public String processSwitch() throws Exception {
+        return "a";
+    }
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+
+@FallbackCmp
+public class SwitchCmp2 extends NodeSwitchComponent {
+
+    @Override
+    public String processSwitch() throws Exception {
+        return "b";
+    }
+}

+ 24 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java

@@ -0,0 +1,24 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.core.NodeWhileComponent;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class WhileCmp1 extends NodeWhileComponent {
+    private int count = 0;
+
+    // 执行过的 chain
+    Set<String> executedChain = new HashSet<>();
+
+    @Override
+    public boolean processWhile() throws Exception {
+        // 判断是否切换了 chain
+        if (!executedChain.contains(this.getCurrChainId())) {
+            count = 0;
+            executedChain.add(this.getCurrChainId());
+        }
+        count++;
+        return count <= 3;
+    }
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.core.NodeWhileComponent;
+
+@FallbackCmp
+public class WhileCmp2 extends NodeWhileComponent {
+    
+    @Override
+    public boolean processWhile() throws Exception {
+        return false;
+    }
+}

+ 153 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/fallback/flow.el.xml

@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <nodes>
+        <node id = "a" class="com.yomahub.liteflow.test.fallback.cmp.ACmp"/>
+        <node id = "b" class="com.yomahub.liteflow.test.fallback.cmp.BCmp"/>
+        <node id = "c" class="com.yomahub.liteflow.test.fallback.cmp.CCmp"/>
+        <node id = "d" class="com.yomahub.liteflow.test.fallback.cmp.DCmp"/>
+        <node id = "ifn1" class="com.yomahub.liteflow.test.fallback.cmp.IfCmp1"/>
+        <node id = "ifn2" class="com.yomahub.liteflow.test.fallback.cmp.IfCmp2"/>
+        <node id = "swn1" class="com.yomahub.liteflow.test.fallback.cmp.SwitchCmp1"/>
+        <node id = "swn2" class="com.yomahub.liteflow.test.fallback.cmp.SwitchCmp2"/>
+        <node id = "for1" class="com.yomahub.liteflow.test.fallback.cmp.ForCmp"/>
+        <node id = "wn1" class="com.yomahub.liteflow.test.fallback.cmp.WhileCmp1"/>
+        <node id = "wn2" class="com.yomahub.liteflow.test.fallback.cmp.WhileCmp2"/>
+        <node id = "itn1" class="com.yomahub.liteflow.test.fallback.cmp.IteratorCmp1"/>
+        <node id = "itn2" class="com.yomahub.liteflow.test.fallback.cmp.IteratorCmp2"/>
+        <node id = "bn1" class="com.yomahub.liteflow.test.fallback.cmp.BreakCmp"/>
+    </nodes>
+
+    <!-- THEN 普通组件降级 -->
+    <chain name="then1">
+        THEN(a, node("x"));
+    </chain>
+    
+    <chain name="then2">
+        THEN(PRE(node("x1")), node("x2"), FINALLY(node("x3")));
+    </chain>
+
+    <!-- WHEN 普通组件降级 -->
+    <chain name="when1">
+        WHEN(b, node("x"));
+    </chain>
+
+    <!-- IF 条件组件降级 -->
+    <chain name="if1">
+        IF(node("x"), a)
+    </chain>
+    
+    <!-- IF 普通组件降级 -->
+    <chain name="if2">
+        IF(ifn1, node("x"))
+    </chain>
+    
+    <!-- FOR 次数循环组件降级 -->
+    <chain name="for1">
+        FOR(node("x")).DO(a);
+    </chain>
+    
+    <!-- FOR 普通组件降级 -->
+    <chain name="for2">
+        FOR(3).DO(node("x"));
+    </chain>
+    
+    <!-- WHILE 条件循环组件降级 -->
+    <chain name="while1">
+        WHILE(node("x")).DO(a)
+    </chain>
+    
+    <!-- WHILE 普通组件降级 -->
+    <chain name="while2">
+        WHILE(wn1).DO(node("x"))
+    </chain>
+    
+    <!-- ITERATOR 迭代组件降级 -->
+    <chain name="iterator1">
+        ITERATOR(node("x")).DO(a)
+    </chain>
+    
+    <!-- ITERATOR 普通组件降级 -->
+    <chain name="iterator2">
+        ITERATOR(itn1).DO(node("x"))
+    </chain>
+    
+    <!-- BREAK 退出循环组件降级 -->
+    <chain name="break1">
+        FOR(3).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <chain name="break2">
+        WHILE(wn1).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <chain name="break3">
+        ITERATOR(itn1).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <!-- SWITCH 选择组件降级 -->
+    <chain name="switch1">
+        SWITCH(node("x")).to(a,b);
+    </chain>
+    
+    <!-- SWITCH 普通组件降级 -->
+    <chain name="switch2">
+        SWITCH(swn1).to(node("x"),a);
+    </chain>
+    
+    <!-- AND 条件组件降级 -->
+    <chain name="and1">
+        IF(AND(node("x"),ifn1), a);
+    </chain>
+    
+    <!-- OR 条件组件降级 -->
+    <chain name="or1">
+        IF(OR(node("x"),ifn1), a);
+    </chain>
+    
+    <!-- NOT 条件组件降级 -->
+    <chain name="not1">
+        IF(NOT(node("x")), a);
+    </chain>
+    
+    <!-- CATCH 普通组件降级 -->
+    <chain name="catch1">
+            CATCH(THEN(a, d)).DO(node("x"))
+    </chain>
+    
+    <!-- 多个组件降级 -->
+    <chain name="multi1">
+        THEN(
+            a,
+            node("x1"),
+            IF(node("x2"), b)
+        );
+    </chain>
+    
+    <chain name="multi2">
+        IF(
+            OR(node("x1"), ifn1),
+            THEN(a, node("x2"))
+        );
+    </chain>
+    
+    <chain name="multi3">
+        FOR(node("x1")).DO(
+            THEN(b, node("x2"))
+        );
+    </chain>
+
+    <!-- 并发降级测试 -->
+    <chain name="concurrent1">
+        WHEN(
+            THEN(node("x1")),
+            IF(node("x2"), b)
+        ).maxWaitSeconds(10000);
+    </chain>
+
+    <chain name="concurrent2">
+        WHEN(
+            node("x1"),
+            IF(node("x2"), b)
+        ).maxWaitSeconds(10000);
+    </chain>
+</flow>

+ 222 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSolonTest.java

@@ -0,0 +1,222 @@
+package com.yomahub.liteflow.test.fallback;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.noear.solon.annotation.Inject;
+import org.noear.solon.test.SolonJUnit5Extension;
+import org.noear.solon.test.annotation.TestPropertySource;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * Solon 降级组件测试
+ *
+ * @author DaleLee
+ * @since 2.11.1
+ */
+@ExtendWith(SolonJUnit5Extension.class)
+@TestPropertySource("classpath:/fallback/application.properties")
+public class FallbackELSolonTest extends BaseTest {
+
+    @Inject
+    private FlowExecutor flowExecutor;
+
+    @Test
+    public void testThen1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("then1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testThen2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("then2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhen1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("when1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String executeStepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("b==>c".equals(executeStepStr) || "c==>b".equals(executeStepStr));
+    }
+
+    @Test
+    public void testIf1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("if1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIf2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("if2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn1==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testFor1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("for1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("for1==>a==>a==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testFor2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("for2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("LOOP_3==>c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhile1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("while1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhile2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("while2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn1==>c==>wn1==>c==>wn1==>c==>wn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIterator1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("iterator1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIterator2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("iterator2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn1==>c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("LOOP_3==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn1==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak3() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break3", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn1==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testSwitch1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("switch1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("swn2==>b", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testSwitch2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("switch2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("swn1==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testAnd1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("and1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testOr1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("or1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>ifn1==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testNot1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("not1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testCatch1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("catch1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>d==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>c==>ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>ifn1==>a==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti3() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi3", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("for1==>b==>c==>b==>c==>b==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testConcurrent1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("concurrent1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String stepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr));
+    }
+
+    @Test
+    public void testConcurrent2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("concurrent2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String stepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr));
+    }
+
+    @Test
+    public void testConcurrent3() throws ExecutionException, InterruptedException {
+        // 执行多条 chain
+        Future<LiteflowResponse> future1 = flowExecutor.execute2Future("concurrent1", "arg", new Object());
+        Future<LiteflowResponse> future2 = flowExecutor.execute2Future("concurrent2", "arg", new Object());
+        Thread.sleep(1000);
+        LiteflowResponse response1 = future1.get();
+        LiteflowResponse response2 = future2.get();
+        Assertions.assertTrue(response1.isSuccess());
+        String stepStr1 = response1.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr1) || "ifn2==>c".equals(stepStr1));
+        Assertions.assertTrue(response2.isSuccess());
+        String stepStr2 = response2.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr2) || "ifn2==>c".equals(stepStr2));
+    }
+}

+ 14 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java

@@ -0,0 +1,14 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeComponent;
+
+@LiteflowComponent("a")
+public class ACmp extends NodeComponent {
+
+    @Override
+    public void process() {
+        System.out.println("ACmp executed!");
+    }
+
+}

+ 14 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java

@@ -0,0 +1,14 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeComponent;
+
+@LiteflowComponent("b")
+public class BCmp extends NodeComponent {
+
+    @Override
+    public void process() {
+        System.out.println("BCmp executed!");
+    }
+
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeBreakComponent;
+
+@LiteflowComponent("bn1")
+@FallbackCmp
+public class BreakCmp extends NodeBreakComponent {
+
+    @Override
+    public boolean processBreak() throws Exception {
+        return true;
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeComponent;
+
+@LiteflowComponent("c")
+@FallbackCmp
+public class CCmp extends NodeComponent {
+
+    @Override
+    public void process() {
+        System.out.println("CCmp executed!");
+    }
+
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeComponent;
+
+@LiteflowComponent("d")
+public class DCmp extends NodeComponent {
+
+    @Override
+    public void process() throws Exception {
+        throw new RuntimeException("component[d]");
+    }
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeForComponent;
+
+@LiteflowComponent("for1")
+@FallbackCmp
+public class ForCmp extends NodeForComponent {
+
+    @Override
+    public int processFor() throws Exception {
+        return 3;
+    }
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeIfComponent;
+
+@LiteflowComponent("ifn1")
+public class IfCmp1 extends NodeIfComponent {
+
+    @Override
+    public boolean processIf() throws Exception {
+        return true;
+    }
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeIfComponent;
+
+@LiteflowComponent("ifn2")
+@FallbackCmp
+public class IfCmp2 extends NodeIfComponent {
+
+    @Override
+    public boolean processIf() throws Exception {
+        return false;
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+@LiteflowComponent("itn1")
+public class IteratorCmp1 extends NodeIteratorComponent {
+
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        return Arrays.asList("a", "b", "c").iterator();
+    }
+}

+ 18 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java

@@ -0,0 +1,18 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+@LiteflowComponent("itn2")
+@FallbackCmp
+public class IteratorCmp2 extends NodeIteratorComponent {
+
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        return Collections.emptyIterator();
+    }
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+
+@LiteflowComponent("swn1")
+public class SwitchCmp1 extends NodeSwitchComponent {
+
+    @Override
+    public String processSwitch() throws Exception {
+        return "a";
+    }
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+
+@LiteflowComponent("swn2")
+@FallbackCmp
+public class SwitchCmp2 extends NodeSwitchComponent {
+
+    @Override
+    public String processSwitch() throws Exception {
+        return "b";
+    }
+}

+ 26 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java

@@ -0,0 +1,26 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeWhileComponent;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@LiteflowComponent("wn1")
+public class WhileCmp1 extends NodeWhileComponent {
+    private int count = 0;
+
+    // 执行过的 chain
+    Set<String> executedChain = new HashSet<>();
+
+    @Override
+    public boolean processWhile() throws Exception {
+        // 判断是否切换了 chain
+        if (!executedChain.contains(this.getCurrChainId())) {
+            count = 0;
+            executedChain.add(this.getCurrChainId());
+        }
+        count++;
+        return count <= 3;
+    }
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeWhileComponent;
+
+@LiteflowComponent("wn2")
+@FallbackCmp
+public class WhileCmp2 extends NodeWhileComponent {
+    
+    @Override
+    public boolean processWhile() throws Exception {
+        return false;
+    }
+}

+ 2 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/fallback/application.properties

@@ -0,0 +1,2 @@
+liteflow.rule-source=fallback/flow.el.xml
+liteflow.fallback-cmp-enable=true

+ 136 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/fallback/flow.el.xml

@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <!-- THEN 普通组件降级 -->
+    <chain name="then1">
+        THEN(a, node("x"));
+    </chain>
+    
+    <chain name="then2">
+        THEN(PRE(node("x1")), node("x2"), FINALLY(node("x3")));
+    </chain>
+
+    <!-- WHEN 普通组件降级 -->
+    <chain name="when1">
+        WHEN(b, node("x"));
+    </chain>
+
+    <!-- IF 条件组件降级 -->
+    <chain name="if1">
+        IF(node("x"), a)
+    </chain>
+    
+    <!-- IF 普通组件降级 -->
+    <chain name="if2">
+        IF(ifn1, node("x"))
+    </chain>
+    
+    <!-- FOR 次数循环组件降级 -->
+    <chain name="for1">
+        FOR(node("x")).DO(a);
+    </chain>
+    
+    <!-- FOR 普通组件降级 -->
+    <chain name="for2">
+        FOR(3).DO(node("x"));
+    </chain>
+    
+    <!-- WHILE 条件循环组件降级 -->
+    <chain name="while1">
+        WHILE(node("x")).DO(a)
+    </chain>
+    
+    <!-- WHILE 普通组件降级 -->
+    <chain name="while2">
+        WHILE(wn1).DO(node("x"))
+    </chain>
+    
+    <!-- ITERATOR 迭代组件降级 -->
+    <chain name="iterator1">
+        ITERATOR(node("x")).DO(a)
+    </chain>
+    
+    <!-- ITERATOR 普通组件降级 -->
+    <chain name="iterator2">
+        ITERATOR(itn1).DO(node("x"))
+    </chain>
+    
+    <!-- BREAK 退出循环组件降级 -->
+    <chain name="break1">
+        FOR(3).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <chain name="break2">
+        WHILE(wn1).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <chain name="break3">
+        ITERATOR(itn1).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <!-- SWITCH 选择组件降级 -->
+    <chain name="switch1">
+        SWITCH(node("x")).to(a,b);
+    </chain>
+    
+    <!-- SWITCH 普通组件降级 -->
+    <chain name="switch2">
+        SWITCH(swn1).to(node("x"),a);
+    </chain>
+    
+    <!-- AND 条件组件降级 -->
+    <chain name="and1">
+        IF(AND(node("x"),ifn1), a);
+    </chain>
+    
+    <!-- OR 条件组件降级 -->
+    <chain name="or1">
+        IF(OR(node("x"),ifn1), a);
+    </chain>
+    
+    <!-- NOT 条件组件降级 -->
+    <chain name="not1">
+        IF(NOT(node("x")), a);
+    </chain>
+    
+    <!-- CATCH 普通组件降级 -->
+    <chain name="catch1">
+            CATCH(THEN(a, d)).DO(node("x"))
+    </chain>
+    
+    <!-- 多个组件降级 -->
+    <chain name="multi1">
+        THEN(
+            a,
+            node("x1"),
+            IF(node("x2"), b)
+        );
+    </chain>
+    
+    <chain name="multi2">
+        IF(
+            OR(node("x1"), ifn1),
+            THEN(a, node("x2"))
+        );
+    </chain>
+    
+    <chain name="multi3">
+        FOR(node("x1")).DO(
+            THEN(b, node("x2"))
+        );
+    </chain>
+
+    <!-- 并发降级测试 -->
+    <chain name="concurrent1">
+        WHEN(
+            THEN(node("x1")),
+            IF(node("x2"), b)
+        ).maxWaitSeconds(10000);
+    </chain>
+
+    <chain name="concurrent2">
+        WHEN(
+            node("x1"),
+            IF(node("x2"), b)
+        ).maxWaitSeconds(10000);
+    </chain>
+</flow>

+ 4 - 2
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/FallbackSpringbootTest.java → liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSpringbootTest.java

@@ -2,6 +2,7 @@ package com.yomahub.liteflow.test.fallback;
 
 import com.yomahub.liteflow.core.FlowExecutor;
 import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.test.BaseTest;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -17,12 +18,13 @@ import java.util.concurrent.Future;
  * SpringBoot 降级组件测试
  *
  * @author DaleLee
+ * @since 2.11.1
  */
 @TestPropertySource(value = "classpath:/fallback/application.properties")
-@SpringBootTest(classes = FallbackSpringbootTest.class)
+@SpringBootTest(classes = FallbackELSpringbootTest.class)
 @EnableAutoConfiguration
 @ComponentScan({"com.yomahub.liteflow.test.fallback.cmp"})
-public class FallbackSpringbootTest {
+public class FallbackELSpringbootTest extends BaseTest {
 
     @Resource
     private FlowExecutor flowExecutor;

+ 221 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSpringTest.java

@@ -0,0 +1,221 @@
+package com.yomahub.liteflow.test.fallback;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import javax.annotation.Resource;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * Spring 降级组件测试
+ *
+ * @author DaleLee
+ */
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration("classpath:/fallback/application.xml")
+public class FallbackELSpringTest extends BaseTest {
+
+    @Resource
+    private FlowExecutor flowExecutor;
+
+    @Test
+    public void testThen1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("then1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testThen2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("then2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhen1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("when1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String executeStepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("b==>c".equals(executeStepStr) || "c==>b".equals(executeStepStr));
+    }
+
+    @Test
+    public void testIf1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("if1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIf2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("if2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn1==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testFor1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("for1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("for1==>a==>a==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testFor2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("for2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("LOOP_3==>c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhile1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("while1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testWhile2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("while2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn1==>c==>wn1==>c==>wn1==>c==>wn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIterator1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("iterator1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testIterator2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("iterator2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn1==>c==>c==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("LOOP_3==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("wn1==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testBreak3() {
+        LiteflowResponse response = flowExecutor.execute2Resp("break3", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("itn1==>a==>bn1", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testSwitch1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("switch1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("swn2==>b", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testSwitch2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("switch2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("swn1==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testAnd1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("and1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testOr1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("or1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>ifn1==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testNot1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("not1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>a", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testCatch1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("catch1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>d==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("a==>c==>ifn2", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("ifn2==>ifn1==>a==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testMulti3() {
+        LiteflowResponse response = flowExecutor.execute2Resp("multi3", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        Assertions.assertEquals("for1==>b==>c==>b==>c==>b==>c", response.getExecuteStepStrWithoutTime());
+    }
+
+    @Test
+    public void testConcurrent1() {
+        LiteflowResponse response = flowExecutor.execute2Resp("concurrent1", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String stepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr));
+    }
+
+    @Test
+    public void testConcurrent2() {
+        LiteflowResponse response = flowExecutor.execute2Resp("concurrent2", "arg");
+        Assertions.assertTrue(response.isSuccess());
+        String stepStr = response.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr));
+    }
+
+    @Test
+    public void testConcurrent3() throws ExecutionException, InterruptedException {
+        // 执行多条 chain
+        Future<LiteflowResponse> future1 = flowExecutor.execute2Future("concurrent1", "arg", new Object());
+        Future<LiteflowResponse> future2 = flowExecutor.execute2Future("concurrent2", "arg", new Object());
+        Thread.sleep(1000);
+        LiteflowResponse response1 = future1.get();
+        LiteflowResponse response2 = future2.get();
+        Assertions.assertTrue(response1.isSuccess());
+        String stepStr1 = response1.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr1) || "ifn2==>c".equals(stepStr1));
+        Assertions.assertTrue(response2.isSuccess());
+        String stepStr2 = response2.getExecuteStepStrWithoutTime();
+        Assertions.assertTrue("c==>ifn2".equals(stepStr2) || "ifn2==>c".equals(stepStr2));
+    }
+}

+ 14 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java

@@ -0,0 +1,14 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeComponent;
+
+@LiteflowComponent("a")
+public class ACmp extends NodeComponent {
+
+    @Override
+    public void process() {
+        System.out.println("ACmp executed!");
+    }
+
+}

+ 14 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java

@@ -0,0 +1,14 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeComponent;
+
+@LiteflowComponent("b")
+public class BCmp extends NodeComponent {
+
+    @Override
+    public void process() {
+        System.out.println("BCmp executed!");
+    }
+
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeBreakComponent;
+
+@LiteflowComponent("bn1")
+@FallbackCmp
+public class BreakCmp extends NodeBreakComponent {
+
+    @Override
+    public boolean processBreak() throws Exception {
+        return true;
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeComponent;
+
+@LiteflowComponent("c")
+@FallbackCmp
+public class CCmp extends NodeComponent {
+
+    @Override
+    public void process() {
+        System.out.println("CCmp executed!");
+    }
+
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeComponent;
+
+@LiteflowComponent("d")
+public class DCmp extends NodeComponent {
+
+    @Override
+    public void process() throws Exception {
+        throw new RuntimeException("component[d]");
+    }
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeForComponent;
+
+@LiteflowComponent("for1")
+@FallbackCmp
+public class ForCmp extends NodeForComponent {
+
+    @Override
+    public int processFor() throws Exception {
+        return 3;
+    }
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeIfComponent;
+
+@LiteflowComponent("ifn1")
+public class IfCmp1 extends NodeIfComponent {
+
+    @Override
+    public boolean processIf() throws Exception {
+        return true;
+    }
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeIfComponent;
+
+@LiteflowComponent("ifn2")
+@FallbackCmp
+public class IfCmp2 extends NodeIfComponent {
+
+    @Override
+    public boolean processIf() throws Exception {
+        return false;
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+@LiteflowComponent("itn1")
+public class IteratorCmp1 extends NodeIteratorComponent {
+
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        return Arrays.asList("a", "b", "c").iterator();
+    }
+}

+ 18 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java

@@ -0,0 +1,18 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+@LiteflowComponent("itn2")
+@FallbackCmp
+public class IteratorCmp2 extends NodeIteratorComponent {
+
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        return Collections.emptyIterator();
+    }
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+
+@LiteflowComponent("swn1")
+public class SwitchCmp1 extends NodeSwitchComponent {
+
+    @Override
+    public String processSwitch() throws Exception {
+        return "a";
+    }
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+
+@LiteflowComponent("swn2")
+@FallbackCmp
+public class SwitchCmp2 extends NodeSwitchComponent {
+
+    @Override
+    public String processSwitch() throws Exception {
+        return "b";
+    }
+}

+ 26 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java

@@ -0,0 +1,26 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeWhileComponent;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@LiteflowComponent("wn1")
+public class WhileCmp1 extends NodeWhileComponent {
+    private int count = 0;
+
+    // 执行过的 chain
+    Set<String> executedChain = new HashSet<>();
+
+    @Override
+    public boolean processWhile() throws Exception {
+        // 判断是否切换了 chain
+        if (!executedChain.contains(this.getCurrChainId())) {
+            count = 0;
+            executedChain.add(this.getCurrChainId());
+        }
+        count++;
+        return count <= 3;
+    }
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.fallback.cmp;
+
+import com.yomahub.liteflow.annotation.FallbackCmp;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeWhileComponent;
+
+@LiteflowComponent("wn2")
+@FallbackCmp
+public class WhileCmp2 extends NodeWhileComponent {
+    
+    @Override
+    public boolean processWhile() throws Exception {
+        return false;
+    }
+}

+ 24 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/fallback/application.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
+       http://www.springframework.org/schema/context
+       http://www.springframework.org/schema/context/spring-context-4.0.xsd">
+
+    <context:component-scan base-package="com.yomahub.liteflow.test.fallback.cmp" />
+
+    <bean id="springAware" class="com.yomahub.liteflow.spi.spring.SpringAware"/>
+
+    <bean class="com.yomahub.liteflow.spring.ComponentScanner"/>
+
+    <bean id="liteflowConfig" class="com.yomahub.liteflow.property.LiteflowConfig">
+        <property name="ruleSource" value="fallback/flow.el.xml"/>
+        <property name="fallbackCmpEnable" value="true"/>
+    </bean>
+
+    <bean id="flowExecutor" class="com.yomahub.liteflow.core.FlowExecutor">
+        <constructor-arg name="liteflowConfig" ref="liteflowConfig"/>
+    </bean>
+</beans>

+ 136 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/fallback/flow.el.xml

@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <!-- THEN 普通组件降级 -->
+    <chain name="then1">
+        THEN(a, node("x"));
+    </chain>
+    
+    <chain name="then2">
+        THEN(PRE(node("x1")), node("x2"), FINALLY(node("x3")));
+    </chain>
+
+    <!-- WHEN 普通组件降级 -->
+    <chain name="when1">
+        WHEN(b, node("x"));
+    </chain>
+
+    <!-- IF 条件组件降级 -->
+    <chain name="if1">
+        IF(node("x"), a)
+    </chain>
+    
+    <!-- IF 普通组件降级 -->
+    <chain name="if2">
+        IF(ifn1, node("x"))
+    </chain>
+    
+    <!-- FOR 次数循环组件降级 -->
+    <chain name="for1">
+        FOR(node("x")).DO(a);
+    </chain>
+    
+    <!-- FOR 普通组件降级 -->
+    <chain name="for2">
+        FOR(3).DO(node("x"));
+    </chain>
+    
+    <!-- WHILE 条件循环组件降级 -->
+    <chain name="while1">
+        WHILE(node("x")).DO(a)
+    </chain>
+    
+    <!-- WHILE 普通组件降级 -->
+    <chain name="while2">
+        WHILE(wn1).DO(node("x"))
+    </chain>
+    
+    <!-- ITERATOR 迭代组件降级 -->
+    <chain name="iterator1">
+        ITERATOR(node("x")).DO(a)
+    </chain>
+    
+    <!-- ITERATOR 普通组件降级 -->
+    <chain name="iterator2">
+        ITERATOR(itn1).DO(node("x"))
+    </chain>
+    
+    <!-- BREAK 退出循环组件降级 -->
+    <chain name="break1">
+        FOR(3).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <chain name="break2">
+        WHILE(wn1).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <chain name="break3">
+        ITERATOR(itn1).DO(a).BREAK(node("x"));
+    </chain>
+    
+    <!-- SWITCH 选择组件降级 -->
+    <chain name="switch1">
+        SWITCH(node("x")).to(a,b);
+    </chain>
+    
+    <!-- SWITCH 普通组件降级 -->
+    <chain name="switch2">
+        SWITCH(swn1).to(node("x"),a);
+    </chain>
+    
+    <!-- AND 条件组件降级 -->
+    <chain name="and1">
+        IF(AND(node("x"),ifn1), a);
+    </chain>
+    
+    <!-- OR 条件组件降级 -->
+    <chain name="or1">
+        IF(OR(node("x"),ifn1), a);
+    </chain>
+    
+    <!-- NOT 条件组件降级 -->
+    <chain name="not1">
+        IF(NOT(node("x")), a);
+    </chain>
+    
+    <!-- CATCH 普通组件降级 -->
+    <chain name="catch1">
+            CATCH(THEN(a, d)).DO(node("x"))
+    </chain>
+    
+    <!-- 多个组件降级 -->
+    <chain name="multi1">
+        THEN(
+            a,
+            node("x1"),
+            IF(node("x2"), b)
+        );
+    </chain>
+    
+    <chain name="multi2">
+        IF(
+            OR(node("x1"), ifn1),
+            THEN(a, node("x2"))
+        );
+    </chain>
+    
+    <chain name="multi3">
+        FOR(node("x1")).DO(
+            THEN(b, node("x2"))
+        );
+    </chain>
+
+    <!-- 并发降级测试 -->
+    <chain name="concurrent1">
+        WHEN(
+            THEN(node("x1")),
+            IF(node("x2"), b)
+        ).maxWaitSeconds(10000);
+    </chain>
+
+    <chain name="concurrent2">
+        WHEN(
+            node("x1"),
+            IF(node("x2"), b)
+        ).maxWaitSeconds(10000);
+    </chain>
+</flow>