news 2026/10/13 19:33:23

Java单元测试利器Mockito:核心用法与团队实战规范

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java单元测试利器Mockito:核心用法与团队实战规范

在做 Java 后端测试时,Mockito 基本是一个绕不开的名字。它帮你造出虚拟依赖、控制外部调用返回值,并精确验证对象之间的交互行为。这篇文章不会只贴几个示例凑数,我会把 Mockito 从入门到进阶的核心用法、设计思路、踩坑经验一次性讲清楚,包括参数匹配器、自定义 Answer、verify 验证、ArgumentCaptor、常见异常排查,以及我们在真实项目里沉淀下来的 Mock 测试规范。无论你是刚开始接触单元测试,还是已经在项目里用了一阵子 Mockito,这篇内容都可以当作一份能直接参考的实战手册。

1. 先从整体看 Mockito 要解决什么问题

1.1 单元测试的痛点:依赖地狱与不稳定测试

我一直觉得,很多团队不做单元测试,不是不想做,而是“做了就挂”。原因很典型:被测的业务类里 new 了一个别的 Service,那个 Service 又连 Redis、调第三方接口、读数据库。一旦本地环境没这些中间件,测试直接失败。就算中间件存在,每次跑测试还要等网络、等 IO,慢得让人怀疑人生。

Mockito 的解决方案是“替身”。它不启动容器、不连数据库,而是通过动态代理或者子类方式,在内存里生成一个目标接口或类的替身对象。被测类拿到的永远是替身,而不是真实依赖。替身的所有方法默认什么都不做,返回 null、false 或者 0,然后你再通过打桩让它按测试需要返回指定结果。这样一来,测试环境不再依赖中间件,测试运行速度也能控制在毫秒级。

但这里必须说清楚一个边界:Mockito 是用来写单元测试的,不是用来替代集成测试的。如果我们把数据库访问、Redis 缓存、外部 HTTP 全部 mock 掉,那测的是“你自己的代码在理想环境下的逻辑”,不是“组件协作的正确性”。所以我个人建议:核心业务逻辑层用 Mockito 写单测,涉及真实中间件的链路交给集成测试。你不需要为了 Mockito 而 mock 一切。

1.2 Mockito 的三板斧:mock、stub、verify 的定位与关系

Mockito 的全部功能可以用三个词概括:mock、stub、verify。三者各管一段,组合起来才完整。

  • mock:造替身对象。它可以在不写实现类的情况下,模拟一个接口或类的行为。
  • stub(打桩):配置替身对象在特定参数下返回特定结果,或者抛出指定异常。
  • verify(验证):校验某方法是否被调用、调用次数、调用顺序和实际参数。

它们的定位如果乱掉,很容易写出意义不明的测试。我经常看到有人只 mock 不打桩,结果对象返回 null,断言全挂在 NPE 上;也有人疯狂打桩,但从不 verify,根本不关心被测类到底调了谁。正确的心态应该是:先用 mock 隔离外部依赖,再用 stub 控制外部依赖的输出,最后用 verify 保证被测类确实“做了该做的事”。这三步合起来才是 Mockito 能带给你的测试价值。

1.3 什么时候不该用 Mockito

Mockito 不是银弹,过度使用会反噬。用我经历过的一个小例子来说:一个订单服务,内部调用了库存服务扣减库存,某次上线前单测全绿,但联调时发现库存服务接口参数反了。原因就是单测里我把库存服务整个 mock 掉了,根本没验证真实 HTTP 报文。这种测试看起来在隔离外部依赖,实则把外部依赖的错误一起“隔离”了。

所以我的判断标准是:如果你 mock 的东西是“你自己团队写且逻辑复杂”的类,请谨慎。它可能承担了重要业务规则,mock 掉等于绕过了核心逻辑。如果你 mock 的东西是“跨进程、跨系统”的稳定边界,比如第三方支付接口、消息队列客户端,那非常合适。Mockito 是让测试变快的工具,不是掩盖问题的遮羞布。

2. 环境准备与最基础用法:从 0 到 1 跑通 mock 和打桩

2.1 引入依赖与初始化 mock 对象

现在主流项目基本都是 Maven 或 Gradle。如果用的是 Mockito 5.x 和 JUnit 5,Maven 里只需要两个依赖:一个 mockito-core,一个 mockito-junit-jupiter。Gradle 也一样,不过顺手引入这两个依赖就行:

<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>5.11.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>5.11.0</version> <scope>test</scope> </dependency>

依赖引入后,创建 mock 对象有两种方式。最简单的是直接静态调用mock():

PaymentClient paymentClient = mock(PaymentClient.class);

另一种是配合注解,我强烈推荐这种方式,因为它让代码更干净:

@ExtendWith(MockitoExtension.class) class OrderServiceTest { @Mock PaymentClient paymentClient; @Mock OrderRepository orderRepository; @InjectMocks OrderService orderService; }

这里@ExtendWith(MockitoExtension.class)是 JUnit 5 的桥接器,它负责在每条测试用例执行前初始化所有@Mock注解字段。@InjectMocks会把 mock 对象按构造方法参数注入到被测类里。很多新手忘了加@ExtendWith或者MockitoAnnotations.openMocks(this),结果字段一直是 null,测试一跑就 NPE,排查半天。这个坑说小不小,先记住。

2.2 when().thenReturn() 打桩的正确姿势

创建了 mock 对象,默认情况下它的方法什么都不做。要让方法有返回值,需要打桩。最经典的写法是:

when(paymentClient.pay("A001", new BigDecimal("99.00"))) .thenReturn(true);

这段代码翻译过来是:当paymentClient.pay("A001", "99.00")被调用时,返回true。被测类调用这段代码时,不会真的发起网络请求,而是直接拿到你配置的结果。

这里有几个重要的细节。第一,打桩的入参必须是 mock 对象的方法调用,而不是被测对象的方法调用。我看过有人写when(orderService.create(...)).thenReturn(...),这在 Mockito 里是无效的,因为它只能对 mock 对象生效。第二,如果你不关心具体参数,可以用参数匹配器,比如when(paymentClient.pay(anyString(), any())).thenReturn(true),后面我会单独讲匹配器的坑。

另外,打桩的返回值要和真实业务逻辑匹配。比如pay方法本身可能返回一个布尔值,但你打桩返回true,测试里订单服务就会认为支付成功。如果你打桩返回 null,订单服务可能直接 NPE。所以打桩不只是“让测试能跑”,还在模拟真实依赖的合理输出。

2.3 spy 的真实对象与部分打桩

mock 是完全的替身,但有时候我们需要“真实对象的大部分逻辑”,只替换其中一小段。最典型的情况是测试一个类的私有方法之外的内部方法:你不想跑真实的发邮件逻辑,但希望类里其他方法正常执行。这时可以用spy。

@Spy EmailService emailService; @Mock SmtpClient smtpClient; @Test void testSendWelcomeEmail() { doReturn(true).when(emailService).sendInternal(anyString()); UserService userService = new UserService(emailService); userService.welcome("张三"); verify(emailService).sendInternal("张三"); }

这段代码里emailService是一个 spy 对象,它默认会调用真实方法,但sendInternal被显式打桩,所以不会真的发出邮件。使用 spy 时有一点特别容易掉坑:如果方法在 spy 内部被自调用,比如sendWelcomeEmail方法内部调用了this.sendInternal,那么打桩sendInternal是可以拦住的,因为 Mockito 走的是子类代理。但如果方法被super.xxx()方式调用,或者 final 方法、私有方法,就拦不住了。遇到这种情况,我会优先考虑重构代码,把需要替换的依赖抽出来,而不是强行对 spy 做文章。

2.4 注解到底怎么配合 JUnit 使用

使用注解时,初始化顺序也算一个高频问题。@InjectMocks会优先选构造方法注入,如果没有构造方法,则会用 setter 或字段注入。我个人建议被测类统一用构造方法注入,并在测试类里把它写成显式创建,而不是完全依赖@InjectMocks:

@BeforeEach void setUp() { orderService = new OrderService(paymentClient, orderRepository); }

显式构造有几个好处:一是读者一眼能看清被测类依赖了哪些 mock;二是避免@InjectMocks在多个构造方法时“猜不透”而注入错对象;三是测试里想传入一个不完全 mock 的依赖时,灵活度高很多。@InjectMocks本身很方便,但我也见过它把我们“误导”得以为依赖都注入了,实际却有字段漏掉。稳妥起见,管理好依赖注入,代码稍微多写两行完全值得。

3. 参数匹配器与自定义 Answer:让桩更聪明

3.1 常用匹配器:any、eq、matches 组合

真实业务里,方法调用很多是动态参数。比如订单号是一串随机生成的编号,支付金额是计算出来的小数。如果用精确值打桩,测试写起来会很痛苦,因为你要提前知道所有运行时参数。这时参数匹配器就派上用场了。

when(paymentClient.pay(anyString(), any(BigDecimal.class))) .thenReturn(true);

anyString()匹配任意字符串,any(BigDecimal.class)匹配任意 BigDecimal。更细一点的匹配器还包括anyInt()、anyLong()、contains("abc")、startsWith("订单")、matches("\\d{6}")等。使用匹配器有一个铁律:如果方法有多个参数,要么全部用精确值,要么全部用匹配器。不能写成when(obj.method("A001", anyInt())),因为 Mockito 在栈里记录了匹配器的位置,你混用精确值会破坏它的内部状态,直接抛InvalidUseOfMatchersException。这条我当年踩过,后来成了团队评审必查项。

// 这句话看起来“聪明”,实际上会报错 when(paymentClient.pay("A001", any())) .thenReturn(true);

正确做法是混用时用eq()包住精确值:

when(paymentClient.pay(eq("A001"), any())) .thenReturn(true);

这个eq()也是一个匹配器,它表示“等于这个值”。只要配上eq,上面的铁律就满足了。记住:匹配器的世界里没有特殊的“精确值”,一切都要用匹配器表达。

3.2 自定义 Answer:实现有逻辑的返回

thenReturn只能返回固定值。但有些场景需要根据实际调用参数计算返回值,比如根据折扣率算出最终支付金额,或者根据用户类型返回不同结果。这时可以用thenAnswer:

when(promotionService.calculateDiscount(any(User.class))) .thenAnswer(invocation -> { User user = invocation.getArgument(0); if ("VIP".equals(user.getLevel())) { return new BigDecimal("0.8"); } return new BigDecimal("1.0"); });

thenAnswer接收一个Answer对象,或者直接传 Lambda。InvocationOnMock里有getArgument()、getArguments()和getMethod()等方法,你可以拿到本次调用的完整信息,写任意计算逻辑。我曾经用它模拟过一个分布式锁:连续调用 3 次返回成功,之后返回失败,以此测试并发场景下的降级逻辑。这个能力比写一堆 mock 状态字段干净多了。

那thenReturn和thenAnswer怎么选?固定值就thenReturn,需要根据参数计算或需要模拟调用次数行为时,用thenAnswer。两者的执行时机也不一样:thenReturn是预先定义的返回值,thenAnswer是调用时动态计算。性能上两者没有明显差距,但thenAnswer灵活度更高,也更容易写出过于复杂的逻辑,注意别把测试写成了另一个业务实现。

3.3 doReturn/when 与 void 方法打桩

有些方法没有返回值,比如eventBus.publish(event)、logger.log(msg)。这种 void 方法不能直接when(...).thenReturn(...),因为when里的表达式本身需要评估一个返回值,而 void 方法返回 void 根本无法继续调用thenReturn。这时候要换doXxx().when()系列:

doNothing().when(eventBus).publish(any(OrderEvent.class)); doThrow(new RuntimeException("发送失败")) .when(eventBus) .publish(any(OrderEvent.class)); doAnswer(invocation -> { // 模拟异步回调 return null; }).when(eventBus).publish(any(OrderEvent.class));

doReturn().when()也可以用来给普通方法打桩,它的效果和when().thenReturn()基本一致,但执行时机不同:when().thenReturn()是在调用时触发,而doReturn().when()是先配置,后触发。对于会执行真实方法而导致抛异常的情况(比如 spy 对象),doReturn().when()更保险。反过来,when().thenReturn()有编译期类型检查的优势,能提前发现返回值类型不匹配。两条路没有绝对优劣,我个人的习惯是:普通方法用when().thenReturn(),void、spy 和需要避免真实调用的场景用doReturn().when()。

4. 行为验证:verify 与 ArgumentCaptor

4.1 验证调用次数与顺序

mock 的另一个价值是行为验证。verify可以确认被测对象是否真的调用了某个依赖,以及调用是否满足预期次数。最常见的是times():

verify(orderRepository, times(1)).save(any(Order.class)); verify(paymentClient, never()).refund(anyString()); verify(paymentClient, atLeastOnce()).pay(anyString(), any()); verify(paymentClient, atMost(3)).pay(anyString(), any());

times(1)是最常用的,但如果业务里存在循环、重试机制,次数断言能帮大忙。比如要求“失败后最多重试 2 次”,你可以在测试里模拟第一次失败、第二次成功,然后verify(paymentClient, times(2)).pay(...)。这种验证能直接暴露重试逻辑的缺陷。

如果还要验证调用顺序,用InOrder:

InOrder inOrder = inOrder(paymentClient, orderRepository); inOrder.verify(paymentClient).pay(anyString(), any()); inOrder.verify(orderRepository).save(any(Order.class));

这个场景对应的是“先支付,再保存订单”的业务规则。顺序验证往往被忽略,但一旦你的业务对时序有要求,它是最能表达意图的断言方式。注意inOrder里要列出所有涉及的 mock 对象,不然 Mockito 无法构建全局顺序。

4.2 捕获参数:ArgumentCaptor 的用法

verify还能配合ArgumentCaptor捕获被测类传给依赖的实际参数,然后对参数内容做断言。相比用匹配器“匹配一个值”直接通过,捕获参数能让你检查更细致的字段。

ArgumentCaptor<Order> orderCaptor = ArgumentCaptor.forClass(Order.class); verify(orderRepository).save(orderCaptor.capture()); Order savedOrder = orderCaptor.getValue(); assertEquals("A001", savedOrder.getOrderId()); assertEquals(new BigDecimal("99.00"), savedOrder.getAmount());

这段代码的意义在于:我们不只要确认save被调用了,还要确认保存的订单里金额是否经过正确计算、订单状态是否被置为“已支付”。如果被测类里有一行order.setStatus("PAYED")忘写了,thenReturn打桩不会暴露它,ArgumentCaptor却可以立刻抓住。需要捕获多个参数时,可以用captor.getAllValues()拿到本次测试里所有调用传入的值列表,适合测试循环批量保存的场景。

4.3 verifyNoMoreInteractions 与 verifyNoInteractions 的取舍

Mockito 还提供了两个“清场”式验证:verifyNoMoreInteractions(mock)验证 mock 上没有多余的未验证调用;verifyNoInteractions(mock)验证 mock 完全没有任何交互。很多人看到这俩挺兴奋,觉得能保证“被测代码没有额外动作”,结果在真实项目里堆了一堆verifyNoMoreInteractions,每加一行业务代码就开始红,测试变得出奇地脆弱。

我比较克制。verifyNoInteractions适合断言“某个依赖完全不该被触碰”的场景,比如库存不足时不应该调用支付客户端:

when(inventoryService.checkStock(anyString())).thenReturn(false); assertThrows(BusinessException.class, () -> orderService.create("A001")); verifyNoInteractions(paymentClient);

但verifyNoMoreInteractions的问题是它对“验证过哪些方法”有严格顺序要求。如果你前面用verify只验证了pay,后面被测类还调用了getStatus,这个getStatus也会被算作“多余交互”,测试直接挂。这种约束收益低、维护成本高,我会尽量避免放在老代码改造里,只在全新代码且预先定义好交互契约时使用。

5. 常见问题与排查技巧实录

5.1 InvalidUseOfMatchersException 为什么老缠着你

这是 Mockito 新手最常遇到的异常,没有之一。字面意思是“匹配器使用非法”,但真实原因通常有两种。第一种是匹配器和精确值混用,上文已经提到。第二种是匹配器出现在了不该出现的地方,比如把any()写在测试方法里的某个中间变量上,或者写在一个自定义方法里,而非直接作为 mock 方法参数:

// 错误写法 String s = anyString(); when(paymentClient.pay(s, any())).thenReturn(true);

这里anyString()在when外面执行,Mockito 的匹配器栈记录不到对应上下文,自然报错。排查技巧也很直接:看异常堆栈第一行,它通常会指出哪个测试方法、哪一行出问题;优先检查最近一次when/verify的参数是否全用了匹配器。如果你在循环里调用带匹配器的方法,也要注意循环结束后匹配器状态是否被清理干净。实际上,Mockito 会在下一步操作时自动清空匹配器栈,所以循环里使用匹配器本身没问题,但循环里如果混用了精确值,就会引发连锁异常。

5.2 静态方法不能 mock 和 final 类问题

传统 JVM 代理只能 mock 非 final 的实例方法和接口。因此很多人一直以为 Mockito 无法 mock 静态方法或 final 类。从 Mockito 4 开始内置了 inline mock maker,可以 mock 静态方法、final 类和 final 方法。但我必须强调,能用是能用,不建议当常规武器。

静态方法 mock 的核心风险在两个方面:一是 mock 静态方法要确保测试是单线程的、没有并发干扰,因为它改的是整个类加载器的字节码;二是测试代码和真实代码的耦合度会变高,一旦有人改了静态方法签名,测试里所有相关打桩都要变。我在项目里见过大量MockedStatic嵌套在 try-with-resources 里的写法,可读性很差。如果想把静态调用干掉,我更推荐先做代码重构,把静态方法包一层实例对象,或者用一个可替换的间接层。重构之后测试自然干净,不会沦为 Mockito 特技表演。

5.3 thenReturn 与 doReturn 搞混会踩什么坑

这两者的区别前面说过,但踩坑实录值得再强调一次。如果你 mock 的对象是一个 super 类或者接口代理,when().thenReturn()在配置阶段会先执行一次真实调用(对 spy 而言)。这个“先执行”有时候会引发副作用。例如spyUserService.getUser("A001")在真实环境会查数据库,你用when(spyUserService.getUser("A001")).thenReturn(fakeUser)时,Mockito 为了计算 stubbing,真的会把getUser("A001")调用一次,数据库直接报错。

解决办法就是把打桩顺序反过来,用doReturn(fakeUser).when(spyUserService).getUser("A001")。它不会在配置阶段触发真实方法。另一个常见点:如果thenReturn传的参数类型不对,编译器能帮我们拦截;但doReturn是 Object 类型,没有编译期类型检查,如果返回值类型不匹配,会运行到被测代码时才 ClassCastException。所以我的习惯是:能用when().thenReturn()就优先用,spy 和 void 场景再用doReturn().when()。

5.4 排查技巧:失败测试先看哪几样

当一个 Mockito 测试失败时,很多人上来就 debug,效率极低。我的排查顺序固定成四步。

第一步,看断言错误里哪个 mock 方法“被期望调用却没有调用”,这通常是打桩条件不对或参数匹配器写错;第二步,看是否有 NPE,这多半是 mock 对象没初始化,检查@ExtendWith和@InjectMocks是否存在;第三步,看是否有UnnecessaryStubbingException,说明你打桩了但测试路径根本没走到那一步,可能被测逻辑分支不对,也可能是过度打桩;第四步,再往上翻异常堆栈,确认是不是在 spy 或静态方法 mock 时触发了真实调用。

这套顺序在实际排查中省了我大量时间。有些开发一看到异常就怀疑框架 bug,其实 Mockito 异常信息写得已经很直白,错误原因和提示修复方向通常就在第一行异常里。静下心读异常,往往比 debug 一个个断点快得多。

我顺手整理了一个速查表,供参考:

异常现象常见原因处理方案
InvalidUseOfMatchersException匹配器和精确值混用用 eq() 包住精确值
大量 NPEmock 未初始化加 @ExtendWith(MockitoExtension.class) 或 openMocks
UnnecessaryStubbingException打桩未被执行删除未用打桩或用 lenient()
真实方法被调用(spy)when().thenReturn 配置阶段触发真实调用改用 doReturn().when()
ClassCastExceptiondoReturn 返回值类型不匹配检查返回值类型,尽量用 when().thenReturn() 获得编译期检查
MockitoException: static mocking未启用 inline mock maker确认 Mockito 5.x 内置,或引入 mockito-inline

6. 实际项目中的工作流与团队规范

6.1 写 mock 代码的团队约定

Mockito 用久了,很容易写出“全 mock、全覆盖、全 verify”的测试,看着绿,实际是纸老虎。我们团队慢慢沉淀了几条规则,我觉得值得分享。

第一条,对外部依赖做 mock,对内部逻辑不做 mock。说白了就是只替身“别人的对象”,不替身“自己的核心业务”;第二条,每条测试只验证一个核心行为。如果一个测试方法里有三个 Verify、五个 when,它已经变成集成测试了,维护成本远比收益高;第三条,打桩无用的桩要清理。Mockito 4 以后默认对未使用的桩报UnnecessaryStubbingException,这不是框架找茬,而是帮你发现测试用例没走到对应分支。如果确实准备了一个桩但这条分支没走到,说明测试意图不明,建议拆分成两个用例。

另外,命名上尽量沿着“行为”而非“实现”走。比如shouldThrowExceptionWhenStockNotEnough就比testCreateOrderFail更能表达意图。配合这些约定的测试,才能作为业务行为的活文档,而不是一堆只知道“跑绿”的验证器。

6.2 用 Mockito 重构一段“难测代码”的实例

有一个模拟项目我记忆很深。老代码里订单模块直接在方法内部new PaymentClient(),导致测试没法替换,每次单测都会真的去创建连接。我用 Mockito 重构它的思路分三步:第一步把PaymentClient抽成构造参数,让依赖从内部创建变外部注入;第二步在测试类里 mockPaymentClient,并用@InjectMocks注入被测服务;第三步对测试中原先无法触达的逻辑分支补齐验证。

重构之后,原来三分钟跑不完的测试变成了一秒内跑完,而且还顺手发现了一个老 bug:订单金额为 0 时,依然调用了支付接口。这类问题在旧代码里几乎不可能通过手工测试暴露出来,但重构后加一个verify(paymentClient, never()).pay(...)就牢牢锁住了。可以说,Mockito 倒逼了一次更健康的依赖注入设计,这种收益往往比 mock 本身更大。

6.3 我最后想分享的一个小技巧

关于 Mockito,我最后想补充一个不那么热门但非常实用的小技巧:用BDDMockito风格的 API 提高测试可读性。它把when(...).thenReturn(...)写成given(...).willReturn(...),把verify(...)写成then(...).should(...)。这听上去只是换了个名字,但对团队协作帮助很大,因为given让测试读起来像“安排场景”,then让断言看起来像“验证结果”,业务人员也能大致看懂测试在干什么。

// BDD 风格 given(paymentClient.pay(anyString(), any())) .willReturn(true); orderService.create("A001", new BigDecimal("99.00")); then(orderRepository).should(times(1)).save(any(Order.class)); then(paymentClient).should(never()).refund(anyString());

当然,这只是风格层的改变,底层一切都还是 Mockito。如果你所在的团队正在从零建设单元测试,我建议直接统一成 BDD 风格,因为它更贴近“行为驱动”的思考路径,团队评审时也更高效。等大家形成了写测试的肌肉记忆,再慢慢挖掘 Mockito 更高级的特性也不迟。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 19:33:09

冒泡排序:算法思维的手术刀与编程直觉训练场

1. 项目概述&#xff1a;这不是教科书里的冒泡排序&#xff0c;而是你真正该懂的“算法第一课”“《算法学习》二 冒泡排序分析”——光看标题&#xff0c;很多人会下意识划走&#xff1a;太基础了&#xff0c;不就是两层for循环、相邻比较、交换位置&#xff1f;背过、写过、调…

作者头像 李华
网站建设 2026/10/13 19:33:06

Python 元组完全指南:不可变性、拆包技巧与高频应用场景

1. 先搞清楚&#xff1a;元组到底是什么元组&#xff08;tuple&#xff09;是 Python 内置的一种序列类型&#xff0c;和列表&#xff08;list&#xff09;一样&#xff0c;它也是用来盛放一组数据的容器。但两者有一个本质区别&#xff0c;列表是可变的&#xff08;mutable&am…

作者头像 李华
网站建设 2026/10/13 19:32:38

跨模型调用统一网关:把litellm真正用进生产环境的实践指南

跨模型调用统一网关&#xff1a;我如何把litellm真正用进生产环境先聊一个前几天差点让我加班的场景。项目里那个模块要同时对接三家大模型供应商——OpenAI那边要GPT&#xff0c;Anthropic那个模型对长文档理解更强&#xff0c;国内一家服务商的价格便宜适合批量任务。按最原始…

作者头像 李华
网站建设 2026/10/13 19:29:30

PlatEMO平台实战:MATLAB多目标优化算法对比与问题自定义指南

简介&#xff1a;面向多目标优化领域的研究者和工程师&#xff0c;这份代码资源提供了目前应用最广的开源进化多目标优化平台之一&#xff0c;涵盖非支配排序遗传算法&#xff08;NSGA-II&#xff09;、基于分解的多目标进化算法&#xff08;MOEA/D&#xff09;等多种经典方法&…

作者头像 李华
网站建设 2026/10/13 19:25:25

编辑器插件驱动的知识管理工作流:从笔记沉淀到快速检索

这些年我做知识管理最大的感受是&#xff1a;工具不怕少&#xff0c;就怕东一榔头西一棒子。今天在编辑器里写技术笔记&#xff0c;明天切到另一个笔记软件整理资料&#xff0c;后天又在文档工具里做方案&#xff0c;每切换一次&#xff0c;上下文就断一次&#xff0c;等再回来…

作者头像 李华
网站建设 2026/10/13 19:24:23

35kV三段式电流保护课程设计:整定计算与Simulink仿真全攻略

35kV电力系统三段式电流继电保护课程设计仿真报告&#xff08;本级线路和下级线路相间短路&#xff09;&#xff0c;这类题目在电气工程及其自动化专业的继电保护课程里算是经典大作业了。题目看起来只有一行字&#xff0c;真动手做起来才会发现&#xff0c;整定计算、灵敏度校…

作者头像 李华