news 2026/10/21 3:01:26

Spring Boot面试92题怎么准备:掌握自动配置和请求链路核心

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot面试92题怎么准备:掌握自动配置和请求链路核心

简介:Spring Boot面试题(92题)是一份面向Java开发者与后端求职者的高频考题合集,围绕Spring Boot如何简化传统Spring工程搭建这一主线,梳理了自动配置、Starters启动器、可执行Jar包、内嵌Servlet容器、应用监控等核心知识点。压缩包内共1个PDF文档,大小4.06MB,题量充实且按知识点排列,便于随时翻阅和考前集中复习。每道题均给出较完整的分析思路与参考答案,例如@SpringBootApplication注解的三个组成部分、spring-boot-starter-data-jpa的依赖集成方式、不同运行环境的profile切换等,既能帮助理解框架底层原理,也能直接用于面试表达。已有7677人学习下载,适合准备Spring Boot相关岗位面试的初中级开发者在短时间内查漏补缺、巩固基础。

1. Spring Boot面试题(92题):别让“背题”变成面试翻车的起点

Spring Boot 面试题(92题)这份列表在网上流传很广,很多人拿到后第一反应是“背下来”,结果面试时被追问一个“为什么”就卡壳。我见过不少候选人,自动配置原理倒背如流,但一被问“你的项目里有没有改过自动配置的生效条件”就沉默,问题恰恰出在把面试题当背诵材料,而不是当复习提纲。

这份 92 题覆盖的知识面其实很集中:自动配置、启动流程、配置文件加载顺序、请求处理链路、数据访问、内嵌容器、监控和部署。真正值得投入的不是记住答案,而是顺着每道题背后的考点,把 Spring Boot 的“约定优于配置”这条主线理解透。面向的读者是准备中级以上岗位面试的从业者,至少写过一到两个 Spring Boot 项目,能跟着下面的路径在两周内把知识体系补齐。

2. 92题背后的复习地图:怎么把这堆题组织成可执行的学习路径

拿到 92 题先别急着看答案,第一件事是分类。我一般会按考察维度把题目分成五类:原理题(自动配置、启动流程、条件注解)、配置题(配置文件加载顺序、多环境配置、自定义 Starter)、请求链路题(拦截器、过滤器、监听器、异常处理)、数据访问题(Spring Data、事务、连接池)、工程题(部署、监控、日志、测试)。分类的价值在于,每一类对应一种复习方法,原理题需要读源码,配置题需要动手改,链路题需要画请求时序。

2.1 考点分布与优先级:先把 92 题里的核心高频点挑出来

92 题看着多,但高频考点是有规律的。我根据近几年的面试反馈,把出现频率最高的考点归纳成四个梯队,复习优先级按这个顺序走:

第一梯队是自动配置原理和启动流程,几乎必问。第二梯队是配置文件加载顺序、条件注解、bean 的生命周期,面试官会用“你改过哪些 starter 的默认行为”这类问题变着法考。第三梯队是拦截器和过滤器的区别、异常处理方案、Spring Data JPA 或 MyBatis 的事务边界。第四梯队是内嵌容器调优、Actuator 监控、多环境打包,这类通常作为加分项。

具体操作上,我用一个表格把每道题打上标签,标注“原理 / 配置 / 链路 / 数据 / 工程”和“高频 / 中频 / 低频”,复习时先集中攻高频原理题,再扫中频配置题。表格列在下面,你可以直接抄作业:

分类典型考点优先级复习方式
原理自动配置原理、启动流程、SpringApplication.run 做的事高读源码 + 画时序图
配置配置文件加载顺序、@ConfigurationProperties高动手改配置观察效果
链路拦截器 / 过滤器 / 监听器执行顺序、全局异常高写 demo 验证执行顺序
数据事务传播机制、JPA 与 MyBatis 选型、连接池参数中结合项目复盘
工程多环境打包、Actuator 端点、日志框架配置中低对着现有项目过一遍

优先级标记的意义不是让你放弃低频题,而是控制投入节奏。面试准备时间通常有限,先保住必考题的深度,再扩展广度,比平均用力更容易出效果。

2.2 把“背答案”改成“写最小验证工程”:一道题对应一个可运行片段

常见做法是拿现成项目改代码验证答案,但现有项目太重,启动一次要十几秒,验证一个拦截器顺序要翻好几层代码。我一般会给 92 题里的每一类考点建一个单独的验证工程,只保留启动类和必要的依赖,跑通一个场景就够。

比如验证配置文件加载顺序,最小工程只需要一个启动类和一个 application.yml:

@SpringBootApplication public class ConfigOrderApplication { public static void main(String[] args) { // 启动时打印当前生效的 profile,配合不同启动参数观察加载顺序 ConfigurableApplicationContext context = SpringApplication.run(ConfigOrderApplication.class, args); Environment env = context.getEnvironment(); System.out.println("当前 active profile: " + String.join(",", env.getActiveProfiles())); System.out.println("server.port 值: " + env.getProperty("server.port")); } }

这段代码的核心是 Environment 对象,它保存了启动时所有配置源合并后的属性集合。启动参数--spring.profiles.active=prod和application.yml里设置的spring.profiles.active会同时影响打印结果,你可以通过改参数来验证“命令行参数 > Java 系统属性 > application-{profile}.yml > application.yml”的优先级顺序。

验证完记得执行一次完整启动,观察控制台输出。你可能会发现server.port同时出现在多个配置源里,Environment 的getProperty返回的是优先级最高的那个值。这个现象比死记优先级顺序直观得多,面试时被问“你怎么证明这个顺序”,直接把实验过程和现象讲出来,说服力远大于背书。

3. 请求链路高频考点:拦截器、过滤器、监听器的执行顺序与三个必调参数

请求链路是 Spring Boot 面试题里最容易被“背混”的部分,因为过滤器属于 Servlet 规范,拦截器属于 Spring MVC,监听器又分好几种,三者执行顺序的细节面试官特别爱追问。92 题里相关题目至少有五六道,但万变不离其宗:请求从客户端到达 controller 的路径上,先过过滤器,再过拦截器,最后到达方法;响应返回时顺序完全相反。

3.1 三大组件原理:谁先执行、谁在哪个容器里

过滤器由 Servlet 容器管理,通过FilterRegistrationBean注册,作用于所有 URL(包括静态资源)。拦截器由 Spring MVC 管理,通过HandlerInterceptor接口定义,只作用于 DispatcherServlet 分发的 handler,不处理静态资源。监听器分ApplicationListener(Spring 事件)和ServletContextListener(Servlet 生命周期),请求处理链路里最常见的还是前者。

三者之间的关系用一句话概括:过滤器在最外层,拦截器在 MVC 层,监听器不在请求路径上,而是响应容器或上下文生命周期事件。面试时如果被问“过滤器能不能替代拦截器”,答案是不能,因为拦截器可以访问 Spring 容器里的 bean,过滤器不一定能,除非手动从容器里取。

验证这个顺序,我写了一个最简 demo,注册一个过滤器、一个拦截器,在控制台打印执行顺序:

@Component public class TraceFilter implements Filter { // 请求进入时最先执行,响应返回时最后执行 @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("过滤器开始"); chain.doFilter(request, response); // 放行,进入下一层(拦截器或 servlet) System.out.println("过滤器结束"); } }
@Component public class TraceInterceptor implements HandlerInterceptor { // preHandle 返回 false 时会跳过 controller 和 afterCompletion @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println("拦截器 preHandle"); return true; // 返回 false 则请求到此终止 } }
@Configuration public class WebConfig implements WebMvcConfigurer { // 给拦截器配置 path pattern,"/**" 表示拦截所有路径 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new TraceInterceptor()).addPathPatterns("/**"); // 可以用 .excludePathPatterns("/api/login") 放行登录接口 } }

这个组合运行后控制台输出顺序是:过滤器开始 → 拦截器 preHandle → controller → 拦截器 postHandle → 拦截器 afterCompletion → 过滤器结束。注意preHandle返回false时,后续全链路直接中止,响应直接返回,这是做登录校验时最常用的短路手段。

三个必调参数里,第一个是过滤器的FilterRegistrationBean.setOrder(),控制多个过滤器之间的执行顺序,数值越小越靠前;第二个是拦截器的addPathPatterns(),控制拦截范围,新人容易漏配导致静态资源被拦截;第三个是excludePathPatterns(),放行登录、健康检查等不需要鉴权的路径。这三个参数在面试中高频出现,配置项名字要说得准确。

3.2 改动一个参数观察行为变化:拦截器放行与拦截的边界

拦截器的preHandle逻辑里最容易踩的坑是返回值处理,很多人把return true写成了return false,结果接口全部 404 又找不到原因。更隐蔽的问题是拦截器生效范围,默认没有任何 path,必须显式调用addPathPatterns,否则配置了也白配。

建议做一个实验:先不配置 pathPatterns,启动后访问任意接口,观察拦截器是否执行;加上addPathPatterns("/api/**")后再访问/static/test.js,观察是否被放行。这个实验能帮你确认拦截器只对 DispatcherServlet 分发的请求生效,静态资源由 DefaultServlet 处理,默认不经过 MVC 拦截器。

# 启动后访问不同路径,观察控制台有没有拦截器输出 curl http://localhost:8080/api/hello # 输出:拦截器 preHandle → 控制器执行 → postHandle → afterCompletion curl http://localhost:8080/static/test.js # 默认无输出:静态资源未走拦截器

这个差异是面试官最喜欢的追问点:“你的拦截器能拦截静态资源吗?”标准回答是“默认不能,但可以通过自定义 ResourceHandler 的映射关系让静态资源也经过拦截器,实际项目中不建议这么做,静态资源应该由容器或网关层处理”。把边界说清楚,比背定义深刻得多。

3.3 面试官最爱追问的场景题:登录校验与鉴权的三重防线

登录校验是拦截器最典型的使用场景,但仅靠拦截器远远不够。我一般建议按照三层来设计:第一层是过滤器做跨域处理和请求日志记录,第二层是拦截器做登录态校验和 token 解析,第三层是方法级注解做权限判断(比如@PreAuthorize)。三层各管一段,互相不越权。

面试时如果被问到“拦截器里怎么拿当前用户”,常见的做法是从请求头取 token,解析后放到ThreadLocal或RequestContextHolder里,供业务层直接调用。这里要留意一个坑:ThreadLocal在多线程环境下会串数据,建议每次请求结束后在afterCompletion里显式调用remove(),防止线程池复用导致用户信息泄漏。

@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // 防止线程池复用导致上一个请求的用户信息残留 UserContext.clear(); }

这道题的完整回答逻辑是:过滤器管跨域和日志,拦截器管 token 解析和登录校验,方法级注解管权限,最后用ThreadLocal封装当前用户上下文。把这条链路说清楚,基本能覆盖 92 题里所有请求链路相关追问。

4. 自动配置与启动流程:从“黑匣子”到能讲清楚五个关键点

Spring Boot 面试题里最核心的题是“自动配置原理”,几乎每一份面试题清单都会把它排在前十。很多候选人能说出@EnableAutoConfiguration和spring.factories,但被追问“条件注解怎么生效”“为什么改@ConditionalOnMissingBean能覆盖默认配置”就接不上。本质问题是没把自动配置当成一个可观察的机制去理解,而是当成一个黑匣子。

4.1 自动配置原理:@EnableAutoConfiguration 与 META-INF 下的加载契约

@SpringBootApplication实际上是三个注解的复合:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。其中@EnableAutoConfiguration负责加载自动配置类,它通过AutoConfigurationImportSelector读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件(新版本)或spring.factories(旧版本),把配置类批量导入容器。

配置类里的条件注解是最后一道闸门。比如@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")表示 classpath 下存在 MySQL 驱动才加载数据源配置;@ConditionalOnMissingBean(DataSource.class)表示容器里没有自定义 DataSource 时才创建默认数据源。这两个注解决定了自动配置的“可覆盖性”,也是你改默认行为时最先要看的地方。

我验证自动配置的方式是启动一个空工程,打印所有自动配置类的名字:

@SpringBootApplication public class AutoConfigApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(AutoConfigApplication.class, args); // 从 BeanFactory 后置处理器里取出自动配置类注册信息,只打印类名 String[] beanNames = context.getBeanDefinitionNames(); Arrays.stream(beanNames).filter(name -> name.contains("AutoConfiguration")) .forEach(System.out::println); } }

这段代码通过getBeanDefinitionNames()拿到所有已注册的 bean 定义,过滤出名字里带AutoConfiguration的。运行后你会看到 HikariCP、Jackson、HttpMessageConverters 等模块的自动配置类都被加载了,但并不是所有条件都满足,比如没有 MySQL 驱动时数据源相关配置就不会生效。

关键理解点在“条件注解的生效时机”。条件判断是在容器的配置解析阶段做的,不是运行时。所以当你自定义一个DataSource后,@ConditionalOnMissingBean(DataSource.class)会自动让默认的DataSourceAutoConfiguration内部配置失效,不需要手动排除。这就是“约定优于配置”在代码层面的体现。

4.2 自定义 Starter:把项目里的通用能力打包成可复用模块

自定义 Starter 不是高频面试题,但一旦问到就是深度区分题,因为它要求你完整理解自动配置的整个链路。常见做法是为日志记录或消息发送等横切能力做一个独立模块,核心是三步:

第一步创建自动配置类,用@Configuration和条件注解限定生效范围;第二步在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件里注册这个类;第三步提供@ConfigurationProperties前缀类,让使用方通过 application.yml 自定义参数。

// 自定义 Minio 文件上传 Starter 的核心配置类 @Configuration @ConditionalOnClass(MinioClient.class) // 有 Minio 依赖才加载 @EnableConfigurationProperties(MinioProperties.class) // 绑定 minio.* 前缀配置项 public class MinioAutoConfiguration { @Autowired private MinioProperties properties; @Bean @ConditionalOnMissingBean // 使用方自定义了 MinioClient 则以自定义的为准 public MinioClient minioClient() { return MinioClient.builder() .endpoint(properties.getEndpoint()) .credentials(properties.getAccessKey(), properties.getSecretKey()) .build(); } }
# 使用方在 application.yml 里这样配置 minio.endpoint=http://192.168.1.100:9000 minio.access-key=admin minio.secret-key=password

注意看这段代码里的两个条件注解:@ConditionalOnClass保证没有引入 Minio 依赖的工程不会被强制要求配置,@ConditionalOnMissingBean保证使用方可以完全接管。这两个注解是自动配置可覆盖性的“制度保障”,面试时被问“自动配置怎么做到不影响用户自定义 bean”,把这个例子拿出来讲最有力。

4.3 启动流程问答应答:SpringApplication.run 内部到底做了几件事

92 题里经常出现“Spring Boot 启动流程是怎样的”,答案是分阶段叙述:先创建SpringApplication实例,然后调用run方法启动应用上下文。run方法内部核心是六个步骤:推断应用类型(Web 还是非 Web)、加载 SpringApplicationRunListeners、准备 Environment、创建并刷新 ApplicationContext、启动 Web 容器、发布 ApplicationReadyEvent。

每步展开讲,有一个细节特别能体现功底:推断应用类型是通过 classpath 里是否存在ServletWebServerApplicationContext相关类来决定的,不是通过配置文件。所以一个依赖了 spring-boot-starter-web 的工程,即使你没写任何 Web 配置,它也会默认启动内嵌 Tomcat。

我建议在准备这道题时画一张简化时序图(文字描述即可):main → SpringApplication.run → 创建上下文 → 注册配置类 → 刷新上下文 → 触发自动配置 → 启动 Web 服务器 → 发布事件。能按这个顺序讲清楚,比死记代码行数有用得多,面试官追问到哪一步你都能现场推演。

5. Spring Boot 面试题的四大避坑点:从失败案例到排查路径

背题最致命的问题是“记住了结论,但没验证过前提”。我在模拟面试里收集过不少翻车案例,下面四条是出现频率最高的,按“现象 → 原因 → 解决”写清楚,复刻的时候直接对照排查。

5.1 自动配置失效:改了 spring.factories 却不生效

现象:自定义 Starter 引入到主工程后,预期的自动配置类没有加载,bean 直接找不到。

原因:新版 Spring Boot 2.7 以上不再从spring.factories读自动配置类,改成META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,且每行一个类名,必须加org.springframework.boot.autoconfigure.EnableAutoConfiguration键。旧资料里教的spring.factories写法在新版本完全不生效。

解决:把配置文件迁移到新路径,写一个自动配置冒烟测试,在测试里显式加载ApplicationContext并确认MinioClientbean 存在。

# 查看当前引入的 spring-boot-autoconfigure 版本 mvn dependency:tree | grep spring-boot-autoconfigure # 如果版本 >= 2.7,优先检查 AutoConfiguration.imports 文件 ls target/classes/META-INF/spring/ | grep import

5.2 拦截器执行两次:静态资源映射导致的链路污染

现象:同一个接口请求,控制台打印了两次拦截器 preHandle。

原因:工程里配置了addResourceHandlers自定义静态资源路径,把某个路径映射到了类路径资源,同时又把这个路径加到了拦截器的addPathPatterns拦截目录里。此时请求可能同时走 MVC 的 handler 和静态资源处理器,各自执行一次拦截器链。

解决:统一拦截范围,避免把资源映射路径混入接口路径。排查时先打开logging.level.org.springframework.web=DEBUG,看请求被 DispatcherServlet 路由到了哪个 handler,确认是不是同一 URL 被两个映射规则重复命中。

5.3 配置文件覆盖混乱:jar 包外配置不生效

现象:部署时把 application.yml 放在 jar 包同级目录下,但启动后读到的还是 jar 包内的配置。

原因:Spring Boot 有固定的配置加载顺序,jar 包外配置文件的优先级高于 jar 包内,但有一个前提——必须放在正确的相对路径下。默认查找路径是file:./config/、file:./、classpath:/config/、classpath:/,如果你把配置文件放在 target 目录或绝对路径的任意位置,不会自动读取。

解决:使用外部配置显式指定位置,启动命令加--spring.config.additional-location=file:/opt/app/conf/,或者把文件放到config子目录下。

# 推荐的外部配置方式,优先级最高 java -jar app.jar --spring.config.additional-location=file:/opt/app/conf/application.yml

这个坑在面试里对应的问题经常是“多环境配置怎么管理”,回答时主动提加载顺序和外部化配置,能展示出你真实部署过,而不只是看过文档。

5.4 JPA 懒加载序列化异常:Web 层踩到事务边界

现象:controller 直接返回实体对象,前端收到 JSON 里某些字段为 null,或者直接报LazyInitializationException。

原因:实体类使用了@OneToMany(fetch = FetchType.LAZY),而查询方法的事务在 service 层就已经结束,controller 序列化实体时触发懒加载,此时 session 已关闭。

解决:三种可选方案,按推荐排序——使用 DTO 显式转换而不是直接返回实体;查询方法内用join fetch或@EntityGraph提前加载;或者开放spring.jpa.open-in-view=false(新版本默认报警告)并改为在 service 层完成数据组装。我推荐第一种,长期维护最省心。

// 用 EntityGraph 在查询阶段提前加载关联集合,避免序列化时触发懒加载 @Repository public interface OrderRepository extends JpaRepository<Order, Long> { @EntityGraph(attributePaths = {"items"}) @Query("select o from Order o where o.id = :id") Optional<Order> findByIdWithItems(@Param("id") Long id); }

这题在面试题清单里经常以“JPA 的 N+1 问题怎么解决”出现,把open-in-view的副作用和EntityGraph的用法讲清楚,基本能压住后续追问。

6. 92题之外:把答案组织成“面试官愿意追问”的表达习惯

准备到后期,你要做的不是再多背十道题,而是把已有答案改写成口语化表达,让面试官觉得你不是背的,而是做过。我的习惯是“三步答题法”:先说结论,再讲现象或源码依据,最后落到你项目里的具体操作。比如被问自动配置原理,第一句“自动配置的本质是条件装配”,第二句“核心是@Conditional系列注解按 classpath 和容器现状判断是否创建 bean”,第三句“我在某个模拟项目里自定义过数据源,发现只要声明了自己的 DataSource 配置,默认自动配置就会让路”。

这个习惯对 92 题里所有原理题都适用,尤其对“启动流程做了哪些事”这种容易变成流水账的问题,结论先行会显得逻辑强很多。同时准备一个“踩坑作品集”,挑两个真实的排障经历(比如外部配置不生效、懒加载报错),每个用“现象→排查→修复→验证”四段式讲,时长控制在三分钟以内。

最后给你一个具体技巧:把每个高频考点写成一张索引卡片,正面是问题,背面是三句关键词和一个项目例子。考前一周每天随机抽十张,对着卡片做 30 秒口头回答,卡住的题做标记,周末集中补。这个方法的血泪经验是——别追求全部 92 题一遍过,而是确保前 30 高频题能形成肌肉记忆,剩下的题靠“三步答题法”现场组织。

我一般面试前也只过一遍自己整理的高频卡,很少翻完整份列表,因为知道 92 题只是地图,真正的功夫在日常项目里。希望这套从列表到表达的训练思路帮到你,按这个节奏走下来,面试时你会发现自己比想象中稳。

本文还有配套的精品资源,点击获取

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

TStarBots1:轻量级星际争霸AI研究框架解析

1. 项目概述&#xff1a;这不是游戏外挂&#xff0c;而是一套可复现的AI竞技研究框架“腾讯星际争霸 TStarBots1”这个标题一出现&#xff0c;很多人第一反应是“哦&#xff0c;腾讯又出游戏AI了&#xff1f;”——但如果你真这么想&#xff0c;就错过了它最硬核的价值。它不是…

作者头像 李华
网站建设 2026/10/18 15:06:53

C语言复试题库精析:指针与运算符优先级考点

简介&#xff1a;这套电子科技大学研究生复试C语言题库&#xff08;最全无重复修正版&#xff09;专为报考成电的复试考生整理&#xff0c;聚焦C语言笔试与机试中的高频题型与易错考点。资源包含1个PDF文件&#xff0c;大小5.88MB&#xff0c;所有题目、解析和运行推演集中在一…

作者头像 李华
网站建设 2026/10/19 15:54:18

维海德全场景云视频会议解决方案怎么样?

判断一套会议方案好不好&#xff0c;标准从来不是"功能多不多"&#xff0c;而是能不能解决你企业的真实问题。维海德全场景云视频会议方案&#xff0c;简单说就是一套覆盖从微型到大型会议室、打通会前会中会后全链路、兼容多平台多模式的整体会议解决方案。但它到底…

作者头像 李华
网站建设 2026/10/19 14:56:34

虚拟化集群集体失联:一次存储多路径故障的惊魂复盘

某个周五深夜&#xff0c;我手机上的运维告警群开始连续弹出消息。一开始是两条&#xff0c;然后是十几条&#xff0c;最后几乎每过几秒就有新告警刷进来。后台有9台服务器同时失去响应&#xff0c;它们都是我们生产环境的虚拟机&#xff0c;分布在不同的宿主机上&#xff0c;承…

作者头像 李华
网站建设 2026/10/20 10:58:27

狐群算法优化VMD参数:实现数字信号自动去噪的完整方案

1. 从盲目调参到智能寻优&#xff1a;为什么非用狐群算法不可做数字信号去噪的老手应该都有这种体会&#xff1a;碰上变分模态分解&#xff08;VMD&#xff09;&#xff0c;算法本身不难实现&#xff0c;真正让人抓狂的是它的参数敏感性。模态个数K设大了&#xff0c;模态被过度…

作者头像 李华
网站建设 2026/10/20 6:12:01

三维重建从“算一次”到“一直算”:持续计算技术实践与架构解析

三维重建正在从「算一次」变成「一直算」这个标题我想了很久应该怎么写。主要原因是&#xff0c;最近我扎进了一个城市级三维重建项目里&#xff0c;发现连续加班的两周时间&#xff0c;几乎全部花在琢磨同一件事&#xff1a;为什么以前那套“先采数据、再离线重建、最后交付模…

作者头像 李华