做后端开发这几年,Spring Boot 用得多,但真正敢说自己弄懂自动装配机制和 Starter 实现原理的人其实不多。Spring Boot 最吸引人的一点,就是引入一个服务端 SDK 往往只需要在构建文件里加一行依赖,配置类、模板类、连接池管理全都自动出现。这个体验背后就是自动装配机制,而 Starter 则是把自动装配、依赖管理、配置提示打包进一个可以被仓库拉取的模块。这篇文章会从设计动机、执行流程、手写 Starter 到问题排查,把这套东西完整捋一遍。适合对 Spring 有一定基础、想彻底搞明白 starter 为什么“不用配就能跑”的开发者。
1. 自动装配机制到底在解决什么问题
1.1 没有自动装配时的日常
早些年用 Spring 搭一个 Web 项目,要做的事情远比现在琐碎。拿到一个新项目,先要复制一坨 XML 或者 JavaConfig,把数据源、事务管理器、模板引擎、组件扫描等挨个声明一遍。每个项目虽然基础依赖相同,这段配置却无法直接复用,因为包名、数据源参数、Bean 名称都可能不一样。很多人就在复制粘贴中把某个 Bean 漏掉,导致启动时不报错,运行到某个业务方法才冒出一个 NullPointerException。
这种年代里,Spring 的“装配”是显式的,所有依赖关系都要开发者自己声明。你可以把它想象成装修房子:每个户主都要自己找水电工、自己画线路图,而且每套房子都要从头画一遍,画错了还要整面墙重新开槽。Spring Boot 想解决的问题,就是把这套重复的水电走线方案标准化,让框架根据房屋结构自动生成最合适的线路,而不是让每个开发者都从零开始手工设计。
自动装配的本质,是 Spring Boot 在 Spring Framework 的 BeanDefinition 加载能力之上,做了一层“自动发现”和“按条件导入”的封装。Bean 本身还是由容器创建,但“要不要创建”“创建成什么样”,由框架根据当前项目的 classpath、配置项、已有 Bean 情况去判断。这样项目里的样板配置就大幅减少,剩下的配置只保留业务上真正需要差异化的部分。
1.2 三个关键角色搞定“自动”
自动装配不是单独一个注解能完成的,它是几个角色协同的结果。拆开看,核心有三个:
- @Configuration 类:自动配置类本质还是一个配置类,负责集中声明 Bean。你平时写的 @Configuration 能做什么,自动配置类就能做什么。
- 条件注解:以 @Conditional 开头的一组注解,比如 @ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnProperty。它们相当于装配现场的“检查员”,判断环境是否满足条件,不满足就不装配。
- 配置属性绑定:通过 @ConfigurationProperties 把配置文件里的项绑定成强类型对象,自动配置类读取这些属性后再决定 Bean 的细节。
这三个角色缺一不可。配置类提供 Bean 定义,条件注解控制装配时机,属性绑定把外部配置注入进来。入口处的 @EnableAutoConfiguration 则负责把这些候选配置类收集起来,统一交给 Spring 容器处理。
这里要纠正一个直觉误区:自动装配不是某种“幽灵逻辑”,它依然是 Spring 的普通配置类。Spring Boot 只是把大量配置类提前写好了,放在一个叫 spring-boot-autoconfigure 的包里,你引入相关依赖后,框架把满足条件的配置类再导入容器。理解到这一层,后面看源码才不会觉得自动装配是黑魔法。
1.3 自动装配不是“全自动”
虽然叫自动装配,但它远不是“无脑全部加载”。每个自动配置类往往带着一堆条件,条件不满足就静默跳过。这一点在使用上非常关键:你不是“引入依赖就一定有某个 Bean”,而是“引入依赖且条件满足时,才会有某个 Bean”。
这种设计带来两个好处。第一,不同场景可以共享同一个基础依赖,框架根据当前是 Web 应用还是非 Web 应用、是不是响应式应用、classpath 里有没有对应类来裁剪装配范围。第二,用户可以随时通过自定义 Bean 覆盖默认实现,也可以显式排除某些自动配置类。例如 Spring Boot 的 @SpringBootApplication 注解里允许通过 exclude 参数移除指定的自动配置类,配置文件中也能用 spring.autoconfigure.exclude 指定要排除的类。
所以“自动”应该理解成“按需自动”,而不是“全量自动”。条件匹配是自动装配的题眼,后面排查问题的时候,大部分时间也都是在和“条件为什么不匹配”较劲。
2. 自动装配的内部流程与核心原理
2.1 入口在 @SpringBootApplication 的组合注解里
Spring Boot 工程的主类上都会标注 @SpringBootApplication,很多人知道它是组合注解,包含 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。真正驱动自动装配的是 @EnableAutoConfiguration。
这个注解本身做的事情很简洁,它只是通过 @Import 导入了一个类:AutoConfigurationImportSelector。Selector 是 Spring 里的导入器机制,作用是根据条件返回一组需要导入的类名。自动装配的完整流程就是从这个 Selector 开始的。
理解流程要抓住一个主线:当 Spring 容器处理主配置类时,发现 @EnableAutoConfiguration 上的 @Import,于是调用 AutoConfigurationImportSelector.selectImports 方法,拿到自动配置类的候选名单,再逐个解析。这个 Selector 不是普通 ImportSelector,它还实现了 DeferredImportSelector,意味着它的导入动作会被推迟到项目里开发者自定义的配置类都处理完之后再执行。这个设计非常重要,它保证了用户自定义配置优先于自动配置,从而能覆盖自动配置里声明的 Bean。
2.2 候选配置类从哪里来
回到前面的关键问题:AutoConfigurationImportSelector 手里的自动配置类候选名单是从哪来的?答案是 classpath 下的固定配置文件。
在 Spring Boot 2.6 及更早版本中,自动配置类列表写在 META-INF/spring.factories 文件里,键是 org.springframework.boot.autoconfigure.EnableAutoConfiguration,值是自动配置类的完整类名,多个类名用逗号分隔。看起来像这样:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.demo.autoconfigure.StudyDemoAutoConfiguration,\ com.example.demo.autoconfigure.StudySecurityAutoConfiguration从 Spring Boot 2.7 开始,框架引入了更聚焦的新文件:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。这个文件不需要再写键名,每行放一个自动配置类的全限定名,更清爽,也更容易被并行读取。到了 Spring Boot 3.0,spring.factories 里传统的 EnableAutoConfiguration 配置已经不再支持,自定义自动配置类必须放在新的 imports 文件中。
为什么官方要换这个文件格式?原因之一是旧的 spring.factories 除了自动配置,还要承载其他工厂机制,比如 EnvironmentPostProcessor、ApplicationListener 等。框架读取时要把整个文件里所有key-value都解析出来,再挑出自己关心的那一项,既浪费又容易误伤。新的 imports 文件单独服务于自动配置,职责更单一,加载路径更直接。
如果你在自己的 Starter 里同时写了两个文件,也不是说一定会出大问题,框架内部会对重复项做去重,但维护两份配置纯属自找麻烦。建议是:只支持 Spring Boot 3.x 就直接用 imports 文件;还需要兼容 2.5 / 2.6 的话,沿用 spring.factories;兼容 2.7+ 和 3.x 时,以 imports 文件为准,不要两边同时维护。
2.3 条件注解给装配现场装上了“检查员”
候选自动配置类拿到了,Spring 不会一股脑全部导入。每个自动配置类上都有条件注解,这些注解会被 Spring 的 ConditionEvaluator 逐个评估。如果条件不满足,这个配置类就会被直接跳过。
常见的条件注解有下面这些:
| 条件注解 | 判断逻辑 |
|---|---|
| @ConditionalOnClass | classpath 中存在指定类才生效 |
| @ConditionalOnMissingClass | classpath 中不存在指定类才生效 |
| @ConditionalOnBean | 容器中已有指定 Bean 才生效 |
| @ConditionalOnMissingBean | 容器中不存在指定 Bean 才生效 |
| @ConditionalOnProperty | 配置项满足指定条件才生效 |
| @ConditionalOnWebApplication | 当前应用为 Web 应用时生效 |
比如某个自动配置类要连接 Redis,它就会在类上加 @ConditionalOnClass({RedisTemplate.class}),保证只有当你引入了 Redis 客户端依赖时,这个配置类才参与装配。如果 classpath 里根本没有 Redis 类,条件直接失败,自动配置类就不加载,也就不会报“找不到类”的错误。这也是 Spring Boot 能同时兼容大量可选组件而不互相冲突的关键。
这里要提醒一个容易踩坑的细节:@ConditionalOnMissingBean 和 @ConditionalOnBean 属于容器状态相关的条件,它们的判断时机非常敏感。如果判断得太早,用户后面定义的 Bean 还没被容器发现,就会误判为“没有 Bean”,从而错误地装配了默认实现。Spring Boot 为了保证自动配置与用户配置的顺序,借助 DeferredImportSelector 让自动配置类最后处理,但在自定义自动配置类内部,多个配置类之间仍然要用 @AutoConfigureOrder、@AutoConfigurationBefore、@AutoConfigurationAfter 控制顺序。写条件注解时,最好把“容器里是否已有用户 Bean”这一类判断放在 @Bean 方法上,同时理解它只能反映当前处理阶段的情况,不要迷信“加了 @ConditionalOnMissingBean 就绝对不覆盖”。
2.4 自动配置排序与覆盖规则
自动配置类之间的顺序,主要靠三个注解控制。@AutoConfigureOrder 是全局优先级,参数值越小优先级越高,默认是 0。@AutoConfigurationBefore 和 @AutoConfigurationAfter 则专门用来声明本配置类和另一个配置类的先后关系。当你自定义 Starter 里依赖了别人提供的默认 Bean 时,这两个注解非常有用,能确保你的配置类不会被加载得太早,避免条件判断时别人还没装配。
顺序背后还有一个更核心的规则:自动配置类整体在用户自定义配置类之后处理。这得益于 DeferredImportSelector,也正是用户能在项目里用 @Bean 覆盖自动配置对象的底层保证。举个例子,Spring Boot 默认的 ObjectMapper 自动配置带有 @ConditionalOnMissingBean,当你在项目里自己声明了 ObjectMapper,它发现容器里已有用户 Bean,就不会再创建默认实例。用户配置优先,这是 Spring Boot 设计上刻意维护的行为。
不过要注意,这种覆盖机制不是万能保险。如果自定义自动配置类写得不规范,比如在类级别直接写了 @ConditionalOnBean,而该 Bean 又是另一个自动配置类提供的,顺序没排好就会产生意想不到的结果。所以在设计自动配置类时,先画好依赖关系图,明确“我要在谁之后装配”“谁在我之后装配”,再决定条件注解的位置和排序声明,比事后调试省心得多。
3. 手写一个 Starter:从零到可发布
3.1 先定模块,别把所有东西塞进一个项目
很多人第一次写自定义 Starter,会下意识地创建一个普通 Spring Boot 工程,把自动配置类、服务类、配置属性类全放在一起。这样能用,但并不是一个好结构。官方 Starter 通常被拆成两个模块,一个是 xxx-spring-boot-starter,一个是 xxx-spring-boot-autoconfigure。
Starter 模块负责对外暴露依赖,它的源码很少,甚至可能只有一个 pom.xml,核心工作是把 autoconfigure 模块和其他必需依赖聚合起来。Autoconfigure 模块才是真正写自动配置逻辑的地方。拆开的理由是:如果 Starter 里直接包含业务实现,每次业务代码改动都要重新发一个 Starter 版本,对引用方很不友好;而拆开后,autoconfigure 模块可以单独迭代,业务方可以根据需要选择只依赖 starter,或者直接依赖 autoconfigure 自行控制装配范围。
我这里用一个模拟项目 X 做示例,项目名就叫 study-demo-spring-boot-starter,结构如下:
study-demo-spring-boot-starter ├── study-demo-autoconfigure │ ├── src/main/java/com/example/demo/autoconfigure │ │ ├── StudyDemoProperties.java │ │ ├── StudyDemoService.java │ │ └── StudyDemoAutoConfiguration.java │ └── src/main/resources/META-INF/spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports └── study-demo-spring-boot-starter └── pom.xml两个模块之间的依赖关系很简单:starter 模块的 pom 里依赖 autoconfigure 模块,而 autoconfigure 模块只依赖真正需要的 Spring Boot 基础库。不要在 starter 模块里引入一堆业务相关依赖,否则引用方会被迫做依赖扫描,很容易产生版本冲突。
3.2 配置属性类:将配置项变成强类型
第一步先写配置属性类。Spring Boot 允许把一个带前缀的配置项组直接映射成 Java 对象,避免在自动配置类里到处读取散落的配置值。拿模拟项目 X 来说,我希望使用者能在配置文件里写 study.demo.enabled 和 study.demo.urls:
package com.example.demo.autoconfigure; import java.util.ArrayList; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "study.demo") public class StudyDemoProperties { private boolean enabled = true; private List<String> urls = new ArrayList<>(); public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public List<String> getUrls() { return urls; } public void setUrls(List<String> urls) { this.urls = urls; } }注意 @ConfigurationProperties 只是把属性类标记为“可绑定”,它不会自动成为容器里的 Bean,需要配合 @EnableConfigurationProperties 或者 @ConfigurationPropertiesScan 注册。前缀命名建议用公司或团队标识加模块名,避免和别的 Starter 冲突。如果你希望使用者在 IDE 里写配置时有自动提示,可以在 autoconfigure 模块引入 spring-boot-configuration-processor,它会生成配置元数据文件,这样 application.properties 里就能补全 study.demo.* 相关键名。
3.3 自动配置类:真正的装配现场
自动配置类是 Starter 的核心。它要做的事情通常是三件:加载配置属性、创建业务 Bean、判断要不要创建默认 Bean。模拟项目 X 的自动配置类可以这样写:
package com.example.demo.autoconfigure; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @AutoConfiguration @ConditionalOnProperty(prefix = "study.demo", name = "enabled", havingValue = "true", matchIfMissing = true) @EnableConfigurationProperties(StudyDemoProperties.class) public class StudyDemoAutoConfiguration { @Bean @ConditionalOnMissingBean public StudyDemoService studyDemoService(StudyDemoProperties properties) { return new StudyDemoService(properties); } }这个类里有几个关键点值得展开说。
@AutoConfiguration 是 Spring Boot 2.7 之后引入的注解,它本质上是一个带 proxyBeanMethods = false 的 @Configuration,同时会被自动装配机制识别。如果你还在写 @Configuration 并配合 spring.factories,那是一种老写法;新项目建议直接用 @AutoConfiguration。
@ConditionalOnProperty 定义了总开关。我在这里设置了 matchIfMissing = true,意思是即使配置文件里没有 study.demo.enabled,也默认当作 true 处理,保证开箱即用。如果你希望用户必须显式开启,就把 matchIfMissing 设为 false。
@ConditionalOnMissingBean 放在 @Bean 方法上,是一个覆盖保护:如果使用方在业务代码里已经声明了 StudyDemoService,自动配置类就不会再创建默认对象。这是所有 Starter 都值得遵守的规范,别让自己的默认实现强制顶掉用户的定制实现。
StudyDemoService 本身就是一个普通 Java 类,可以提供一些业务方法。它不一定需要实现什么特定接口,Starter 不强求抽象。只要把配置属性传进去,让它完成初始化即可。
3.4 注册自动配置类
配置类和属性类都写完了,Spring 怎么知道要加载它呢?关键在于 resources 下的固定文件。在 Spring Boot 3.x 环境下,需要在 autoconfigure 模块的 src/main/resources/META-INF/spring/ 目录下创建文件 org.springframework.boot.autoconfigure.AutoConfiguration.imports,然后把自动配置类的完整类名写进去:
com.example.demo.autoconfigure.StudyDemoAutoConfiguration文件路径和文件名都不能写错。我见到过有同学把目录建成了 META-INF/Spring,或者文件名末尾多了空格,结果启动时自动配置类一直没加载,还以为是条件注解的问题。这种问题非常隐蔽,排查时要先解压 jar 确认文件确实在包里,并且路径和名字与规范完全一致。
如果你要兼容老版本 Spring Boot 2.5 / 2.6,也可以同时在 META-INF/spring.factories 里写:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.demo.autoconfigure.StudyDemoAutoConfiguration但只建议二选一,不要长期同时维护两份。新项目直接面向 Spring Boot 3.x 的话,用 imports 文件就够了。
3.5 本地发布与踩坑记录
自动配置类注册好后,需要把 starter 模块和 autoconfigure 模块安装到本地仓库或公司内部仓库,才能在别的项目里引用。在 autoconfigure 模块的 pom 里执行 mvn install,再到 starter 模块执行 mvn install,然后把 starter 依赖加到目标项目里即可。
这里有一个我踩过的坑,必须单独拿出来说:如果 autoconfigure 模块继承了 spring-boot-starter-parent,打包时默认会执行 spring-boot-maven-plugin 的 repackage,生成一个可执行 fat jar。这个 fat jar 里包含了依赖和嵌套 jar,但它不适合作为普通库被其他项目依赖。其他项目引了它之后,类路径上的自动配置 imports 文件可能读取不到,导致自动配置完全没生效,而你从 IDE 里看依赖树又确实有依赖,非常让人抓狂。
解决思路有两个。一是给 spring-boot-maven-plugin 配置 classifier,让它生成的 fat jar 带 exec 后缀,保留原始 jar 作为可依赖的普通 jar。二是更推荐的做法:库类模块不要继承 spring-boot-starter-parent,而是用 dependencyManagement 引入 spring-boot-dependencies 来管理版本,这样不会默认触发 repackage。Starter 和 autoconfigure 模块本质是库,不是最终应用,不应该打成可执行 jar。
发布之后,目标项目可以这样验证:配置文件里设置 study.demo.enabled=true,然后在业务类里注入 StudyDemoService,启动时自动配置日志里就会出现该自动配置类命中的记录。
4. 常见问题与排查技巧实录
4.1 自动配置报告就是最好的排查入口
遇到自动配置没有生效,第一反应别去猜,直接看 Spring Boot 启动时的自动配置报告。只要在配置文件里设置 debug=true,或者启动命令带上 --debug,Spring Boot 启动完成后就会打印 CONDITIONS EVALUATION REPORT。
这份报告会列出所有候选自动配置类,分成 Positive matches 和 Negative matches。Positive 表示条件满足、配置类被加载,Negative 表示条件不满足、被跳过。Negative matches 下面往往直接标注了是哪几个条件导致的失败。比如你看到一个自动配置类没生效,报告里写 “not matched on onClassCondition: required class X not found”,那问题就非常明确了:classpath 里缺类,或者类名因为版本升级被改掉了。
我建议把这份报告当成第一手调试工具。它比翻源码更直观,尤其适合排查“我明明引入了 Starter,但自动配置的 Bean 没出现”这类问题。报告里还能看到每个条件具体是怎么匹配的,比在代码里加日志更快。
4.2 自动配置没有生效的逐项检查
如果报告里连某个自动配置类都没出现,或者出现但条件不满足,就要按下面这个顺序逐项查:
- 确认依赖真的引入了。使用构建工具的依赖树命令查看 starter 和 autoconfigure 是否在 compile 范围内,别只依赖 IDE 的自动补全。
- 确认自动配置注册文件在最终构建产物里。解压 jar,看 META-INF/spring/ 目录下是否有对应 imports 文件。被打包工具过滤掉是常事。
- 确认没有显式排除。检查主类上的 exclude、配置文件里的 spring.autoconfigure.exclude、以及是否通过开关关闭了自动配置。
- 确认条件注解匹配。看 Negative matches 里给出的原因,逐个确认 classpath 是否有对应类、属性值是否匹配、Bean 是否存在。
- 确认包名和依赖版本一致。Spring Boot 3.x 下,很多类已经从 javax.* 迁移到 jakarta.*,如果 Starter 里还按老的类名写 @ConditionalOnClass,条件会静默失败。
这套检查清单能解决绝大多数“自动配置不生效”的问题,关键是每一步都要用实际产物去验证,不要凭 IDE 的视图判断。
4.3 自定义配置 Bean 被覆盖/不生效
有时候你明明在业务代码里定义了一个 Bean,结果自动配置类又创建了一个默认 Bean,导致注入的是默认实现,而不是你的自定义实现。
原因通常是自动配置类漏写 @ConditionalOnMissingBean。如果自动配置的 @Bean 方法上没有这个条件,它就会无条件创建 Bean,虽然自动配置整体在用户配置之后处理,但没有条件约束时,框架不会主动判断是否已有用户 Bean。
反过来还有一种情况:你的自定义 Bean 没有生效,而自动配置的 Bean 被覆盖了。这可能是因为你把自定义配置类也写成了自动配置类,并且和官方自动配置类之间排序不当。记住一点:业务代码里的普通 @Configuration 或 @Component 的 Bean 是用户配置,优先级天然高于自动配置;但是为了稳妥,最好在定义默认 Bean 的自动配置方法上始终保留 @ConditionalOnMissingBean。
还有个小技巧:想确认容器里最终注入的是哪个 Bean,可以在临时 ApplicationRunner 里打印一下 Bean 的类名或 hashCode,这样一眼就能看出是自定义实例还是默认实例。
4.4 版本升级导致的兼容问题
Spring Boot 3.x 之后,自动装配的加载方式发生了变化,这给老 Starter 带来了不少兼容问题。最典型的是这两类:
第一,spring.factories 失效。Spring Boot 3 不再从 spring.factories 读取 EnableAutoConfiguration 键下的自动配置列表,只认 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。很多老项目升级后自动配置全部失效,就是这个原因。
第二,javax 到 jakarta 的坐标迁移。Spring Boot 3 把底层基础组件从 javax 迁移到 jakarta,比如 Servlet、Validation 等。如果你的自动配置类上有 @ConditionalOnClass(javax.servlet.Filter.class),在 Spring Boot 3 项目里这个类根本不存在,条件永远不会匹配。
升级体验是从版本 2.7 过渡会平滑一些,因为 2.7 同时支持新的 imports 文件和老的 spring.factories。如果项目直接跨到 3.x,必须把自定义 Starter 的注册文件、条件注解里的类名、依赖坐标全部过一遍。这个工作没什么捷径,只能靠自动配置报告一点点排。
5. 我对这套机制的实操体会
5.1 什么时候适合自己写 Starter
自动装配机制很强大,但不要到处滥用。我自己判断是否抽 Starter 的标准是:这套能力是否会被多个项目复用,并且带有相对固定的初始化逻辑。比如自己团队内部的日志链路封装、消息队列配置、统一校验组件、公共安全过滤链,这类非业务核心的技术基础设施就很适合做成 Starter。业务逻辑本身变来变去,不应当塞进自动配置里;一个自动配置类一旦开始频繁按业务条件分支,说明它可能退化成业务代码了,维护成本会很高。
团队内使用自定义 Starter 时,版本管理也很重要。建议至少提供两个模块分开发布,并制定一个简单的命名分组规范。自动装配类的序列化顺序、条件注解的放置位置也尽量保持一致,避免每个人写一套风格,最后没人敢动别人的模块。
5.2 调试这套机制的最佳路径
如果你想把自动装配再往深挖,我建议沿着一条清晰的路径看源码,不要一上来就整个类通读。先看 @SpringBootApplication 进入 @EnableAutoConfiguration,再到 AutoConfigurationImportSelector 的 selectImports 和 getAutoConfigurationEntry 方法,理解候选配置类如何收集、过滤、排序。然后找一个熟悉的自动配置类,比如 DataSourceAutoConfiguration,看它上面的条件注解如何组合。
我自己调试自定义 Starter 时,现在的习惯是:写完一个最小可运行示例,把所有自动配置类全开 debug 日志,先看一遍自动配置报告,确认 Positive/Negative matches 完全符合预期,再把它发布出去。这个过程看起来多花了几分钟,但实际上能挡掉后面一大串联调和集成问题。自动装配机制说到底还是在帮开发者省事,前提是我们得看懂它手上的那张“装配清单”,并且知道怎么核对清单。