MyBatis-Plus多租户隔离自定义注解实现与避坑指南
1. 项目缘起一个看似简单却暗藏玄机的需求最近在重构一个基于Spring Boot和MyBatis-Plus的多租户后台管理系统时遇到了一个挺有意思的“小”需求。系统里大部分数据查询都通过MyBatis-Plus的TenantId注解和内置的租户拦截器自动加上了tenant_id ?的条件这套机制运行得一直很稳定。直到我们需要开发一个“数据看板”模块这个模块需要聚合所有租户的某些统计数据比如全平台的总订单量、总用户增长趋势等。按照常规思路我们可能会想到为这个特定的Mapper方法写一个自定义的SQL在XML里手动去掉租户条件。但这带来两个问题一是破坏了MyBatis-Plus统一管理租户SQL的优雅性二是如果未来有更多类似的“全局”查询需求我们就要在每个Mapper里重复写这种特殊SQL维护起来会是一场噩梦。更关键的是团队里有些对MyBatis-Plus租户机制理解不深的同事可能会在不需要隔离的地方误写自定义SQL或者在需要隔离的地方忘了加条件导致数据错乱。于是一个想法自然浮现能不能像Spring的Transactional注解那样通过一个简单的注解来灵活地控制某个方法是否启用租户隔离呢比如在Mapper接口的某个方法上打上IgnoreTenant执行这个方法时MyBatis-Plus的租户插件就自动“失效”几秒钟。这听起来很美好但MyBatis-Plus官方并没有提供这样的“开关”。官方文档强调租户插件是全局生效的要忽略就得动插件配置或者用SqlParser注解不够灵活。所以“MyBatis-Plus忽略多租户隔离自定义注解”这个需求就成了我们必须自己动手解决的典型场景。它不仅仅是加个注解那么简单背后涉及到对MyBatis-Plus插件机制、SQL解析过程以及线程上下文管理的深入理解。2. 核心原理MyBatis-Plus租户插件是如何工作的要自定义一个忽略租户的注解首先得成为MyBatis-Plus租户插件的“知己”摸清它的工作脉络。MyBatis-Plus的多租户功能其核心是TenantLineInnerInterceptor这个内部拦截器。它属于MyBatis的Interceptor链会在SQL被执行前进行拦截和处理。它的工作流程可以概括为以下几个关键步骤拦截与判断当MyBatis执行一条SQL时无论是通过BaseMapper的通用方法还是自定义的Select等注解方法租户拦截器会首先被触发。它会判断当前执行的SQL是否需要添加租户条件。这个判断依据主要是TenantLineHandler接口的实现。处理器决策TenantLineHandler是你需要实现的核心类其中最重要的方法是getTenantId()和ignoreTable(String tableName)。getTenantId()这个方法返回当前请求的租户ID。通常我们会从当前用户的会话信息如SecurityContextHolder或请求头中获取并将其存储在ThreadLocal中确保线程隔离。ignoreTable(String tableName)这个方法决定哪些表不需要加租户条件。比如系统字典表、配置表等全局表可以在这里直接返回true。SQL解析与增强如果处理器判断当前表需要租户隔离ignoreTable返回false并且成功获取到了租户IDgetTenantId返回非空拦截器就会对原始的SQL语句进行解析。它会识别出SQL中涉及的所有表然后在WHERE条件中为每一个需要隔离的表自动追加类似AND tenant_id your_tenant_id的条件。对于INSERT语句则会自动在插入字段和值中补上租户ID字段和值。执行与清理增强后的SQL会被交给下一个拦截器或直接执行。执行完毕后流程结束。理解了这套机制我们自定义注解的目标就清晰了我们需要一种方式在TenantLineHandler做决策第2步时能够动态地、针对当前执行的方法让ignoreTable方法对所有表临时返回true或者让getTenantId方法临时返回null。这样租户条件就不会被添加。那么如何将方法上的注解信息传递到TenantLineHandler的决策逻辑中去呢这里的关键桥梁就是ThreadLocal。因为一次Web请求通常在一个线程内完成我们可以利用线程上下文来传递“忽略租户”的指令。具体思路是在方法执行前通过AOP面向切面编程拦截带有自定义注解的方法将一个“忽略标记”放入当前线程的ThreadLocal变量中。然后在TenantLineHandler的实现里先检查这个ThreadLocal变量中是否存在“忽略标记”如果存在就做出“忽略”的决策。3. 实战三步构建自定义忽略租户注解理论清晰后我们开始动手实现。整个过程可以分为三个核心步骤定义注解、实现租户处理器逻辑、创建AOP切面。3.1 第一步定义注解IgnoreTenant这个注解本身非常简单它的主要作用是一个“标记”告诉后续的AOP切面“这个方法需要忽略租户隔离”。package com.yourproject.annotation; import java.lang.annotation.*; /** * 忽略多租户隔离注解。 * 标注在Mapper接口的方法上执行该方法时将临时禁用租户SQL拦截。 */ Target(ElementType.METHOD) // 表示该注解只能用在方法上 Retention(RetentionPolicy.RUNTIME) // 注解在运行时保留这样AOP才能获取到 Documented public interface IgnoreTenant { }3.2 第二步增强租户处理器CustomTenantLineHandler这是最核心的一步。我们需要自定义一个TenantLineHandler并在其中加入对ThreadLocal标记的检查。package com.yourproject.handler; import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.StringValue; import org.springframework.stereotype.Component; import java.util.HashSet; import java.util.Set; Component public class CustomTenantLineHandler implements TenantLineHandler { /** * 租户ID字段名根据你的数据库表设计来定 */ private static final String TENANT_ID_COLUMN tenant_id; /** * 用于临时存储“忽略租户”标记的ThreadLocal。 * 使用InheritableThreadLocal是为了在某些异步场景下如Async子线程也能继承这个标记。 * 但需注意线程池复用可能导致数据污染生产环境更推荐使用TransmittableThreadLocal阿里开源。 */ private static final ThreadLocalBoolean IGNORE_TENANT_FLAG new InheritableThreadLocal(); /** * 设置忽略租户标记。 * 由AOP切面调用。 */ public static void setIgnoreTenant() { IGNORE_TENANT_FLAG.set(true); } /** * 清除忽略租户标记。 * 必须由AOP切面在方法执行后清理防止内存泄漏和上下文污染。 */ public static void clearIgnoreTenant() { IGNORE_TENANT_FLAG.remove(); } /** * 获取租户ID的表达式。 * 这里先检查忽略标记。如果标记存在返回null告知拦截器不要添加租户条件。 */ Override public Expression getTenantId() { if (Boolean.TRUE.equals(IGNORE_TENANT_FLAG.get())) { // 存在忽略标记返回null拦截器将不会添加tenant_id条件 return null; } // 正常业务逻辑从当前用户上下文获取租户ID String currentTenantId getCurrentTenantIdFromContext(); // 假设这个方法能从SecurityContext或请求头中获取 if (currentTenantId null) { // 获取不到租户ID可以返回null或抛出异常取决于你的业务设计 return null; } return new StringValue(currentTenantId); } /** * 获取租户ID字段名 */ Override public String getTenantIdColumn() { return TENANT_ID_COLUMN; } /** * 忽略表名单。这里也可以结合忽略标记做动态判断。 * 但更常见的做法是当getTenantId()返回null时拦截器就不会处理所以这里可以只配置静态全局表。 */ Override public boolean ignoreTable(String tableName) { // 静态配置哪些表永远不需要租户隔离如全局配置表 SetString ignoreTables new HashSet(); ignoreTables.add(sys_config); ignoreTables.add(sys_dict); return ignoreTables.contains(tableName.toLowerCase()); } private String getCurrentTenantIdFromContext() { // 实现你的租户ID获取逻辑例如 // SecurityContext context SecurityContextHolder.getContext(); // return context.getAuthentication().getDetails().getTenantId(); // 这里返回一个模拟值 return tenant_001; } }关键点解析IGNORE_TENANT_FLAG是一个静态的ThreadLocal变量它是连接AOP切面和租户处理器的桥梁。getTenantId()方法是核心。它首先检查IGNORE_TENANT_FLAG。如果标记为true直接返回null。MyBatis-Plus的拦截器在收到null的租户ID表达式时就不会为SQL添加租户条件。setIgnoreTenant()和clearIgnoreTenant()是静态方法供AOP切面调用。清理操作至关重要必须放在finally块中执行否则可能导致线程池复用时的数据错乱。3.3 第三步创建AOP切面IgnoreTenantAspectAOP切面负责在标注了IgnoreTenant的方法执行前后操作ThreadLocal中的标记。package com.yourproject.aspect; import com.yourproject.handler.CustomTenantLineHandler; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; Aspect Component Slf4j Order(0) // 确保此切面在事务切面等之前执行租户过滤应在最外层 public class IgnoreTenantAspect { /** * 环绕通知拦截所有被IgnoreTenant注解的方法。 * 切入点表达式指向我们自定义的注解。 */ Around(annotation(com.yourproject.annotation.IgnoreTenant)) public Object aroundIgnoreTenantMethod(ProceedingJoinPoint joinPoint) throws Throwable { // 方法执行前设置忽略租户标记 CustomTenantLineHandler.setIgnoreTenant(); log.debug(Ignore tenant isolation for method: {}, joinPoint.getSignature().toShortString()); try { // 执行原方法 return joinPoint.proceed(); } finally { // 方法执行后无论成功或异常必须清除标记 CustomTenantLineHandler.clearIgnoreTenant(); log.debug(Cleared ignore tenant flag for method: {}, joinPoint.getSignature().toShortString()); } } }关键点解析Around注解定义了切面逻辑annotation(...)指定了拦截条件。在try块之前调用setIgnoreTenant()确保方法体内的数据库操作生效。finally块中调用clearIgnoreTenant()是保证健壮性的生命线。这能防止因为方法抛出异常导致标记未被清除进而污染后续使用同一线程的请求。3.4 配置与使用最后在MyBatis-Plus的配置类中使用我们自定义的CustomTenantLineHandler。package com.yourproject.config; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import com.yourproject.handler.CustomTenantLineHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor(CustomTenantLineHandler tenantLineHandler) { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 添加租户拦截器并传入我们自定义的处理器 TenantLineInnerInterceptor tenantInterceptor new TenantLineInnerInterceptor(tenantLineHandler); interceptor.addInnerInterceptor(tenantInterceptor); // 可以继续添加其他拦截器如分页插件 // interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } }现在你就可以在Mapper接口的方法上使用IgnoreTenant注解了public interface OrderMapper extends BaseMapperOrder { IgnoreTenant Long selectTotalOrderCount(); // 其他方法默认受租户隔离保护 ListOrder selectListByUser(Long userId); }调用selectTotalOrderCount()方法时执行的SQL将不会包含tenant_id条件从而实现跨租户的数据统计。4. 避坑指南那些我踩过的雷和进阶思考实现本身并不复杂但在实际生产环境中应用时有几个坑需要特别注意。4.1 线程池与ThreadLocal的“脏数据”问题这是最大的一个坑。我们的实现依赖于ThreadLocal。在标准的同步Web请求中如Spring MVC一个请求从头到尾在一个线程处理ThreadLocal工作良好。但是一旦涉及异步编程如使用Async注解、CompletableFuture、消息监听器等任务会被提交到线程池执行。线程池中的线程是复用的。问题场景请求A调用了带有IgnoreTenant的方法在ThreadLocal中设置了标记。方法执行完毕后如果清理操作clearIgnoreTenant()因为某些原因如切面Order顺序问题、异常未被捕获没有执行那么这个标记会残留在该线程的ThreadLocal中。当这个线程被线程池回收用于处理请求B时请求B并没有调用忽略租户的方法但由于残留的标记它的所有数据库操作也会忽略租户导致严重的数据安全问题解决方案强制清理确保AOP切面的Order值足够高数值小使其在最外层执行并且clearIgnoreTenant()必须放在finally块中。这是底线。使用InheritableThreadLocal如示例代码所示它允许子线程继承父线程的ThreadLocal值。这解决了简单的new Thread()创建子线程的问题但对于线程池子线程是复用的老线程继承只发生在线程创建时复用时不继承问题依旧。使用阿里开源的TransmittableThreadLocalTTL这是生产环境的推荐方案。TTL专门解决了线程池场景下的上下文传递问题。你需要将项目中的ThreadLocal替换为TransmittableThreadLocal并在提交异步任务时使用TTL的包装器如TtlRunnable或TtlCallable。// 使用TTL改造CustomTenantLineHandler import com.alibaba.ttl.TransmittableThreadLocal; public class CustomTenantLineHandler implements TenantLineHandler { // 使用TransmittableThreadLocal替代InheritableThreadLocal private static final TransmittableThreadLocalBoolean IGNORE_TENANT_FLAG new TransmittableThreadLocal(); // ... 其他代码不变 }4.2 嵌套调用与注解失效另一个常见问题是嵌套调用。Spring AOP默认使用基于代理的AOP。当一个类中的methodA()无注解调用了同一个类中的methodB()有IgnoreTenant注解时methodB()上的注解可能会失效。原因methodA()调用methodB()是内部调用this.methodB()并没有经过Spring创建的代理对象因此AOP切面无法拦截到这次调用。解决方案自我注入推荐在类中注入自己的代理实例。Service public class DashboardService { Autowired private DashboardService selfProxy; // 注入自己 public Long getGlobalStat() { // 通过代理对象调用触发AOP return selfProxy.getTotalCount(); } IgnoreTenant public Long getTotalCount() { // ... 执行忽略租户的查询 } }需要在配置类上添加EnableAspectJAutoProxy(exposeProxy true)并使用(DashboardService) AopContext.currentProxy()获取代理但自我注入更清晰。重构代码将带有IgnoreTenant注解的方法抽取到另一个Bean中通过Bean之间的调用来触发AOP。4.3 与MyBatis-Plus其他插件的交互顺序MyBatis-Plus的拦截器InnerInterceptor是有执行顺序的。TenantLineInnerInterceptor租户通常应该在PaginationInnerInterceptor分页之前执行。因为先添加租户条件再基于这个完整的结果集进行分页计算才是正确的逻辑。我们的自定义逻辑通过影响TenantLineHandler来工作所以不改变拦截器顺序但需要知晓这个背景。如果你的项目还使用了动态表名等插件也需要考虑它们的执行顺序确保SQL的组装逻辑符合预期。4.4 注解的粒度控制与组合使用我们目前的IgnoreTenant注解是方法粒度的。有时我们可能希望更精细的控制比如忽略特定表的租户条件可以设计注解IgnoreTenantForTables({table1, table2})在TenantLineHandler.ignoreTable方法中读取注解值进行动态判断。这需要更复杂的AOP设计可能要将注解信息也存入ThreadLocal。在Controller层或Service层使用目前注解用在Mapper层最直接。如果想在Service层使用需要确保Service方法内所有的数据库操作可能涉及多个Mapper调用都在同一个IgnoreTenant上下文中。我们的当前实现可以支持因为标记是线程级别的。只需将切面切入点改为拦截Service方法即可Around(annotation(com.yourproject.annotation.IgnoreTenant) || within(com.yourproject.annotation.IgnoreTenant))后者支持类级别注解。5. 方案对比为什么不直接用官方方案或SQL注解在实现自定义注解之前MyBatis-Plus也提供了其他方式来忽略租户了解它们的优缺点能让我们更清楚自定义注解的价值。方案实现方式优点缺点适用场景官方ignoreTable方法在自定义的TenantLineHandler中硬编码表名。配置简单全局生效。不灵活无法根据方法或上下文动态忽略。新增一个需要忽略的方法就要改代码。确定永远不需要租户隔离的静态全局表如字典表。官方SqlParser注解在Mapper方法或类上添加SqlParser(filter true)。MyBatis-Plus原生支持。此注解在3.4.0及以上版本已被标记为废弃。它关闭的是整个SQL解析过滤器可能影响其他插件如分页优化。粒度太粗不推荐使用。旧版本项目临时使用新项目避免。自定义SQLXML/注解在Mapper.xml中写完整的SQL不依赖BaseMapper。绝对控制灵活度高。1.破坏统一性租户逻辑分散在SQL中难以维护。2.容易出错开发人员容易忘记在需要隔离的地方加条件。3.重复劳动每个需要忽略的方法都要写特殊SQL。极其复杂、性能要求极高的特定SQL场景。自定义注解本文方案通过AOPThreadLocal动态控制TenantLineHandler行为。1.声明式、优雅一个注解搞定意图清晰。2.集中管理租户忽略逻辑集中在处理器和切面中。3.灵活动态可根据方法、参数甚至运行时条件决定是否忽略。1. 增加了AOP和ThreadLocal的复杂度。2. 需要处理好异步场景下的上下文传递问题。绝大多数需要动态忽略租户的场景如全局报表、数据导出、后台运营查询等。对比下来自定义注解方案在灵活性、可维护性和开发体验上取得了很好的平衡。它遵循了“约定优于配置”和“关注点分离”的原则将“是否忽略租户”这个业务决策通过注解清晰地表达出来而将复杂的实现细节隐藏在了框架层。6. 总结与最佳实践建议实现一个健壮的“忽略多租户隔离自定义注解”远不止是加上几行AOP代码那么简单。它要求我们对MyBatis-Plus的插件机制、Spring AOP的工作原理以及并发编程中的线程上下文管理有深入的理解。回顾整个实现过程有几个关键点值得再次强调明确需求边界这个注解是为了解决“特定方法需要全局视角”的问题而不是用来逃避设计合理的租户数据架构。滥用此注解会导致数据隔离形同虚设。线程安全是生命线务必使用TransmittableThreadLocal处理异步场景并在AOP切面中无条件清理ThreadLocal。测试要全面不仅要测试注解生效时的SQL更要测试注解不生效时的SQL是否正确隔离。还需要模拟高并发和异步调用场景验证是否存在数据污染。文档与规范在团队内明确该注解的使用规范和场景避免滥用。可以考虑将注解放在一个独立的模块或包中并编写清晰的README。监控与告警可以在CustomTenantLineHandler的setIgnoreTenant和clearIgnoreTenant方法中加入日志或Metrics上报监控生产环境中哪些方法、在什么时间频繁触发了忽略租户逻辑用于审计和性能分析。最后技术方案没有银弹。本文介绍的自定义注解方案在大多数Spring Boot MyBatis-Plus的多租户项目中是一个优雅且实用的选择。但它也引入了额外的复杂度。如果你的项目结构简单异步场景少或者需要忽略租户的场景极少那么谨慎地使用“自定义SQL”或“静态忽略表”的方式也未尝不可。关键在于作为开发者我们要理解每种方案背后的权衡并根据自己项目的实际情况做出最合适的选择。

相关新闻

紫微斗数排盘入门:从生辰八字到命盘搭建的八步详解

紫微斗数排盘入门:从生辰八字到命盘搭建的八步详解

1. 从零开始:一张白纸到命盘的诞生很多人一听到“紫微斗数排盘”,脑海里立刻浮现出天干地支、星曜宫位这些复杂术语,感觉像在看天书,下意识就觉得这是玄学大师的专属技能。其实,这完全是个误解。排盘本身,本…

2026/8/17 8:57:24 阅读更多 →
LeakCanary原理全解析:Android内存泄漏自动化检测与实战指南

LeakCanary原理全解析:Android内存泄漏自动化检测与实战指南

1. 项目概述:为什么我们需要一个“内存泄漏捕手”? 在Android开发这个行当里,内存泄漏(Memory Leak)是个老生常谈却又让人头疼不已的问题。它不像空指针异常那样会立刻导致应用崩溃,给你一个明确的错误堆栈…

2026/8/17 8:57:24 阅读更多 →
Linux系统重启记录查看:last、uptime与日志排查全解析

Linux系统重启记录查看:last、uptime与日志排查全解析

1. 项目概述:为什么我们需要查看Linux重启记录?在日常运维、系统排障或者安全审计工作中,有一个看似简单却至关重要的需求:搞清楚这台Linux服务器什么时候重启过,以及为什么重启。无论是半夜被报警叫醒,发现…

2026/8/17 8:56:24 阅读更多 →

最新新闻

Excel VLOOKUP函数深度解析:从核心原理到高效数据匹配实战

Excel VLOOKUP函数深度解析:从核心原理到高效数据匹配实战

1. 从“大海捞针”到“精准定位”:VLOOKUP为何是Excel的“定海神针”如果你在办公室里问,哪个Excel函数最让人又爱又恨,VLOOKUP大概率会高票当选。爱它,是因为它确实能解决工作中最频繁、最头疼的跨表查询问题;恨它&am…

2026/8/17 9:34:49 阅读更多 →
Jenkins SSH连接配置详解:从原理到实战的自动化部署指南

Jenkins SSH连接配置详解:从原理到实战的自动化部署指南

1. 从“手动传包”到“一键部署”:为什么Jenkins SSH连接是自动化部署的基石如果你和我一样,经历过手动登录服务器、上传War包、重启Tomcat、再刷新页面看日志的“石器时代”部署流程,那你一定明白,当项目迭代到一天需要发布好几次…

2026/8/17 9:34:49 阅读更多 →
Spring-Kafka 3.x 消费失败处理全解析:从反序列化到死信队列实战

Spring-Kafka 3.x 消费失败处理全解析:从反序列化到死信队列实战

1. 从一次线上告警说起:为什么消费失败不是小事 那天晚上十一点,手机突然开始疯狂震动。打开监控平台一看,核心订单处理服务的消息积压告警亮起了红灯。Kafka Topic order-events 的 Lag(消费延迟)从平时的几十条&am…

2026/8/17 9:34:49 阅读更多 →
F28335 Flash代码固化:从RAM调试到独立运行的全流程解析

F28335 Flash代码固化:从RAM调试到独立运行的全流程解析

1. 从RAM到Flash:为什么F28335的代码下载是个技术活如果你刚开始玩TI的DSP,尤其是像F28335这种经典的C2000系列,大概率会经历这么一个阶段:在CCS里写代码、编译、点一下调试按钮,程序跑起来了,一切都很美好…

2026/8/17 9:34:49 阅读更多 →
终端基准测试实战:从概念到成本优化,实现性能与成本平衡

终端基准测试实战:从概念到成本优化,实现性能与成本平衡

最近在技术社区看到不少关于终端性能测试和成本优化的讨论,特别是“Terminal bench 2.1: Same score, 16% cheaper run Lemoncrow”这个标题,引发了很多开发者对如何评估和优化本地开发环境或服务器终端性能的兴趣。对于后端开发、运维和追求效率的工程师…

2026/8/17 9:34:49 阅读更多 →
Jenkins SSH连接远程服务器:自动化部署的完整配置与实战指南

Jenkins SSH连接远程服务器:自动化部署的完整配置与实战指南

1. 项目概述:为什么Jenkins连接远程服务器是自动化部署的基石 如果你正在用Jenkins做自动化构建,但构建出来的包、镜像或者测试报告还停留在本地,那这个自动化流程的价值就大打折扣了。真正的自动化,是从代码提交开始,…

2026/8/17 9:33:48 阅读更多 →

日新闻

LabVIEW异步调用实战:从原理到生产者消费者模式,解决界面卡顿与并行处理难题

LabVIEW异步调用实战:从原理到生产者消费者模式,解决界面卡顿与并行处理难题

1. 项目概述:为什么异步调用是LabVIEW进阶的必修课? 如果你用LabVIEW做过稍微复杂点的项目,尤其是涉及界面响应、多任务并行或者硬件IO等待的场景,大概率遇到过这样的窘境:前面板点个按钮,整个程序就“卡死…

2026/8/17 0:00:08 阅读更多 →
LabVIEW异步调用实战:解决界面卡顿与并行处理难题

LabVIEW异步调用实战:解决界面卡顿与并行处理难题

1. 项目概述:为什么异步调用是LabVIEW进阶的必经之路如果你在LabVIEW里写过稍微复杂点的程序,尤其是涉及到界面响应、多任务并行或者硬件IO等待,大概率会遇到一个头疼的问题:程序“卡”住了。前面板点不动,进度条不更新…

2026/8/17 0:00:08 阅读更多 →
飞书局域网文件传输实战:3种方案实现高速点对点传输

飞书局域网文件传输实战:3种方案实现高速点对点传输

1. 项目概述:为什么要在局域网内用飞书传文件? 飞书作为一款主流的协同办公套件,其核心功能是围绕云端协作设计的。无论是文档、表格还是文件,通常的分享逻辑都是“上传到云端 -> 生成链接 -> 分享给同事”。这个流程在互联…

2026/8/17 0:00:08 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/17 2:58:27 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/17 2:58:30 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/17 2:58:32 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/16 6:00:23 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/16 6:00:24 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/16 6:00:27 阅读更多 →