spring-boot-starters模块包含了SpringBoot内置的各种starter:spring-boot-starter-xxx。由于SpringBoot内置的各种starter太多,以我们常用的spring-boot-starter-web起步依赖来探究。
看下spring-boot-starter-web模块的pom.xml文件内容:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.5.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.5.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.5.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
</dependencies>
可以看到,spring-boot-starter-web模块依赖了spring-boot-starter,spring-boot-starter-tomcat,spring-web和spring-webmvc等模块,居然没有依赖spring-boot-autoconfigure自动配置模块!
由于spring-boot-starter-web模块肯定跟spring-boot-autoconfigure自动配置模块有关,所以spring-boot-starter-web模块肯定是间接依赖了spring-boot-autoconfigure自动配置模块。
spring-boot-starter模块是绝大部分spring-boot-starter-xxx模块依赖的基础模块,是核心的Starter,包括了自动配置,日志和YAML支持。我们此时来关注下spring-boot-starter的pom.xml文件,也许其依赖了了spring-boot-autoconfigure自动配置模块。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.5.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.5.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.5.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>1.3.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.28</version>
<scope>compile</scope>
</dependency>
</dependencies>
我们前面的猜想没有错,正是spring-boot-starter模块依赖了spring-boot-autoconfigure自动配置模块!因此,到了这里我们就可以得出结论了:spring-boot-starter-web模块没有一行代码,但是其通过spring-boot-starter模块间接依赖了spring-boot-autoconfigure自动配置模块,从而实现了其起步依赖的功能。
我们再来看下spring-boot-autoconfigure自动配置模块的内部包结构:我们可以知道spring-boot-starter-web起步依赖的自动配置功能原来是由spring-boot-autoconfigure模块的web包下的类实现的。
到了这里spring-boot-starter-web起步依赖的构建基本原理我们就搞清楚了,但是还有一个特别重要的关键点我们还没Get到。这个关键点跟Maven的optional标签有的作用有关。
为了Get到这个点,我们先来思考一个问题:平时我们开发web项目为什么引入了spring-boot-starter-web这个起步依赖后,spring-boot-autoconfigure模块的web相关的自动配置类就会起自动起作用呢?
我们应该知道,某个自动配置类起作用往往是由于classpath中存在某个类,这里以DispatcherServletAutoConfiguration这个自动配置类为切入点去Get这个点好了。
先看下DispatcherServletAutoConfiguration能够自动配置的条件是啥?
由图所示,DispatcherServletAutoConfiguration能够自动配置的条件之一是@ConditionalOnClass(DispatcherServlet.class),即只有classpath中存在DispatcherServlet.class这个类,那么DispatcherServletAutoConfiguration自动配置相关逻辑才能起作用。
而DispatcherServlet这个类是在spring-webmvc这个依赖库中的,如下图所示:
此时我们再看下spring-boot-autoconfigure模块的pom.xml文件引入spring-webmvc这个依赖的情况。spring-boot-autoconfigure模块引入的spring-webmvc这个依赖时optional被设置为true,原来是可选依赖。即spring-webmvc这个依赖库只会被导入到spring-boot-autoconfigure模块中,而不会被导入到间接依赖spring-boot-autoconfigure模块的spring-boot-starter-web这个起步依赖中。
spring-boot-starter-web起步依赖显式引入了spring-webmvc这个依赖库,即引入spring-webmvc 时没有optional这个标签,又因为DispatcherServlet这个类是在spring-webmvc这个依赖库中的,从而classpath中存在DispatcherServlet这个类,因此DispatcherServletAutoConfiguration这个自动配置类就生效了。当然,web相关的其他自动配置类生效也是这个原理。
至此,我们也明白了spring-boot-autoconfigure模块为什么要把引入的spring-webmvc这个依赖作为可选依赖了,其目的就是为了在spring-boot-starter-web起步依赖中能显式引入spring-webmvc这个依赖(这个起决定性作用),从而我们开发web项目只要引入了spring-boot-starter-web起步依赖,那么web相关的自动配置类就生效,从而可以开箱即用这个就是spring-boot-starter-web这个起步依赖的构建原理了。
总结
- spring-boot-starter-xxx起步依赖没有一行代码,而是直接或间接依赖了xxx-autoconfigure模块,而xxx-autoconfigure模块承担了spring-boot-starter-xxx起步依赖自动配置的实现;
- xxx-autoconfigure自动配置模块引入了一些可选依赖,这些可选依赖不会被传递到spring-boot-starter-xxx起步依赖中,这是起步依赖构建的关键点;
- spring-boot-starter-xxx起步依赖显式引入了一些对自动配置起作用的可选依赖;
经过前面3步的准备,我们项目只要引入了某个起步依赖后,就可以开箱即用了,而不用手动去创建一些bean等。