当前位置: 首页 > article >正文

@SpringBootApplication 与 SPI 机制的终极解密

敲代码离不开springboot少了springboot谁还来替我当牛马——ai欢迎来到 Spring Boot 的“后台控制室”刚开始、小白的你是否曾有过这样的错觉“我就加了一个SpringBootApplication注解连application.properties都没怎么写Redis 连接池有了MySQL 驱动配好了Tomcat 启动了甚至连 Swagger 文档都生成了……这难道不是魔法吗”这就是预定大于配置的魅力“工业化流水线”的极致体现如果把传统的 SSM 框架比作“手工打造跑车”你需要自己选引擎、装轮胎、接线路那么 Spring Boot 就是“全自动无人工厂”。你只需要按下启动按钮Main 方法工厂内部的机械臂SPI 机制就会根据预设的图纸Starter自动把零件组装好。今天我们要拆掉工厂的围墙看看里面的机械臂到底是怎么运作的。我们要手撕源码彻底搞懂SPI (Service Provider Interface)和条件装配的黑盒逻辑。核心痛点为什么它“全知全能”场景一配置地狱的终结者在 Spring Boot 之前为了整合 MyBatis你需要引入 jar 包。写SqlSessionFactoryBean。配置MapperScannerConfigurer。写一堆 XML 或 Java Config。现在引入mybatis-spring-boot-starter搞定。疑问它怎么知道我要连 MySQL 还是 Oracle它怎么知道我的 Mapper 接口在哪场景二启动速度的秘密Spring 容器要扫描成千上万个类。如果每个 Starter 里的几百个配置类都无脑加载启动得慢成蜗牛。事实哪怕你引入了 50 个 Starter启动依然飞快。疑问它是如何做到“只加载我需要的”而把不需要的统统过滤掉的生活化比喻传统 Spring相亲角。大妈容器拿着大喇叭喊“谁实现了 DataSource 接口站出来”几百个人类同时举手大妈得一个个问“你有 MySQL 驱动吗有 HikariCP 吗”累得半死。Spring Boot SPI猎头公司的 VIP 名单。大妈容器不去现场喊人而是直接去META-INF目录下拿一份加密名单spring.factories或.imports。名单上写着“如果classpath下有 MySQL 驱动就请‘MySQL 配置专员’上岗如果有 Redis 驱动就请‘Redis 配置专员’上岗。”结果大妈只看名单按需招人效率极高绝不浪费口舌。源码追踪从 main() 到 loadFactories() 的奇幻漂流让我们戴上显微镜跟随SpringApplication.run()的脚步看看魔法是如何发生滴1. 入口SpringApplication.run()如果这都感觉陌生只能拉出去了——人还是有点底线public static ConfigurableApplicationContext run(Class? primarySource, String... args) { return new SpringApplication(primarySource).run(args); }2. 准备环境prepareEnvironment()这一步主要加载配置文件暂时跳过。3. 创建上下文createApplicationContext()根据类型创建AnnotationConfigServletWebServerApplicationContext。4.关键步骤prepareContext()-load()这里会调用load方法将你的主配置类加载进去。5.核心高潮refreshContext()-invokeBeanFactoryPostProcessors()在容器刷新过程中有一个至关重要的处理器ConfigurationClassPostProcessor。它会解析Configuration类。而你的SpringBootApplication本质上包含了EnableAutoConfiguration。重点来了EnableAutoConfiguration导入了一个关键的选择器Import(AutoConfigurationImportSelector.class) public interface EnableAutoConfiguration { ... }6. 揭秘时刻AutoConfigurationImportSelector.selectImports()这个类是真正的“猎头总管”。它的核心逻辑如下简化版伪代码public String[] selectImports(AnnotationMetadata annotationMetadata) { // 1. 获取所有候选配置类的名单 // 在 Spring Boot 2.7 中优先读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports // 在旧版本中读取 META-INF/spring.factories ListString configurations getCandidateConfigurations(annotationMetadata, AutoConfigurationEntry.class); // 2. 【去重与排序】确保顺序正确 configurations sort(configurations, autoConfigurationMetadata); // 3. 【核心过滤】这就是性能优化的关键 // 使用 ConditionalOnXxx 注解过滤掉不满足条件的配置类 configurations filter(configurations, autoConfigurationMetadata); // 4. 返回最终需要加载的类名数组 return StringUtils.toStringArray(configurations); }看到了吗所谓的“自动装配”就是读取名单 - 排序 - 过滤 - 加载的过程。机制详解Java SPI vs Spring SPI在这之前我问一句你敢答应吗“Spring Boot 的 SPI 和 Java 原生的 SPI 有什么区别” 这是一个坑跳进去就出不来下面说一下历程不得不说新设计真的很睿智Java 原生 SPI (ServiceLoader)位置META-INF/services/接口全限定名内容直接列出实现类的全限定名。缺点无延迟加载一旦调用ServiceLoader.load()所有实现类会被实例化。哪怕你只用其中一个其他的也会报错或浪费资源。无条件过滤无法根据环境有没有某个类、有没有某个 Bean来决定加载谁。Key-Value 缺失只能存类名不能存元数据。2. Spring SPI (SpringFactoriesLoader)位置META-INF/spring.factories(Boot 2.x) 或META-INF/spring/*.imports(Boot 3.x)。内容KeyValue格式。Key 是接口/注解Value 是实现类列表逗号分隔。进化优势解耦通过 Key 分类管理不仅仅是 SPI还能管理监听器、初始化器等。延迟与过滤Spring 拿到名单后不会立即实例化而是先解析类上的Conditional注解。只有条件满足才真正加载并实例化。有序性支持AutoConfigureBefore,AutoConfigureAfter控制加载顺序。忍不住感叹Java 原生的 SPI 就像“盲目招聘”只要简历投进来在文件里HR 就全部发 Offer 入职不管公司需不需要。Spring 的 SPI 就像“精准猎头”拿到简历后先做背景调查检查 ClassPath、检查 Bean符合条件的才发 Offer。这就是为什么 Spring Boot 能加载上百个 Starter 却不卡顿的原因。条件装配性能优化的“守门员”如果没有条件装配引入redis-starter就会强制加载 Redis 配置哪怕你根本没装 Redis 驱动启动直接报ClassNotFoundException。条件注解是 Spring Boot 的灵魂。它们在parse阶段起作用直接拦截不符合条件的配置类。常用条件注解大赏注解含义典型应用场景ConditionalOnClass类路径下有指定类时才生效RedisAutoConfiguration只有在Jedis.class存在时才加载。ConditionalOnMissingBean容器中没有指定 Bean 时才生效用户自己定义了DataSource自动配置就不创建了避免冲突。ConditionalOnProperty配置文件中有指定属性时才生效server.port8080存在时才配置 Tomcat 端口。ConditionalOnWebApplication当前是 Web 环境时才生效只有 Web 项目才加载 SpringMVC 相关配置。ConditionalOnSingleCandidate容器中只有一个该类型的 Bean 时生效确保不会产生歧义。源码级过滤逻辑在AutoConfigurationImportSelector的filter方法中Spring 会利用OnClassCondition,OnBeanCondition等内部类模拟加载环境判断条件是否成立。不成立的配置类连 Class 都不会被加载到 JVM 中这才是极致的性能优化。实战演练手写一个“防脱发”Starter光看不练假把式。我们来手写一个自定义 Startercorn-care-spring-boot-starter。功能自动检测项目中是否有Shampoo类如果有自动创建一个 CornCareServiceBean并打印“正在...”。⚠️ 代码手动修改过但是不影响看第一步创建普通 Maven 模块hair-care-spring-boot-starterdependencies !-- ………… -- !-- 核心依赖提供自动装配能力 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-autoconfigure/artifactId optionaltrue/optional /dependency !-- 可选依赖模拟业务库实际使用时由引入方决定要不要加 -- dependency groupIdcom.example/groupId artifactIdshampoo-lib/artifactId optionaltrue/optional /dependency !-- 方便测试 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter/artifactId /dependency /dependencies第二步编写业务逻辑类 (Service)package com.example.haircare.service; public class CornCareService { public void care() { System.out.println( [HairCare] 检测到活动正在为您自动获取金币... 丝滑!); } }第三步编写自动配置类 (AutoConfiguration) ——核心中的核心package com.example.haircare.autoconfigure; import com.example.haircare.service.CornCareService; import com.example.shampoo.Shampoo; // 假设这是第三方库的类 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration // 1. 只有当 classpath 下有 Shampoo 类时这个配置类才生效 ConditionalOnClass(Shampoo.class) // 2. 只有当配置文件开启了 hair.care.enabledtrue (默认 true) 时才生效 ConditionalOnProperty(prefix corn.care, name enabled, havingValue true, matchIfMissing true) public class CornCareAutoConfiguration { // 3. 只有当容器中没有 CornCareService 这个 Bean 时才创建默认的 // 这样用户就可以自己定义一个 Bean 来覆盖默认行为 Bean ConditionalOnMissingBean(CornCareService.class) public CornCareService cornCareService() { return new CornCareService(); } }第四步注册 SPI 名单 (魔法发生的地方)在src/main/resources下创建目录结构META-INF/spring/(Spring Boot 3.x 推荐) 或META-INF/(2.x)。文件路径src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(注如果是 Spring Boot 2.7 及以下文件名为META-INF/spring.factories)com.example.haircare.autoconfigure.HairCareAutoConfiguration (如果是 spring.factories 格式则是 org.springframework.boot.autoconfigure.EnableAutoConfigurationcom.example.haircare.autoconfigure.CornrCareAutoConfiguration)这就相当于告诉 Spring Boot“嘿我是HairCareAutoConfiguration启动的时候记得看看我符不符合条件符合就把我加载了。”第五步在另一个项目中测试新建一个 Spring Boot 项目demo-app。dependency groupIdcom.example/groupId artifactIdcorn-care-spring-boot-starter/artifactId version1.0.0/version /dependency !-- 只有引入了这个Shampoo 类才会存在自动配置才会生效 -- dependency groupIdcom.example/groupId artifactIdshampoo-lib/artifactId version1.0.0/version /dependency2. 编写测试代码SpringBootApplication RestController public class DemoApplication { Autowired(required false) // 允许为 null测试用 private CornCareService cornCareService; public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } GetMapping(/hair) public String doCorn() { if (cornCareService ! null) { cornCareService.care(); return 参与成功; } else { return 未检测到活动领取服务未启动。; } } }3. 验证场景场景 A引入了shampoo-lib。结果访问/hair- 输出“护发成功”控制台打印“正在为您自动参与...”。原理ConditionalOnClass(Shampoo.class)成立配置类加载Bean 创建。场景 B移除shampoo-lib依赖。结果访问/hair- 输出“未检测到活动...”。原理Shampoo类不存在配置类直接被跳过零开销。场景 C用户自定义 Bean。Configuration public class MyConfig { Bean public CornCareService myCustomService() { return () - System.out.println( 自定义高端活动 SPA!); } }结果输出“自定义高端活动 SPA!”。原理ConditionalOnMissingBean检测到已有 Bean默认配置不执行。interview‌题SPI 与自动装配Q1: Spring Boot 的自动装配原理是什么核心是SPI 机制 条件注解。Spring Boot 启动时EnableAutoConfiguration通过AutoConfigurationImportSelector加载META-INF/spring.factories(或.imports) 中配置的所有自动配置类。加载过程中利用ConditionalOnClass,ConditionalOnMissingBean等注解进行按需过滤。只有满足条件的配置类才会被解析并注册到 Spring 容器中从而实现“约定优于配置”的自动化装配。Q2: 为什么要自定义 Starter和普通依赖有什么区别普通依赖只是引入了 Jar 包用户需要手动配置 Bean、XML 或 Java Config容易出错且重复劳动。自定义 Starter将“依赖 自动配置逻辑”打包。用户只需引入 Starter无需任何配置即可使用默认功能同时也保留了通过ConditionalOnMissingBean进行自定义扩展的能力。这是封装复用和降低接入成本的最佳实践。Q3: Spring Boot 2.7 和 3.0 在 SPI 机制上有什么变化2.7 及以前主要使用META-INF/spring.factories文件Key 是EnableAutoConfigurationValue 是类列表。所有类型的 SPI 都在一个文件里解析时需要遍历 Key。3.0 (Jakarta EE)为了性能和规范废弃了spring.factories用于自动配置。改为在META-INF/spring/目录下为每个接口单独建立文件如org.springframework.boot.autoconfigure.AutoConfiguration.imports。优势减少了文件解析的 IO 开销不需要读取整个大文件再过滤 Key直接读取目标文件即可启动速度进一步提升。Q4: 如果两个 Starter 都配置了同一个 Bean会发生什么如何解决现象Spring 容器启动报错BeanDefinitionOverrideException(默认不允许覆盖) 或者后面的覆盖前面的 (取决于配置)。解决最佳实践在自动配置类中使用ConditionalOnMissingBean。这样如果用户或其他 Starter 已经定义了该 Bean当前的自动配置就会失效避免冲突。调整顺序使用AutoConfigureBefore或AutoConfigureAfter明确加载顺序。允许覆盖设置spring.main.allow-bean-definition-overridingtrue(不推荐容易掩盖问题)。“自动装配不是魔法是基于‘约定优于配置’Convention over Configuration的极致工程化实现。理解 SPI你就理解了 Spring 生态疯狂扩张的基石。”“好的 Starter 设计应该像空气一样平时感觉不到它的存在零配置但当你需要时它无处不在自动生效当你想定制时它随时退让ConditionalOnMissingBean。”“不要为了炫技而滥用自动装配。如果一个配置逻辑复杂多变显式的 Java Config 往往比隐式的魔法更易于维护和调试。透明永远是架构的第一原则。”

相关文章:

@SpringBootApplication 与 SPI 机制的终极解密

敲代码离不开springboot,少了springboot谁还来替我当牛马——ai欢迎来到 Spring Boot 的“后台控制室”~刚开始、小白的你是否曾有过这样的错觉:“我就加了一个 SpringBootApplication 注解,连 application.properties 都没怎么写…...

1.2指令系统-存储系统-cache

一、指令系统 00:02 1. 计算机指令的组成 00:03 基本结构:由操作码和操作数两部分组成,以二进制编码形式存放在存储器中操作码:决定要完成的操作(如加法、减法),用二进制数码表示操…...

水厂、电站、化工厂用的闸阀一样吗?

闸阀是工业里最常用的全开全关型阀门,很多人以为闸阀都是通用的,不管哪个行业都是同一种。但实际上,水厂、电站、化工厂的工况天差地别,对应的闸阀在材质、压力、密封等标准上完全不一样,用错了轻则可能漏液&#xff0…...

S7-1200 PLC 高级语言SCL数控G代码功能块源文件

S7-1200PLC 高级语言SCL数控G代码功能块源文件 整个G代码解析的程序做成了一个FB功能块,利用1200PLC内置的字符串控制指令来实现拆分提取字符串信息;整个程序的大概思路就是1.解析指令 2.提取数据 3.判断书否输入有错误 把提取出来的数据对应上并且赋…...

新概念英语第一册037_Making a bookcase

Lesson 37: Making a bookcase. Watch the story and answer the question What is Susan’s favourite colour? Pink.Key words and expressions work 工作hard adv. 努力地make 做bookcase 书橱,书架hammer 锤子paint …...

【AI】创建 claude code cli 风格的欢迎界面

使用高级 ANSI 字体生成器(Text to ANSI) 不要局限于 Python 自带的 pyfiglet 字体。你可以去一些专业的 ANSI Art 网站生成文本,然后直接把生成的字符串复制到代码里。 去 TAAG (Text to ASCII Art Generator)。 尝试使用 Sub-Zero、ANSI …...

【网络安全入门】一文讲透:核心属性、主流攻击手法与防御体系

网络安全(Network Security)是指通过采取必要措施,防范对网络的攻击、侵入、干扰、破坏和非法使用以及意外事故,使网络处于稳定可靠运行的状态,以及保障网络数据的完整性、保密性、可用性的能力。通俗来说,…...

OpenClaw:新一代AI Agent开发平台,让聊天更智能

OpenClaw:新一代AI Agent开发平台 OpenClaw是一个强大的AI Agent开发框架,支持多种通信渠道。 核心特性 多渠道支持内置浏览器控制强大的记忆系统 为什么选择OpenClaw? 开源免费:基于MIT协议,完全开源易于扩展&am…...

不断提升维修技能是医疗器械维修工程师职业发展更好的必要条件

凛冬已过,春山可望,在医疗维修行业经过这两年的洗牌,未来将变得更干净、透明,留下的也将是更健康且有活力的行业生态。长远来看医疗器械售后维修这块更是走向高质量发展路径。不断提升维修技能是医疗器械维修工程师职业发展更好的…...

作业:​在AI工具的辅助下,创建一个校园管理系统——主营方向是二手物品交易

-- 1. 插入用户信息(12条) INSERT INTO user_info (user_no, user_name, user_type, user_phone, user_college) VALUES (20240101, 张三, student, 13800138001, 计算机学院), (20240102, 李四, student, 13800138002, 电子工程学院), (20240103, 王五,…...

AI私域获客生产厂家

一、公司概况 上海超客多多智能科技有限公司是一家专注于AI驱动企业增长的科技服务公司。其核心使命是助力中小企业、工厂型企业和实体商家,以更低的成本、更少的人力构建一套可复制、可规模化的线上获客与成交体系。二、核心产品 - AI自运转百倍获客体系整体功能 这…...

别只盯着银含量——银包镍粉的性能密码藏在核芯形貌里

引言买银包镍粉,很多采购工程师第一个问题是:"银含量多少?"这个问题没错,但只问这一个,你很可能选错料。在银包镍粉的世界里,有一个反直觉的核心规律:决定产品性能上限的,…...

前后端数据加密传输,crypto前端加密后端解密 js+java

前端页面引用js,注意引用顺序&#xff0c;否则可能会报错。 <script type"text/javascript" src"${ctx!}/static/js/crypto-js.js"></script> <script type"text/javascript" src"${ctx!}/static/js/aes.js"></s…...

STM32_TIM_定时器

文章目录一、定时器二、基本定时器   1、时钟源   2、计数器时钟   3、计数器   4、自动重装载寄存器   5、定时时间的计算三、高级定时器   1、高级定时器框图   2、高级定时器引脚分布   3、高级定时器功能图     1.时钟源     2.控制器     3.时…...

红队打点,蓝队断后!万字长文拆解内网攻防实战中的那些关键技战术

我们来聊聊网络安全领域中一个非常重要的实战概念&#xff1a;红蓝对抗。 简单来说&#xff0c;红蓝对抗是一种网络安全的实战攻防演练。它借鉴了军事演习的概念&#xff0c;旨在通过模拟真实的网络攻击&#xff0c;来检验和提升一个组织&#xff08;比如一家公司、一个单位甚…...

JS 开发问题:url.includes is not a function

在 JavaScript 开发中&#xff0c;出现如下错误信息 Uncaught TypeError: url.includes is not a function问题原因 这个错误是&#xff0c;尝试调用 url 的 includes 方法&#xff0c;但 url 不是一个字符串 问题复现 例如&#xff0c;url 是一个数字 let url 123;if (u…...

基于深度学习的征信报告结构化提取技术架构与实践

在金融科技&#xff08;FinTech&#xff09;领域&#xff0c;信贷风控系统的核心在于数据。然而&#xff0c;作为风控最关键的数据源之一&#xff0c;人行征信报告在部分场景下会以非结构化或半结构化的 PDF/图片形式存在。 对于开发者而言&#xff0c;如何将这些非结构化文档…...

JavaEE零基础入门指南

JavaEE零基础完整入门指南 一、JavaEE概述与学习路径规划 1.1 JavaEE基本概念 JavaEE&#xff08;Java Platform, Enterprise Edition&#xff09;是Sun公司&#xff08;现Oracle&#xff09;推出的企业级应用开发平台&#xff0c;主要用于构建大规模、分布式、多层次的企业…...

高压直流输电在线监测Matlab仿真模型:包含故障监测与GUI界面参数设置功能

高压直流输电在线监测Matlab仿真模型 本设计对故障监测&#xff0c;同时设置了GUI界面&#xff0c;可以设置参数等等一、系统开发背景与核心目标 在电力系统“强直弱交”特性持续增强的背景下&#xff0c;大规模直流互联引发的送、受端交流系统相互影响日益凸显&#xff0c;对…...

从手工账本到数字时代:美业管理者的进化之路

凌晨十一点&#xff0c;美发店的镜子前&#xff0c;发型师还在为一位延迟的顾客整理头发&#xff1b;隔壁美容院的咨询间里&#xff0c;顾问耐心地解释着会员卡权益&#xff1b;收银台前&#xff0c;店长翻看着厚厚的记录本&#xff0c;计算着当天的业绩。这样的场景&#xff0…...

深入解析 LangGraph Checkpoint

一、LangGraph Checkpoint 的核心设计目标 LangGraph Checkpoint 解决的并不是简单的“存储状态”问题&#xff0c;而是 复杂工作流系统中的可恢复执行问题。 从架构角度看&#xff0c;它承担了四个关键职责&#xff1a; 1️⃣ 持久化状态管理 保存 Graph 的完整状态&#x…...

Harmonyos应用实例128:正方体展开图辨识

应用实例八:正方体展开图辨识 知识点:第四章《几何图形初步》—— 立体图形的展开图。 功能:展示各种平面图形(1-4-1型、2-3-1型等)。学生判断该图形能否围成正方体。点击"折叠"按钮,演示3D折叠动画验证答案,培养空间想象能力。 /*** 正方体平面展开图与折…...

历时100天,亿元Cocos小游戏实战合集顺利完结!!!

引言 哈喽大家好&#xff0c;我是亿元程序员。 还记得100天前&#xff0c;在《100个Cocos实例》合集完成三分之二时&#xff0c;曾暗暗“发誓”&#xff1a;一定要启动一个更聚焦、更实用的新系列。 于是&#xff0c;《亿元Cocos小游戏实战合集》就此诞生。 这个系列&#…...

openclaw 飞书表情包发送器

openclaw 飞书表情包发送器 github地址 功能 1.在回复了用户消息后,自动调用接口发送表情包图片2.支持概率命中,即概率发送&#xff08;默认60%概率&#xff09;3.不影响主回复消息 使用教程 1.需要依赖飞书官方的插件&#xff08;非openclaw原生飞书插件&#xff09; np…...

VS Code + LaTex + SumatraPDF联合使用指南

&#x1f9f8; VS Code 与 TexLive SumatraPDF 下载安装 我们就以TexLive为排版系统&#xff0c;用VS Code做编辑器&#xff0c;我本来也是有安装TexStudio的&#xff0c;但因为一直用的VS Code&#xff0c;比较习惯&#xff0c;并且VS Code的界面个人感觉要比TexStudio要好看…...

计算机网络相关知识

1. 计算机网络基础概念计算机网络是指通过通信设备和线路将地理位置不同的、具有独立功能的计算机系统连接起来&#xff0c;在网络软件的支持下实现资源共享和信息传递的系统。按照覆盖范围可分为&#xff1a;局域网&#xff08;LAN&#xff09;&#xff1a;覆盖范围较小&#…...

从理论到实践:打造坚不可摧的 Redis 缓存体系

在现代高并发应用架构中&#xff0c;Redis 几乎是缓存层的标配。然而&#xff0c;简单地将数据放入 Redis 并不能解决所有问题。如何设计一个既能扛住流量洪峰&#xff0c;又能保证数据一致性&#xff0c;并且易于维护的缓存系统&#xff0c;才是真正的挑战。本文将基于大厂实战…...

GTC 2026落幕:AI 不再是模型,而是AgenticOps

AI 不再是模型 而是AgenticOps GTC 2026 刚刚落幕&#xff0c;Jensen Huang 站在台上说出了一个词&#xff1a;AI Factory。 不是模型&#xff0c;不是算法&#xff0c;是工厂。 这个词的出现&#xff0c;其实在验证一件事&#xff1a;OpenCSG 从一开始就走对了方向。 这个…...

Java EE3(第十章:Spring中的事务管理)

...

Comsol激光打孔(不通)水平集两相流仿真模型的奇妙之旅

comsol激光打孔&#xff08;不通&#xff09;水平集两相流仿真模型&#xff0c;涉及温度场流场水平集&#xff0c;一共发两个版本最近在研究激光打孔相关的项目&#xff0c;用到了Comsol来构建水平集两相流仿真模型&#xff0c;这里面涉及到温度场和流场&#xff0c;过程还挺有…...