diff --git a/.github/workflows/create-plantuml.yml b/.github/workflows/create-plantuml.yml new file mode 100644 index 0000000..84c7daa --- /dev/null +++ b/.github/workflows/create-plantuml.yml @@ -0,0 +1,19 @@ +name: generate plantuml +on: push +jobs: + generate_plantuml: + runs-on: ubuntu-latest + name: plantuml + steps: + - name: checkout + uses: actions/checkout@v1 + with: + fetch-depth: 1 + - name: plantuml + id: plantuml + uses: grassedge/generate-plantuml-action@v1.5 + with: + path: doc/img + message: "Render PlantUML files" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/maven-publish.yml similarity index 100% rename from .github/workflows/release.yml rename to .github/workflows/maven-publish.yml diff --git a/.mvn/settings.xml.weibo b/.mvn/settings.xml.weibo new file mode 120000 index 0000000..b5e3655 --- /dev/null +++ b/.mvn/settings.xml.weibo @@ -0,0 +1 @@ +/Users/jack/.m2/settings.xml.weibo \ No newline at end of file diff --git a/README.md b/README.md index 4441f90..1982eab 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,12 @@ ## 一、说在前面的话 -这是一种基于动态代理与配置中心切换 bean 的实现类来达到服务降级与流量灰度发布方法研究。 +本项目描述并实现了一种基于动态代理与配置中心切换 bean 的实现类来达到服务降级与流量灰度发布方法的研究。 这是一个通过动态切换接口的实现类(springboot 中的 bean ),实现了一种高效而灵活的解决方案,用于应对复杂系统中的服务降级和功能灰度发布。 **该项目提出并实现了一种使用动态代理和配置中心来管理某个接口的多个业务实现,通过动态切换 Bean 的实现类快速实现服务降级。 此外,借助 [JEXL](https://commons.apache.org/proper/commons-jexl/) 自定义方法的解析规则,还提供了一种小流量功能灰度发布的解决方案。** -在当今复杂的业务系统中,服务降级和功能灰度发布已成为保障系统稳定性和灵活性的关键需求。本项目通过动态切换接口的实现类(Spring Boot中的Bean),提出了一种高效而灵活的解决方案。 -利用动态代理和配置中心的结合,以及[JEXL](https://commons.apache.org/proper/commons-jexl/)自定义方法的解析规则,我们实现了对接口的多种业务实现进行动态管理, -从而快速应对服务降级和小流量功能灰度发布的场景。 - ## 二、相关工作 & 设计理念 **相关工作** @@ -32,7 +28,8 @@ ISP)则要求我们确保流程中的任何接口实现都可以被替换, 实现原理其实很简单,通过代理类来执行规则判定即可,通过时序图描述的逻辑如下: -![](doc/img/idea.png) + +![](doc/img/execution-sequence-diagram.svg) 你可以像这样子引入他: @@ -40,7 +37,7 @@ ISP)则要求我们确保流程中的任何接口实现都可以被替换, plus.jdk spring-smart-ioc-starter - 1.0.0 + 1.0.2 ``` @@ -55,11 +52,11 @@ ISP)则要求我们确保流程中的任何接口实现都可以被替换, **`@ConditionOnRule`执行 eval逻辑时运行环境中的一些魔法变量的说明** -| 变量名 | 说明 | 补充 | -|---------|---------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| args | 当前调用的方法的入参, 允许在condition rule里面使用函数入参判定是否调用该实现类的方法,这个参数每次执行方法调用的时候都会作为临时变量写入运行环境 | 例如,你的方法定义为```String greeting(String name, String sex);```, 那么你可以在 condition rule里面通过 `args.name` 或 `args.sex` 来访问被调用的方法(Method)的入参 | -| global | 全局的变量或工具类 | 你可以使用 `global` 在condition rule中访问你写入的全局变量或一些工具类,例如,当你使用 `globalSmartIocContext.registerGlobalVar("random", new Random()))`注册`random`后,可以使用这样子的表达式 `@ConditionOnRule("global.randdom.nextInt(100) % 10 >= 8")`来将 20% 的流量来打到当前的这个实现类上 | -| current | 允许在 condition rule 维度来访问当前的 beanName(`current.beanName`) 和 methodName(`current.methodName`) | 这里你可以通过监听配置中心使用 `@ConditionOnRule("current.beanName == global.myServiceName")` 来指定当前要切换为哪个实现类. 这里的 `myServiceName`可以通过 `GlobalSmartIocContext#registerGlobalVar(name, obj)`写入 | +| 变量名 | 说明 | 补充 | +|---------|---------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| args | 当前调用的方法的入参, 允许在condition rule里面使用函数入参判定是否调用该实现类的方法,这个参数每次执行方法调用的时候都会作为临时变量写入运行环境 | 例如,你的方法定义为```String greeting(String name, String sex);```, 那么你可以在 condition rule里面通过 `args.name` 或 `args.sex` 来访问被调用的方法(Method)的入参 | +| global | 全局的变量或工具类 | 你可以使用 `global` 在condition rule中访问你写入的全局变量或一些工具类,例如,当你使用 `globalSmartIocContext.registerGlobalVar("random", new Random()))`注册`random`后,可以使用这样子的表达式 `@ConditionOnRule("global.random.nextInt(100) % 10 >= 8")`来将 20% 的流量来打到当前的这个实现类上 | +| current | 允许在 condition rule 维度来访问当前的 beanName(`current.beanName`) 和 methodName(`current.methodName`) | 这里你可以通过监听配置中心使用 `@ConditionOnRule("current.beanName == global.myServiceName")` 来指定当前要切换为哪个实现类. 这里的 `myServiceName`可以通过 `GlobalSmartIocContext#registerGlobalVar(name, obj)`写入 | **写入全局变量:** @@ -88,6 +85,53 @@ public class SetGlobalConfigTest { } } ``` +接下来你可以通过`global`变量来访问这里注册的全局变量和全局函数.例如, 你可以像下面这样,指定当 qps 小于 2000 时利用注册的 random 函数将 20% 的流量打到这个实现上: + +```java +import plus.jdk.smart.ioc.annotations.SmartService; + +@ConditionOnRule("global.qps < 2000 && random.nextInt(100) % 10 > 8") +@SmartService(group = MyService.class) +public class QpsDegradeService implements MyService { + // do something +} +``` +**最后的魔法** + +思虑再三,虽然这样子会带来一些安全性的问题,但是这完全取决于使用者了,不应该舍本逐末。 +最后登场的魔法就是全局`jexl.eval(anyString())`函数了,这个函数允许你直接通过全局变量自定义脚本。 +> 当然了,你也可以是用局部变量来传递脚本来执行,但是注意,这是非常不安全的一种做法,因为函数的输入大多来自外部输入, +> 如果你把这个功能开放给外部输入,那你最好保证调用方是可信的,值得你去给他最高级别的执行权限!!!否则就不要这么做。 +> 甚至于,在上述功能足够满足你需求的情况下,尽量不要使用这个魔法函数,这里只是为了满足一些上述功能不能满足需求的场景 + +通过这个功能,你可以把你想要执行的脚本配置在 配置中心 中,当服务启动或配置变化时,通过如下指令来更新你的脚本到 `global` 对象中: + +```java +public class SmartIocSelectorFactoryTest { + + /** + * Global intelligent IOC context instance, used to obtain and manage global configuration information. + */ + @Resource + private GlobalSmartIocContext globalSmartIocContext; + + @Test + public void evalExpressionTest() { + // Test the magic jexl.eval() function + globalSmartIocContext.registerGlobalVar("testScript", "1 + 2"); + } +} +``` + +然后使用 `@ConditionOnRule("jexl.eval(global.testScript)")`配置在需要的类或者方法上即可。通过这个魔法方法你可以把你的切换规则放在配置中心,通过配置中心按需求动态下发即可!!! + +> 注意, 使用函数输入时,千万不要把 `args` 的成员变量的内容传递给`jexl.eval(anyString())`函数,这会导致你的入参变成可执行的脚本!!!比如你的函数入参分别为 `a=3`、`b=4`, +> 你可以这么写`@ConditionOnRule("jexl.eval(global.testScript) > args.a + args.b")`, 这是安全的; 但是如果你直接把 args的参数内容直接传递给`jexl.eval(anyString())`, +> 例如 `@ConditionOnRule("jexl.eval(args.b)"),这无疑是一种裸奔的行为,会导致你的服务受到攻击,甚至导致更严重的后果。 + +所以,我的朋友,我还是不得不提醒你,

__永远都不要把入参直接当做脚本执行,这不管在哪都是一样的, +就像[变基的风险](https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%8F%98%E5%9F%BA.html#_rebase_peril)中提交到的, +如果你遵循这条金科玉律,就不会出差错。否则人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你!!!__

## 四、服务降级 & 小流量灰度的示例 diff --git a/doc/execution-sequence-diagram.md b/doc/execution-sequence-diagram.md new file mode 100644 index 0000000..5258206 --- /dev/null +++ b/doc/execution-sequence-diagram.md @@ -0,0 +1,100 @@ +```plantuml:execution-sequence-diagram +@startuml +title 启动 & 执行时序图 +!theme vibrant + +!define purple_color 6A00FF +!define green_color green +!define red_color FF05DE +!define yellow_color yellow +!define STRONG_TEXT(text) text +!define PURPLE_TEXT(text) text +!define GREEN_TEXT(text) text +!define BLUE_TEXT(text) text +!define GREEN_TEXT(text) text +!define RED_TEXT(text) text +!define YELLOW_TEXT(text) text +!define record_impl_msg GREEN_TEXT("记录bean加载过程中的每个接口的的实现类") +!define create_proxy_msg GREEN_TEXT("创建代理类并注册全局的 bean") +!define create_proxy_msg_note GREEN_TEXT("在这里会为指定interface生成代理类") +!define start_success_msg GREEN_TEXT("服务启动完成") +!define start_success_msg_note GREEN_TEXT("服务启动完成, 其他需要引入的地方使用 @Resource注解 按接口引入即可") +!define bean_load_flow GREEN_TEXT("bean加载流程") +!define logic_exec_group_name RED_TEXT(逻辑执行:通过组件生成的接口的代理类发起方法调用(该类直接通过 @Resource 注解注入即可)) +!define logic_exec_msg "通过代理类发起逻辑调用" +!define get_impl_msg 获取接口的实现类 +!define parse_condition_rule 解析实现类的切换规则 +!define jexl_engine_create_msg PURPLE_TEXT("创建jexl解析引擎") +!define jexl_engine_context_msg PURPLE_TEXT("向jexl注册方法入参") +!define jexl_engine_context_reg_msg PURPLE_TEXT("向jexl注册全局变量和自定义函数") +!define jexl_engine_eval_msg PURPLE_TEXT("通过 jexl 来执行脚本并获取返回结果") +!define jexl_engine_eval_result PURPLE_TEXT("jexl 执行输入的条件表达式并返回结果") +!define jexl_engine_eval_condition PURPLE_TEXT("判定jexl 执行输入的条件表达式是否为 true") +!define loop_bean_impl_msg "遍历所有的 bean 实现" +!define script_eval_true_msg "jexl eval结果为 true" +!define script_eval_false_msg "jexl eval结果为 false" +!define script_eval_false_continue_msg "继续遍历 eval下一个 bean的条件" +!define bean_impl_invoke_method_msg "调用符合条件的实现类的方法" +!define bean_impl_invoke_method_return_msg RED_TEXT("返回函数执行结果") +!define no_matching_rules_were_hit "未命中任何符合条件实现类" +!define get_default_implementation_annotated_via_primary_msg RED_TEXT(获取通过@SmartService的primary)\nRED_TEXT(属性标注的默认实现) +!define bean_load_flow_note GREEN_TEXT(在这里会扫描并记录所有接口被)\nRED_TEXT("@SmartService")GREEN_TEXT( 标注的实现类) +!define jexl_desc_note_line1 YELLOW_TEXT(JEXL(Java Expression Language)是Apache Commons项目中的一个子项目,它提供了 一种轻量级且灵活的表达式语言,) +!define jexl_desc_note_line2 YELLOW_TEXT(用于在Java应用程序中动态计算和操作数据。JEXL的设计初衷是为了使表达式的解析和执行变得简单且高效,同时保持与Java语言的紧密集成。) +!define jexl_desc_note_line3 YELLOW_TEXT(JEXL设计之初就考虑了性能问题,通过编译和缓存机制来提高表达式的执行效率。这里的逻辑判定耗时一般在纳秒级别) + + + +collections 服务启动 as service_start +collections 记录每个接口的实现类 as record_impl +collections 动态代理 as proxy_bean +collections 启动成功 as start_success +collections 业务逻辑调用 as logic_exec +collections Jexl引擎 as jexl_engine + +group bean_load_flow + autonumber + service_start -[#green]> record_impl : record_impl_msg + note left record_impl #yellow:bean_load_flow_note + record_impl -[#green]> proxy_bean: create_proxy_msg + note left proxy_bean #yellow: create_proxy_msg_note + proxy_bean -[#green]> start_success:start_success_msg + note left start_success #yellow:start_success_msg_note +end + +group logic_exec_group_name + autonumber + logic_exec -[#red]> proxy_bean:RED_TEXT(logic_exec_msg) + proxy_bean -[#red]> record_impl:RED_TEXT(get_impl_msg) + record_impl -[#red]> proxy_bean: RED_TEXT(parse_condition_rule) + loop STRONG_TEXT(RED_TEXT(loop_bean_impl_msg)) + note over proxy_bean,start_success #green + jexl_desc_note_line1 + jexl_desc_note_line2 + jexl_desc_note_line3 + end note + proxy_bean -[#purple_color]> jexl_engine: jexl_engine_create_msg + proxy_bean -[#purple_color]> jexl_engine: jexl_engine_context_msg + proxy_bean -[#purple_color]> jexl_engine: jexl_engine_context_reg_msg + proxy_bean -[#purple_color]> jexl_engine: jexl_engine_eval_msg + jexl_engine -[#purple_color]> proxy_bean: jexl_engine_eval_result + proxy_bean --[#purple_color]> proxy_bean: jexl_engine_eval_condition + alt RED_TEXT(script_eval_true_msg) + autonumber 10 + proxy_bean -> record_impl: RED_TEXT(bean_impl_invoke_method_msg) + record_impl -> proxy_bean: RED_TEXT(bean_impl_invoke_method_return_msg) + proxy_bean -> logic_exec: RED_TEXT(bean_impl_invoke_method_return_msg) + else RED_TEXT(script_eval_false_msg) + autonumber 10 + record_impl -> record_impl: RED_TEXT(script_eval_false_continue_msg) + end + end + alt RED_TEXT(no_matching_rules_were_hit) + autonumber 11 + proxy_bean -> record_impl:get_default_implementation_annotated_via_primary_msg + record_impl -> proxy_bean: bean_impl_invoke_method_return_msg + proxy_bean -> logic_exec: bean_impl_invoke_method_return_msg + end +end +@enduml +``` diff --git a/doc/img/README.md b/doc/img/README.md new file mode 100644 index 0000000..e69de29 diff --git a/doc/img/idea.png b/doc/img/idea.png deleted file mode 100644 index be83499..0000000 Binary files a/doc/img/idea.png and /dev/null differ diff --git a/doc/plantuml.md b/doc/plantuml.md deleted file mode 100644 index 8ba3fd7..0000000 --- a/doc/plantuml.md +++ /dev/null @@ -1,69 +0,0 @@ -```plantuml -@startuml -title 设计时序图 -!theme vibrant -!define service_start 服务启动 -!define record_impl "记录每个接口的实现类" -!define record_impl_msg 记录bean加载过程\n中的每个接口的的实现类 -!define proxy_bean "动态代理" -!define create_proxy_msg "创建代理类\n并注册全局的 bean" -!define start_success "启动成功" -!define start_success_msg "服务启动完成" -!define bean_load_flow bean加载流程 -!define logic_exec_group_name 逻辑执行 -!define logic_exec "业务逻辑调用" -!define logic_exec_msg "通过代理类发起逻辑调用" -!define get_impl_msg 获取接口的实现类 -!define parse_condition_rule 解析实现类的切换规则 -!define jexl_engine "Jexl引擎" -!define jexl_engine_create_msg "创建jexl解析引擎" -!define jexl_engine_context_msg "向jexl注册方法入参" -!define jexl_engine_context_reg_msg "向jexl注册全局变量和自定义函数" -!define jexl_engine_eval_msg "通过 jexl 来执行脚本并获取返回结果" -!define jexl_engine_eval_result "jexl 执行输入的条件表达式并返回结果" -!define loop_bean_impl_msg "遍历所有的 bean 实现" -!define script_eval_true_msg "jexl eval结果为 true" -!define script_eval_false_msg "jexl eval结果为 false" -!define script_eval_false_continue_msg "继续遍历 eval下一个 bean的条件" -!define bean_impl_invoke_method_msg "调用符合条件的实现类的方法" -!define bean_impl_invoke_method_return_msg "返回函数执行结果" -!define no_matching_rules_were_hit "未命中任何符合条件实现类" -!define get_default_implementation_annotated_via_primary_msg "获取通过primary标注的默认实现" - -group bean_load_flow - autonumber - service_start -> record_impl : record_impl_msg - record_impl -> proxy_bean: create_proxy_msg - proxy_bean -> start_success:start_success_msg -end - -group logic_exec_group_name - autonumber - logic_exec -> proxy_bean:logic_exec_msg - proxy_bean -> record_impl:get_impl_msg - record_impl -> proxy_bean: parse_condition_rule - loop loop_bean_impl_msg - proxy_bean -> jexl_engine: jexl_engine_create_msg - proxy_bean -> jexl_engine: jexl_engine_context_msg - proxy_bean -> jexl_engine: jexl_engine_context_reg_msg - proxy_bean -> jexl_engine: jexl_engine_eval_msg - jexl_engine -> proxy_bean: jexl_engine_eval_result - alt script_eval_true_msg - autonumber - proxy_bean -> record_impl: bean_impl_invoke_method_msg - record_impl -> proxy_bean: bean_impl_invoke_method_return_msg - proxy_bean -> logic_exec: bean_impl_invoke_method_return_msg - else script_eval_false_msg - autonumber - record_impl -> record_impl: script_eval_false_continue_msg - end - end - alt no_matching_rules_were_hit - autonumber - proxy_bean -> record_impl:get_default_implementation_annotated_via_primary_msg - record_impl -> proxy_bean: bean_impl_invoke_method_return_msg - proxy_bean -> logic_exec: bean_impl_invoke_method_return_msg - end -end -@enduml -``` diff --git a/pom.xml b/pom.xml index 8b13f85..ba400a7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ plus.jdk spring-smart-ioc-starter - 1.0.0 + 1.0.2 spring-smart-ioc-starter diff --git a/src/main/java/plus/jdk/smart/ioc/global/Evalable.java b/src/main/java/plus/jdk/smart/ioc/global/Evalable.java new file mode 100644 index 0000000..5cacc1a --- /dev/null +++ b/src/main/java/plus/jdk/smart/ioc/global/Evalable.java @@ -0,0 +1,8 @@ +package plus.jdk.smart.ioc.global; + +public interface Evalable { + /** + * Execute the specified script and return the results。 + */ + Object eval(String script); +} diff --git a/src/main/java/plus/jdk/smart/ioc/global/SmartIocSelectorFactory.java b/src/main/java/plus/jdk/smart/ioc/global/SmartIocSelectorFactory.java index 46fb612..8e84fb5 100644 --- a/src/main/java/plus/jdk/smart/ioc/global/SmartIocSelectorFactory.java +++ b/src/main/java/plus/jdk/smart/ioc/global/SmartIocSelectorFactory.java @@ -36,12 +36,25 @@ public class SmartIocSelectorFactory { */ private final GlobalSmartIocContext globalSmartIocContext; + /** + * Jexl expression engine for parsing and executing conditional expressions. + */ + private final JexlEngine jexlEngine; + /** * Create a new SmartIocSelectorFactory instance. */ public SmartIocSelectorFactory(ApplicationContext applicationContext, GlobalSmartIocContext globalSmartIocContext) { this.applicationContext = applicationContext; this.globalSmartIocContext = globalSmartIocContext; + JexlFeatures jexlFeatures = new JexlFeatures() + // Disable execution of loop statements + .loops(false) + // Disable modification of global variables + .sideEffectGlobal(false) + // Disable side effects, i.e. do not allow expressions to modify any variables (global or local) + .sideEffect(false); + this.jexlEngine = new JexlBuilder().features(jexlFeatures).create(); } /** @@ -65,25 +78,18 @@ protected void registerSdiDefinition(BeanDescriptor beanDescriptor) { * commons-jexl */ public Object evalExpression(String expression, Map params, String beanName, String methodName) { - StopWatch stopWatch = new StopWatch(); + StopWatch stopWatch = new StopWatch(expression); stopWatch.start(String.format("{%s}-{%s}", expression, params)); - JexlFeatures jexlFeatures = new JexlFeatures() - // 禁止执行循环语句 - .loops(false) - // 禁止修改全局变量 - .sideEffectGlobal(false) - // 禁用副作用,即不允许表达式对任何变量(全局或局部)进行修改 - .sideEffect(false); - Properties properties = new Properties(); - properties.put("beanName", beanName); - properties.put("methodName", methodName); - JexlEngine jexl = new JexlBuilder().features(jexlFeatures).create(); JexlContext context = new MapContext(); context.set("args", params); context.set("random", new Random()); + Map properties = new HashMap<>(); + properties.put("beanName", beanName); + properties.put("methodName", methodName); context.set("current", properties); context.set("global", globalSmartIocContext.getGlobalProperties()); - Object result = jexl.createExpression(expression).evaluate(context); + context.set("jexl", (Evalable) script -> evalExpression(script, params, beanName, methodName)); + Object result = jexlEngine.createExpression(expression).evaluate(context); stopWatch.stop(); log.info("evalExpression {}, cost {}ms, {}", expression, stopWatch.getTotalTimeMillis(), stopWatch.prettyPrint()); return result; diff --git a/src/test/java/plus/jdk/smart/ioc/global/SmartIocSelectorFactoryTest.java b/src/test/java/plus/jdk/smart/ioc/global/SmartIocSelectorFactoryTest.java index ab802e5..c8e48b2 100644 --- a/src/test/java/plus/jdk/smart/ioc/global/SmartIocSelectorFactoryTest.java +++ b/src/test/java/plus/jdk/smart/ioc/global/SmartIocSelectorFactoryTest.java @@ -2,6 +2,7 @@ import cn.hutool.core.date.StopWatch; import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; @@ -25,18 +26,23 @@ public class SmartIocSelectorFactoryTest { @Resource private SmartIocSelectorFactory smartIocSelectorFactory; + /** + * Global intelligent IOC context instance, used to obtain and manage global configuration information. + */ + @Resource + private GlobalSmartIocContext globalSmartIocContext; + @Test public void evalExpressionTest() { - DispatchContext dispatchContext = new DispatchContext() {}; - dispatchContext.setName("jack"); - StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - String expression = "dispatchContext.getName() == \"jack\".substring(1)"; - Map params = new HashMap<>(); - params.put("dispatchContext", dispatchContext); - - Object object = smartIocSelectorFactory.evalExpression(expression, params, "testBean", "name"); - stopWatch.stop(); - log.info("evalExpression cost {}ms", stopWatch.getTotalTimeMillis()); + // Test the magic jexl.eval() function + Map args = new HashMap<>(); + + globalSmartIocContext.registerGlobalVar("testScript", "1 + 2"); + Assert.assertEquals(smartIocSelectorFactory.evalExpression("jexl.eval(global.testScript)", args, null, null), 3); + + args.put("c", 4); + globalSmartIocContext.registerGlobalVar("testScript", "1 + 2"); + Assert.assertEquals(smartIocSelectorFactory.evalExpression("jexl.eval(global.testScript) < args.c", args, null, null), true); + Assert.assertEquals(smartIocSelectorFactory.evalExpression("jexl.eval(global.testScript) > args.c", args, null, null), false); } } \ No newline at end of file diff --git a/src/test/java/plus/jdk/smart/ioc/service/DegradeByGlobalConfigTest.java b/src/test/java/plus/jdk/smart/ioc/service/DegradeByGlobalConfigTest.java index 24e2b47..dfd5ca6 100644 --- a/src/test/java/plus/jdk/smart/ioc/service/DegradeByGlobalConfigTest.java +++ b/src/test/java/plus/jdk/smart/ioc/service/DegradeByGlobalConfigTest.java @@ -8,11 +8,13 @@ import org.springframework.test.context.junit4.SpringRunner; import plus.jdk.smart.ioc.TomcatLauncher; import plus.jdk.smart.ioc.global.GlobalSmartIocContext; +import plus.jdk.smart.ioc.global.SmartIocSelectorFactory; import plus.jdk.smart.ioc.model.RecallContext; import plus.jdk.smart.ioc.model.RecallResult; import plus.jdk.smart.ioc.service.degrade.MaterialRecallService; import javax.annotation.Resource; +import java.util.HashMap; @Slf4j @RunWith(SpringRunner.class) @@ -31,6 +33,12 @@ public class DegradeByGlobalConfigTest { @Resource private GlobalSmartIocContext globalSmartIocContext; + /** + * Factory class instance for obtaining and selecting the appropriate IoC container。 + */ + @Resource + private SmartIocSelectorFactory smartIocSelectorFactory; + @Test public void degradeTest() { // Tests if qps <= 1000 normally uses the normal vector recall implementation to return recommended content diff --git a/src/test/java/plus/jdk/smart/ioc/service/SmartFlowByMethodArgsTest.java b/src/test/java/plus/jdk/smart/ioc/service/SmartFlowByMethodArgsTest.java index bb82fe1..03f7bdf 100644 --- a/src/test/java/plus/jdk/smart/ioc/service/SmartFlowByMethodArgsTest.java +++ b/src/test/java/plus/jdk/smart/ioc/service/SmartFlowByMethodArgsTest.java @@ -6,6 +6,7 @@ import org.springframework.test.context.junit4.SpringRunner; import plus.jdk.smart.ioc.TomcatLauncher; import plus.jdk.smart.ioc.global.Advised; +import plus.jdk.smart.ioc.global.SmartIocSelectorFactory; import plus.jdk.smart.ioc.model.DispatchContext; import plus.jdk.smart.ioc.service.small.flow.SmsDispatchService; import plus.jdk.smart.ioc.service.small.flow.impl.AlibabaCloudSmsDispatchService;