流程图上没画人,单子却走对了:工作流引擎内置处理人解析器

作者:mldong日期:2026/9/4


一、流程图上没画人,单子却走对了

用流程设计器画一张最简单的请假审批流:开始 → 部门领导审批 → 结束,三个节点两条线。

画完之后你检查一遍这张图:节点上没有任何人的名字。没有"张三",没有"李四",连"审批人"三个字都是你自己写的节点显示名。从图上看,这张流程不知道该把单子交给谁。

然后你发起了一笔请假。几秒后,你部门领导的待办列表里,多了这条单子。

它怎么知道该去找领导的?

你可能会说:节点名不是写着"部门领导审批"吗——但节点显示名只是个标签,引擎不会拿它去组织架构里搜人;就算你把节点改成"甲乙丙",流程照样得走对。真正的答案是:处理人从来不是画流程时定死的,是引擎在建任务那一刻,按一条固定的优先级链现场算出来的。这条链的最后一级,站着引擎自带的 7 个"找人"处理器——你在节点上写的,只是一个类名字符串。

这篇就把这条链拆开:处理人的三级优先级、一个类名怎么变成一个活人、7 个内置处理器各自怎么找人、以及为什么业务方一行找人代码都不用写。

二、先排队,再找人:处理人的三级优先级

引擎走到一个任务节点、要创建任务时,做的第一件事不是建任务,而是解析参与者(actor)。这段逻辑在 CreateTaskHandler.resolveActors 里,优先级写得非常直白——先到先得,上一级找到了就不再往下看

1// jeeflow-core/src/.../handler/impl/CreateTaskHandler.java(节选,1.8.21)
2private List<String> resolveActors(TaskModel taskModel, ProcessModel model, Execution execution) {
3    List<String> actors = new ArrayList<>();
4    FlowData args = execution.getArgs();
5    // 1. 动态指定下一节点处理人优先(v1.0.1:对齐 boot2/boot3 tf_nextNodeOperator)
6    Object nextNodeOperator = args.get(FlowConst.NEXT_NODE_OPERATOR);
7    if (nextNodeOperator != null && !nextNodeOperator.toString().isEmpty()) { /* 拆逗号入列 */ return actors; }
8    // 2. 固定指派 assignee——token 即变量 key,能替换就换,换不了就是字面量
9    if (taskModel.getAssignee() != null && !taskModel.getAssignee().isEmpty()) {
10        for (String raw : assignee.split(",")) {
11            String token = raw.trim();
12            // mldong 契约特殊值:applicant  流程发起人
13            if (token.contains("applicant")) {
14                token = token.replace("applicant", execution.getProcessInstance().getOperator());
15            }
16            Object v = args.get(token);          // token 是变量 key?命中就替换成变量值
17            if (v != null) { /* 变量值入列 */ }
18            else if (!actors.contains(token)) { actors.add(token); }  // 没命中,字面量就是 userId
19        }
20    }
21    // 3. 动态指派处理器 assignmentHandler(assignee 为空时才生效)
22    if (actors.isEmpty()) {
23        String handlerClass = taskModel.getAssignmentHandler();
24        if (handlerClass != null && !handlerClass.isEmpty()) {
25            AssignmentHandler handler = (AssignmentHandler)
26                    Class.forName(handlerClass.trim()).getDeclaredConstructor().newInstance();
27            String result = handler.assign(execution);
28            /* 结果按逗号拆开入列 */
29        }
30    }
31    return actors;
32}
33

三级,一级比一级"动态":

第 1 级:tf_nextNodeOperator——上一步提交时动态指定。 提交任务时可以在参数里直接点名"下一节点给谁办"。这是唯一能越过所有配置的方式:流程运行中的人比流程设计时的配置更清楚这单该给谁,所以它排第一。变量名里的 tf_ 前缀是 mldong 框架的历史契约(task forward),对齐 boot2/boot3 的同名机制。

第 2 级:assignee——流程图上写死的固定值。 支持逗号分隔多人,而且每个 token 有一次"变身"机会:token 先当变量 key 去流程变量里查,查到就用变量值,查不到就把它本身当 userId。所以 assignee: "leader1" 是写死一个人,assignee: "deptManager" 是引用一个流程变量,assignee: "applicant" 则是引擎级特殊值——替换成流程发起人(这就是第 6 篇讲过的 applicant 契约在处理人侧的样子,退回发起人重办之所以闭环,靠的也是它)。

第 3 级:assignmentHandler——按类名调处理器动态算。 注意代码里那个 if (actors.isEmpty())——assignee 配了值,assignmentHandler 就整个被跳过。这是故意的设计,spec 里写明了理由:"静态配置优先于动态逻辑,减少误解析"。写死的值永远比动态逻辑优先级高,配置的人意图才不会被意外覆盖。

三级走完还没完——任务建好、落库之前,还有一道 FlowInterceptor 拦截器可以改名单。引擎自带的 SurrogateInterceptor(委托代理)就在这儿干活:查一下"这个处理人是不是委托了别人",命中就把代理人换进去。所以完整的图景是这样:

这条优先级链理清了,剩下的问题就一个:第 3 级里那个 Class.forName(...) 到底在干嘛——一个写在流程 JSON 里的类名字符串,怎么就变成了坐在工位上的部门领导?

三、一个类名,怎么变成一个活人

先看它在流程定义里长什么样。这是引擎测试资产里一条真实流程(11-assignment-handler.json)的节点:

1{
2  "id": "task3",
3  "type": "snaker:task",
4  "text": { "value": "部门领导" },
5  "properties": {
6    "form": "dept-form",
7    "assignmentHandler": "com.mldong.jeeflow.interceptor.impl.OrgUserAssignmentHandlers$DeptLeaderAssignmentHandler",
8    "taskType": 0, "performType": 0
9  }
10}
11

节点上和人有关的只有一个 assignmentHandler 属性,值是一个Java 全限定类名。没有参数、没有配置块、没有任何人的名字。

引擎拿到这个字符串后做的事,CreateTaskHandler 里就一行:

1AssignmentHandler handler = (AssignmentHandler)
2        Class.forName(handlerClass.trim()).getDeclaredConstructor().newInstance();
3String result = handler.assign(execution);
4

反射:加载类、实例化、调 assign。就这么土。这个"土办法"是刻意的——引擎核心零第三方依赖,不引 Spring 不引 Guice,一个类名字符串加一次反射就是全部的"依赖注入"。

处理器的接口也简单到过分:

1// jeeflow-core/src/.../interceptor/AssignmentHandler.java
2public interface AssignmentHandler {
3    String assign(Execution execution);
4}
5

一个入参,一个字符串返回值(多人用逗号拼)。所有的"找人"逻辑都被压进这一个方法。它能用到的原材料,全在 Execution 执行上下文里:

Execution 里的东西是什么找人时干什么用
execution.getOperator()当前操作人——上一步点"提交"的那个人"我的领导审"类 handler 的起点
execution.getProcessInstance().getOperator()流程发起人——这单是谁发起的"发起人的领导审"类 handler 的起点
execution.getArgs()流程变量(表单数据)表单字段取人、变量替换
execution.getNodeModel()当前任务节点模型拿节点编码去匹配字段/角色

注意前两行的区别——当前操作人流程发起人是两个不同的人,而且正是"部门领导审批"这种最常见需求的全部秘密:请假人(发起人)提交后,第一站的"当前操作人"就是发起人本人,引擎拿他的部门找领导;领导审批提交后,"当前操作人"变成领导本人。同一个 handler,站在不同节点上,基准人不同,找到的人就不同。

还有一个容易忽略的细节:assign 返回的是 String 不是集合,多人用逗号拼接。这个看似随意的设计其实是对 PHP/Go 等动态语言的友好——一个字符串在任何语言间传递都不会有类型争议。六语言引擎在这套机制上能对齐得七七八八(第五节细说),从接口签名的克制开始就定了调。

四、引擎自带的 7 个答案

接口只有一个方法,难的部分其实是"引擎要不要帮你写几个现成的实现"。jeeflow 的答案是:要,而且一口气内置 7 个。先上全家福(中文名是引擎 HandlerRegistry 里登记的官方叫法,顺序即官方排序):

#注册名(类名简写)官方中文名找人逻辑数据从哪来
1OperatorAssignmentHandler流程发起人返回 instance.operator纯引擎语义,零依赖
2FormFieldAssigneeHandler根据表单字段值分配参与者节点编码匹配表单字段的值纯引擎语义,零依赖
3DeptLeaderAssignmentHandler当前用户所属部门经理当前操作人 → 其部门 → 部门领导IUserProvider + IOrgUserProvider
4DeptMainLeaderAssignmentHandler当前用户所属部门分管领导同上,取分管领导同上
5ApplicantDeptLeaderAssignmentHandler发起人所属部门经理发起人 → 其部门 → 部门领导同上
6ApplicantDeptMainLeaderAssignmentHandler发起人所属部门分管领导同上,取分管领导同上
7TaskRoleAssigneeHandler根据任务节点唯一编码关联角色分配参与者节点编码当作角色编码,查该角色下的人IOrgUserProvider

三个代表挨个说。

代表一:OperatorAssignmentHandler,最简单的,也是最容易被名字骗的。 它的类名叫"操作人处理器",实现却是:

1// jeeflow-core/src/.../interceptor/impl/OperatorAssignmentHandler.java
2public class OperatorAssignmentHandler implements AssignmentHandler {
3    @Override
4    public String assign(Execution execution) {
5        if (execution.getProcessInstance() != null
6                && execution.getProcessInstance().getOperator() != null
7                && !execution.getProcessInstance().getOperator().isEmpty()) {
8            return execution.getProcessInstance().getOperator();   // 注意:是发起人
9        }
10        return "apply.operator";
11    }
12}
13

它返回的不是 execution.getOperator()(当前操作人),而是 instance.getOperator()流程发起人)。类名里的 "Operator" 沿用的是实例字段名 operator——实例的 operator 字段存的是发起人,所以这个 handler 的真实语义是"让发起人再审一次",典型用途是"申请不通过退回申请人确认"这类节点。类名和语义之间隔着一层窗户纸,源码注释里写得明明白白,但只看类名配置的人容易踩坑——这也是为什么把它放在第一个讲。

代表二:FormFieldAssigneeHandler,让表单决定谁审批。 场景:报销单上有个"指定审批人"字段,填报人选了谁,单子就给谁审。它的找人逻辑是一个三级字段匹配:

1// jeeflow-core/src/.../interceptor/impl/FormFieldAssigneeHandler.java(节选)
2private Object findFieldValue(FlowData args, String taskName) {
3    // issues/48 E20:表单字段变量为 f_ 前缀(f_approver),先匹配前缀再回落裸名(兼容存量)
4    if (args.containsKey("f_" + taskName)) return args.get("f_" + taskName);
5    if (args.containsKey(taskName)) return args.get(taskName);
6    Matcher matcher = NUMBER_SUFFIX_PATTERN.matcher(taskName);
7    if (matcher.matches() && args.containsKey(matcher.group(1))) {
8        return args.get(matcher.group(1));       // task_01  匹配 task 字段
9    }
10    return null;
11}
12

节点编码叫 approver,就先找流程变量里的 f_approver(mldong 契约里表单字段统一带 f_ 前缀),找不到再找裸名 approver,还找不到就把 _01 这种编号后缀摘掉再试一次。字段值支持逗号分隔字符串或集合,全算参与者。流程图上一行配置不写死任何人,人选在表单里——这是七个 handler 里唯一"人在提交时才确定"的。

代表三:DeptLeaderAssignmentHandler,两个 SPI 串成一条链。 这是"部门领导审批"的真身,也是内置 handler 里最能让你看懂分层设计的:

1// jeeflow-core/src/.../interceptor/impl/OrgUserAssignmentHandlers.java(节选)
2public static class DeptLeaderAssignmentHandler implements AssignmentHandler {
3    @Override
4    public String assign(Execution execution) {
5        return byDept(deptIdOf(execution.getOperator(), execution), false);
6    }
7}
8
9private static String deptIdOf(String userId, Execution execution) {
10    IUserProvider userProvider = ServiceContext.find(IUserProvider.class);
11    if (userProvider == null) return null;
12    IUserProvider.UserInfo u = userProvider.getUser(userId);
13    return u != null ? u.getDeptId() : null;          // 第一步:人  部门
14}
15
16private static String byDept(String deptId, boolean main) {
17    IOrgUserProvider org = ServiceContext.find(IOrgUserProvider.class);
18    if (org == null) return null;
19    List<String> ids = main ? org.findDeptMainLeaders(deptId) : org.findDeptLeaders(deptId);
20    return ids == null || ids.isEmpty() ? null : String.join(",", ids);   // 第二步:部门  领导们
21}
22

两步:当前操作人 --IUserProvider.getUser()--> 部门 ID --IOrgUserProvider.findDeptLeaders()--> 领导列表。引擎对组织架构的全部认知只有这两个接口——它不知道你们公司部门表长什么样、领导字段叫 leaderIds 还是 managers,它只知道"给我一个 userId 我要能问到他的部门,给我一个部门 ID 我要能问到它的领导"。这个设计的意义,下一节专门讲。

7 个 handler 不是纸面上的清单,引擎测试资产里有一条专门的流程逐个验证它们(11-assignment-handler.json:task1 表单字段取人 → task2 发起人审批 → task3 部门领导 → task4 角色取人,一条串联合笼),配一套内存版组织数据(D01 部门 → 领导 leader1、leader2,分管 boss1,角色 task4 → roleA、roleB)跑全链路断言。

五、业务方为什么不写 handler:把逻辑留下,把数据交出去

上一节那两步 SPI 串联,单看是"设计干净",放到历史里看才知道是"血泪换来的"。这个故事的起点是一份集成反馈(issues/16,2026-08)。

boot4(mldong 的 JDK 21 分支)作为集成方接入 jeeflow 时,在内置 wf 模块里实现了 8 个参与者处理类。其中两个是纯引擎语义(发起人、表单字段),剩下六个全是组织维度:当前人部门领导、当前人分管领导、发起人部门领导、发起人分管领导、角色取人……问题在于——这些 handler 是"通用业务语义",任何一个要审批流的集成方都躲不开,但 jeeflow 当时的 SPI 只有"取单个用户信息"和"搜索用户",没有"按部门取领导""按角色取人"的数据接口。于是每一个集成方都得把这一套 handler 重新抄写一遍,抄的还全是自己家的部门表查询。

jeeflow 的解法是把这件事拦腰切成两半

- 逻辑归引擎:那 7 个 handler(含两个纯语义的)引擎直接内置,v1.6.0(2026-08-04)四语言同批落地; - 数据归业务方:新增 IOrgUserProvider SPI,就三个方法——findDeptLeaders(deptId) / findDeptMainLeaders(deptId) / findByRole(roleCode),加上已有的 IUserProvider.getUser(userId),业务方实现这四个方法,组织数据就接进来了。

效果在 boot4 的代码量上立竿见影:原本 8 个 handler 类,现在只剩两个数据接口实现。换个角度说,"部门领导审批"这个需求的全部定制点,被压缩成一条 SQL——"查出某部门的领导 ID 列表"。逻辑有且只有一份,数据各家接各家的,这就是"把逻辑留下,把数据交出去"。

这套设计还有个容易被忽视的收尾:设计器怎么知道下拉框里该列哪 7 个选项? 引擎 v1.4.0 起带了一个 HandlerRegistry(处理器注册中心),构造时就把这 7 个内置 handler 连同官方中文名、排序一起登记成元数据:

1// jeeflow-core/src/.../metadata/HandlerRegistry.java(节选)
2public HandlerRegistry() {
3    registerBuiltins();   // 构造即注册内置通用 handler 元数据——集成方零注册即得字典
4}
5private void registerBuiltins() {
6    Class<?> type = AssignmentHandler.class;
7    register(type, "com.mldong.jeeflow.interceptor.impl.OperatorAssignmentHandler", "流程发起人", -9999, null);
8    register(type, "...OrgUserAssignmentHandlers$ApplicantDeptLeaderAssignmentHandler", "发起人所属部门经理", 10, null);
9    register(type, "...OrgUserAssignmentHandlers$ApplicantDeptMainLeaderAssignmentHandler", "发起人所属部门分管领导", 20, null);
10    register(type, "...OrgUserAssignmentHandlers$DeptLeaderAssignmentHandler", "当前用户所属部门经理", 30, null);
11    /* ...表单字段 50、角色 60... */
12}
13

前端流程设计器的"处理人处理器"下拉字典直接以它为数据源——字典里的值和运行时按名解析的键天然同一个来源,不存在"设计器里能选、引擎里跑不通"的值漂移。集成方要扩展自己的 handler,除了实现接口,也在这儿补一条元数据,设计器里立刻可见。

六、六语言:同一份流程 JSON,七个同样的名字

这套机制最硬的约束其实是一条跨语言契约:注册名就是 Java 全限定类名——包括 OrgUserAssignmentHandlers$DeptLeaderAssignmentHandler 里这个 Java 内部类的 $ 语法。Go/Python/Node/Rust/PHP 的引擎在注册和解析内置 handler 时,键一律原样使用这个 Java 字符串。代价是其他语言的注册表里存着一段"外语";好处是同一份流程 JSON 不用改一个字,可以在任何一门语言的引擎上跑出同样的找人结果,前端设计器也只需要认识一种写法。

逐语言核对(2026-09-04,源码 grep)的结果:机制六语言全有,接口签名各从其俗(Java/PHP/Rust 收 Execution 返回逗号串,Go/Python/Node 收 (node, instance, operator) 返回列表——语义相同,只是"上下文打包传"还是"拆开传"的口味差),IUserProvider + IOrgUserProvider 两个数据 SPI 六语言齐装满员。差异有三处,如实列出:

维度JavaGoPythonNodePHPRust
内置 7 handler7/77/77/77/72/77/7
表单字段匹配f_ 前缀→裸名→_N 后缀同 Java同 Java同 Java同 Java少 _N 后缀一步
assignee 与 handler 同时配置时assignee 先handler 先assignee 先handler 先assignee 先assignee 先
applicant 匹配子串替换全等子串子串子串全等

三处差异的分量不一样,值得分别说一句:

PHP 只内置了 2 个(发起人部门领导、表单字段)——更要紧的是它的元数据字典仍登记着全部 7 个:设计器下拉里能看到 7 个选项,真正配了没实现的那 5 个,运行时会解析失败。这是六语言里唯一的"字典与实现不一致",补齐前,PHP 栈的流程设计得避开那 5 个选项(或由集成层自行实现注册)。

优先级两派:spec 的口径是"assignee 静态配置优先于 assignmentHandler 动态逻辑",Java/Python/PHP/Rust 照此实现;Go/Node 的实现里却是注册表 handler 先查。它只在一个节点同时配置了 assignee 和 assignmentHandler 时才咬人——这种配置本身就是坏味道(语义含糊),但一旦发生,六语言会给出两种不同的处理人。跨语言迁移流程定义前,值得用一条 grep 自查。

applicant 子串 vs 全等:Java/Python/Node/PHP 用"包含 applicant 就替换"(assignee: "applicantLeader" 也会被替换成"发起人ID+Leader"),Go/Rust 要求 token 全等。写 applicant 这个精确值的场景两者一致;但变量命名里碰巧含 applicant 字样的,子串派会悄悄替换。与其记规则,不如约定只用裸 applicant 或逗号列表里的独立段。

这三处都已挂到 issues 台账,后续批次对齐。除却这些边角,"节点上一个类名,引擎现场找出人"这件事,在六门语言上的行为是一致的——下次你在流程设计器里画完一张没有任何人名的流程图,可以放心点提交:找人的活,引擎内置的 7 个处理器比你急。

参考资料

- jeeflow 源码(2026-09-04 核对):jeeflow-java 1.8.21(CreateTaskHandler.resolveActors / interceptor/AssignmentHandler / interceptor/impl/{OperatorAssignmentHandler,FormFieldAssigneeHandler,OrgUserAssignmentHandlers} / metadata/HandlerRegistry / spi/{IUserProvider,IOrgUserProvider} / AssignmentHandlerTest + flows/11-assignment-handler.json) - 跨语言核对:jeeflow-go 1.8.24(engine/engine_impl.go resolveActors / engine/builtin.go)/ jeeflow-python 1.8.24(jeeflow/engine.py / jeeflow/builtin.py)/ jeeflow-node 1.8.24(src/engine.ts / src/builtin.ts)/ jeeflow-php 1.3.8(src/Handler/CreateTaskHandler.php / src/Handler/Builtin/)/ jeeflow-rust 1.0.7(src/engine.rs resolve_assignee / src/interceptor.rs) - issues/16《boot4 集成 jeeflow 参与者解析 SPI 扩展与通用 handler 内置》(v1.6.0,2026-08-04 四语言落地) - jeeflow-doc 规范:concepts/04-extensions §5 HandlerRegistry、concepts/05-spi-design - 系列前篇:第 6 篇《"applicant" 契约:退回发起人的闭环设计》、第 14 篇《流程实例如何沿着箭头走到结束:Rust 工作流引擎的执行内核》 - 仓库:github.com/mldong/jeef… · 文档站 · 开源演示站 · 集成演示站


流程图上没画人,单子却走对了:工作流引擎内置处理人解析器》 是转载文章,点击查看原文


相关推荐


Next.js 全栈笔记系统实战:RSC 组件架构、Redis 数据层与规范驱动开发
浮生望2026/8/27

摘要 以Next.js笔记系统为例,深入RSC异步组件直接查询Redis、Server/Client组件边界、BEM命名规范、路径别名及组件驱动开发流程,展示从需求分析到技术方案到组件拆分的全栈落地路径。 一个笔记系统的需求并不复杂:左侧笔记列表,右侧 Markdown 编辑预览,支持 CRUD 和搜索。但用 Next.js 全栈实现它,涉及的技术决策覆盖了 RSC 组件边界、数据层选型、组件拆分策略和项目结构规范——这些正是 Next.js 工程化的核心议题。 技术选型:为什么是 Redis


JavaScript实战技巧总结
阿橙的百宝箱2026/8/19

JavaScript实战技巧总结* 引言 JavaScript作为现代Web开发的基石,其灵活性和强大的功能使其成为开发者必备的技能之一。然而,随着ECMAScript标准的不断演进和前端生态的日益复杂,如何高效、优雅地编写JavaScript代码成为开发者面临的重要课题。本文将从实际出发,总结一系列经过实战检验的JavaScript技巧,涵盖性能优化、代码简洁性、异步处理、模块化等多个关键领域,帮助开发者提升代码质量和开发效率。 一、性能优化技巧 1. 减少DOM操作 DOM操作是Java


篇2-bitsandbytes-具体观-算法与实现剖析
happyprince2026/8/4

三部曲之二 · 看懂:潜入水下的技术细节 本篇承接整体观建立的认知地图,潜入水下逐一审视 bitsandbytes 的核心技术实现——LLM.int8()、QLoRA/NF4、8-bit 优化器,以及支撑它们的极致工程。每个技术点遵循"原理(论文)→ 代码 → 例子"三段式剖析,让读者真正"看懂"代码背后的学术根源与工程取舍。 一、总述:三大算法支柱 + 一条工程脊梁 bitsandbytes 的技术内核可拆成三大算法支柱 + 一条工程脊梁。三大算法支柱是:① LLM.int


把随身WiFi改成网盘聚合器:中兴F50挂载本地存储+夸克网盘实战
羑悻的小杀马特.2026/7/27

文章目录 前言1 什么是OpenList?2 中兴F50上安装OpenList服务3 挂载本地存储和网盘存储3.1 挂载本地F50自带的20G存储3.2 挂载夸克网盘 4 穿透OpenList以支持公网访问4.1 如何用?4.2 在F50上安装4.3 配置OpenList的http隧道 5 固定二级子域名(升级任意套餐)总结 前言 中兴F50放在包里时,很多人只把它当作一台提供热点的随身WiFi。需要传文件、打开网盘资料或临时分享内容时,仍然要依赖手机、电脑和多个客户端


【数据库】CRUD-- 增删改
哦虎!2026/7/19

第二章 增删改 文章目录 第二章 增删改前言一、命名二、增1. 新增2. 指定列插入3. 多行插入4. 插入查询结果5. 查询的指定列插入6.注意 三、改1.修改数据2.组内修改 四、删除1. 删除数据2.drop和delete的区别 五、截断表总结 前言 本期主要讲增 删 改, 会稍微使用到查 , 也就是select , 遇到会稍做讲解 , 详细讲解会放到下期~ 一、命名 创建表时,命名有两种 驼峰命名(studentName)蛇形命名(stud


C++内存管理
孬甭_2026/7/11

目录 1 · C / C++ 内存分布 2 · C++内存管理方式 2 - 1 · new / delete 操作内置类型 2 - 2 · new / delete 操作自定义类型 3 · operator new 和 operator delete 函数 4 · new 和 delete 的实现原理 4 - 1 · 内置类型 4 - 2 · 自定义类型 5 · 定位new 6 · new / delete 与 malloc / free 的区别 1 · C / C


EventBus → SharedFlow
plainGeek2026/7/3

EventBus → SharedFlow 老写法(Java + EventBus) // 事件定义 public class LoginEvent { private long userId; public LoginEvent(long userId) { this.userId = userId; } public long getUserId() { return userId; } } // 发送 EventBus.getDefault().post(new


【Linux基础】初始Linux
键盘敲碎了雾霭2026/6/25

🎬 博主名称:键盘敲碎了雾霭 🔥 个人专栏: 《C语言》《数据结构》 《C++》 《Matlab》 《Python》 《Linux》 ⛺️指尖敲代码,雾霭皆可破 文章目录 一、Linux发展史1.1 UNIX发展历史1.2 Linux发展历史 二、开源三、应用现状四、发行版本五、os概念六、使用XShell远程登录Linux6.1 会话方式登录6.2 命令行登录6.3 多用户使用 文章结语 一、Linux发展史 要说Linux,还


【Java基础】链表的七十二变——从LRU缓存到手写浏览器前进后退
zzz_23682026/6/16

链表的七十二变——从LRU缓存到手写浏览器前进后退 写在前面的目录 一、真实面试真题引入 二、链表的底层解构——不止是 next 指针   2.1 单向链表:最简单的链式结构   2.2 双向链表:前后眼的设计哲学   2.3 链表反转四步拆解   2.4 哨兵节点——让边界消失   2.5 跳表——给链表加个索引 三、"纯手工、零依赖"原创案例实战   3.1 浏览器前进后退——双向链表实现标签页导航   3.2 LRU 缓存淘汰——HashMap + 双向链表的 O(1) 魔法 四、


安全第一与合规治理:魔芋 AI 正式发布企业级大模型网关 Mai Gateway
青莲网络2026/6/9

在现代企业业务创新中,人工智能(AI)正逐步成为关键驱动力。随着大语言模型的发展,企业应用架构已从传统的微服务、云原生架构,加速向AI原生架构演进。在此过程中,企业在AI集成、系统稳定性、安全合规及管理复杂性方面面临诸多挑战。 为了帮助企业安全、高效地落地 AI 应用,魔芋 AI(MoYu AI)正式推出重磅核心产品——Mai Gateway(大模型网关)。作为企业 AI 应用与模型服务、工具及其他 Agent 之间的核心连接组件,Mai Gateway 通过提供协议转换、安全防护、流量治理和

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读