目录
3.2 解决方案一:@ComponentScan组件扫描(不推荐)
2.2 版本锁定(dependencyManagement)
在前十几天中,我们一直在学习 Web 开发的应用层面——会配置、会开发、会调试。但一个合格的后端开发者,不能只停留在“会用”,更要懂“为什么这样用”。
今天,我们就来深入 SpringBoot 的底层原理,同时补齐 Maven 高级功能(分模块设计、继承聚合、私服)。这两块知识,是让你从“码农”走向“架构师”的第一步。
第一部分:SpringBoot 原理篇
1. 配置优先级
SpringBoot 支持三种配置文件:
-
application.properties -
application.yml -
application.yaml
如果三者同时存在,并都配置了同一个属性(如 server.port),优先级从高到低为:

application.properties > application.yml > application.yaml
注意事项:虽然springboot支持多种格式配置文件,但是在项目开发时,推荐统一使用一种格式的配置。(yml是主流)
除了配置文件,SpringBoot 还支持两种外部配置:

-
Java 系统属性:
-Dserver.port=9000 -
命令行参数:
--server.port=10010
五种配置方式优先级(从高到低)
命令行参数 > 系统属性 > properties > yml > yaml
实际场景:项目打包后,启动时动态指定端口:
java -Dserver.port=9000 -jar app.jar --server.port=10010最终生效的是
10010。
注意事项:
Springboot项目进行打包时,需要引入插件
spring-boot-maven-plugin(基于官网骨架创建项目,会自动添加该插件)
2. Bean 管理
2.1 Bean 的作用域
Spring 容器中的 Bean 默认是 单例(singleton) 的。
| 作用域 | 说明 |
|---|---|
singleton(默认) | 容器中只有一个实例,容器启动时创建 |
prototype | 每次获取都创建新实例 |
request(web) | 每次 HTTP 请求创建一个实例 |
session(web) | 每个 HTTP Session 创建一个实例 |
application(web) | 每个 ServletContext 创建一个实例 |
知道了bean的5种作用域了,我们要怎么去设置一个bean的作用域呢?
可以借助Spring中的@Scope注解来进行配置作用域
1). 测试一
-
控制器
//默认bean的作用域为:singleton (单例)
@RestController
@RequestMapping("/depts")
public class DeptController {
@Autowired
private DeptService deptService;
public DeptController(){
System.out.println("DeptController constructor ....");
}
//省略其他代码...
}
-
测试类
@SpringBootTest
class SpringbootWebConfig2ApplicationTests {
@Autowired
private ApplicationContext applicationContext; //IOC容器对象
//bean的作用域
@Test
public void testScope(){
for (int i = 0; i < 10; i++) {
DeptController deptController = applicationContext.getBean(DeptController.class);
System.out.println(deptController);
}
}
}
重启SpringBoot服务,运行测试方法,查看控制台打印的日志:

注意:
singleton的 Bean 默认在容器启动时创建,可用@Lazy延迟到第一次使用时创建。
2). 测试二
修改控制器DeptController代码:
@Scope("prototype") //bean作用域为非单例
@RestController
@RequestMapping("/depts")
public class DeptController {
@Autowired
private DeptService deptService;
public DeptController(){
System.out.println("DeptController constructor ....");
}
//省略其他代码...
}
重启SpringBoot服务,再次执行测试方法,查看控制吧打印的日志:

注意事项:
prototype的bean,每一次使用该bean的时候都会创建一个新的实例
实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean不需要配置scope属性
总结:
默认singleton的bean,在容器启动时被创建,可以使用@Lazy注解来延迟初始化(延迟到第一次使用时)
prototype的bean,每一次使用该bean的时候都会创建一个新的实例。
实际开发当中,绝大部分的bean是单例的,也就是说绝大部分bean不需要配置scope属性。
2.2 管理第三方 Bean
当我们要引入第三方依赖中的类(如阿里云 OSS 工具类)时,无法使用
@Component注解。此时要用@Bean显式声明。
方式一:在启动类中直接声明
@ServletComponentScan
@EnableScheduling
@SpringBootApplication
public class TliasWebManagementApplication {
public static void main(String[] args) {
SpringApplication.run(TliasWebManagementApplication.class, args);
}
@Bean
public AliyunOSSOperator aliyunOSSOperator(AliyunOSSProperties ossProperties) {
return new AliyunOSSOperator(ossProperties);
}
}
方式二:使用配置类(推荐)
@Configuration
public class OSSConfig {
@Bean
public AliyunOSSOperator aliyunOSSOperator(AliyunOSSProperties ossProperties) {
return new AliyunOSSOperator(ossProperties);
}
}
推荐做法:将第三方 Bean 统一放在
@Configuration类中,便于维护。
通过
@Bean注解的name 或 value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名。如果第三方bean需要依赖其他bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配。
3. SpringBoot 自动配置原理(核心)
起步依赖
假如我们没有使用SpringBoot,用的是Spring框架进行web程序的开发,此时我们就需要引入web程序开发所需要的一些依赖。

当我们引入了 spring-boot-starter-web 之后,maven会通过依赖传递特性,将web开发所需的常见依赖都传递下来。

所以,起步依赖的原理就是Maven的依赖传递。
自动配置
SpringBoot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。

3.1 为什么引入依赖后 Bean 不能直接用?
SpringBoot 的包扫描默认只扫描启动类所在包及其子包。第三方依赖的包(如
com.example)不会被扫描到,所以其@Component失效。
3.2 解决方案一:@ComponentScan组件扫描(不推荐)
@SpringBootApplication
@ComponentScan({"com.itheima","com.example"}) //指定要扫描的包
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
缺点:
-
使用繁琐
-
性能低
3.3 解决方案二:@Import
@Import可以手动导入指定类,让 Spring 将其加载到 IOC 容器。
四种导入方式:
| 方式 | 示例 |
|---|---|
| 导入普通类 | @Import(TokenParser.class) |
| 导入配置类 | @Import(HeaderConfig.class) |
| 导入 ImportSelector 实现类 | @Import(MyImportSelector.class) |
| 使用 @EnableXxx 注解(封装了 @Import) | @EnableHeaderConfig |
SpringBoot 正是采用 第 4 种方式,通过 @EnableAutoConfiguration 注解实现自动配置。
1). 使用@Import导入普通类:
@Import(TokenParser.class) //导入的类会被Spring加载到IOC容器中
@SpringBootApplication
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
2). 使用@Import导入配置类:
-
配置类
@Configuration
public class HeaderConfig {
@Bean
public HeaderParser headerParser(){
return new HeaderParser();
}
@Bean
public HeaderGenerator headerGenerator(){
return new HeaderGenerator();
}
}
-
启动类
@Import(HeaderConfig.class) //导入配置类 @SpringBootApplication public class SpringbootWebConfig2Application { public static void main(String[] args) { SpringApplication.run(SpringbootWebConfig2Application.class, args); } } -
测试类
@SpringBootTest
public class AutoConfigurationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
public void testHeaderParser(){
System.out.println(applicationContext.getBean(HeaderParser.class));
}
@Test
public void testHeaderGenerator(){
System.out.println(applicationContext.getBean(HeaderGenerator.class));
}
//省略其他代码...
}
3). 使用@Import导入ImportSelector接口实现类:
-
ImportSelector接口实现类
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//返回值字符串数组(数组中封装了全限定名称的类)
return new String[]{"com.example.HeaderConfig"};
}
}
-
启动类
@Import(MyImportSelector.class) //导入ImportSelector接口实现类
@SpringBootApplication
public class SpringbootWebConfig2Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfig2Application.class, args);
}
}
结论:我们不用自己指定要导入哪些bean对象和配置类了,让第三方依赖它自己来指定。
4). 使用第三方依赖提供的 @EnableXxxxx注解
-
第三方依赖中提供的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)//指定要导入哪些bean对象或配置类
public @interface EnableHeaderConfig {
}
-
在使用时只需在启动类上加上@EnableXxxxx注解即可
@EnableHeaderConfig //使用第三方依赖提供的Enable开头的注解
@SpringBootApplication
public class SpringbootWebConfig2Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfig2Application.class, args);
}
}
3.4 源码追踪(核心流程)
入口:@SpringBootApplication → 包含三个核心注解:

-
@SpringBootConfiguration:表明是配置类 -

-
@ComponentScan:默认扫描当前包及子包 -

-
@EnableAutoConfiguration:自动配置的核心 -

@EnableAutoConfiguration 内部通过 @Import(AutoConfigurationImportSelector.class) 导入了一个 ImportSelector。

该 Selector 的 selectImports() 方法会读取以下文件中的配置类全类名:

这些配置类中定义了大量的
@Bean,但并非全部生效——它们通常带有@Conditional条件注解,只有满足条件才会注册 Bean。
3.5 @Conditional 条件装配
@Conditional注解:
作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring的IOC容器中。
位置:方法、类
@Conditional本身是一个父注解,派生出大量的子注解:
@ConditionalOnClass:判断环境中有对应字节码文件,才注册bean到IOC容器。
@ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),才注册bean到IOC容器。
@ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器。
| 注解 | 作用 |
|---|---|
@ConditionalOnClass | 类路径下存在指定类时才生效 |
@ConditionalOnMissingBean | 容器中不存在指定 Bean 时才生效 |
@ConditionalOnProperty | 配置文件中存在指定属性值时才生效 |
例如 Gson 自动配置:


只有当项目中引入了 Gson 依赖,该 Bean 才会被创建。
4. 自定义 Starter(实战)
很多第三方技术没有官方的 SpringBoot Starter,我们可以自己封装一个,供团队复用。
需求
将阿里云 OSS 工具类封装成
aliyun-oss-spring-boot-starter,引入后直接注入AliyunOSSOperator即可使用。
规范
-
官方 Starter 命名:
spring-boot-starter-xxx -
第三方 Starter 命名:
xxx-spring-boot-starter
通常包含两个模块:
-
xxx-spring-boot-starter:仅做依赖管理 -
xxx-spring-boot-autoconfigure:自动配置核心代码
将来在项目当中进行相关功能开发时,只需要引入一个起步依赖就可以了,因为它会将autoconfigure自动配置的依赖给传递下来。
实现步骤
1. 创建 autoconfigure 模块
-
引入所需依赖(OSS SDK、Lombok、SpringBoot 等)
-
编写配置属性类
AliyunOSSProperties -
编写工具类
AliyunOSSOperator(去掉@Component) -
编写自动配置类:
@Configuration
@EnableConfigurationProperties(AliyunOSSProperties.class)
public class AliyunOSSAutoConfiguration {
@Bean
public AliyunOSSOperator aliyunOSSOperator(AliyunOSSProperties props) {
return new AliyunOSSOperator(props);
}
}
-
在
resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中写入:
com.aliyun.oss.AliyunOSSAutoConfiguration
2. 创建 starter 模块
-
仅保留
pom.xml -
依赖 autoconfigure 模块
3. 测试
在测试项目中引入 starter 依赖,配置 aliyun.oss.endpoint 和 bucketName,直接注入 AliyunOSSOperator 即可上传文件。
从此,团队中任何项目要使用阿里云 OSS,只需引入 starter 并配置必要参数,无需重复编写工具类。
第二部分:Maven 高级
1. 分模块设计与开发
1.1 为什么需要分模块?

一个大项目全部写在一个模块中会导致:
-
代码臃肿,难以维护
-
多人协作冲突频繁
-
通用组件无法复用
1.2 分模块策略

通常按功能模块拆分,例如:
-
tlias-pojo:实体类 -
tlias-utils:工具类 -
tlias-web-management:主业务
分模块设计就是将项目按照功能/结构拆分成若干个子模块,方便项目的管理维护、拓展,也方便模块键的相互调用、资源共享。
也可以按层次拆分(controller / service / mapper)。

1.3 实践
将原有项目中的
pojo和utils分别抽取为独立 Maven 模块,然后在主模块中通过<dependency>引入它们。
<dependency>
<groupId>com.itheima</groupId>
<artifactId>tlias-pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
在 tlias-pojo 模块的pom.xml文件中引入依赖
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.2.8</version>
</dependency>
</dependencies>
这样其他项目组也可以直接复用这些通用模块。
注意:分模块开发需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分。
总结:
什么是分模块设计:将项目按照功能拆分成若干个子模块
为什么要分模块设计:方便项目的管理维护、扩展,也方便模块间的相互调用,资源共享
注意事项:分模块设计需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分
2. 继承与聚合
2.1 继承(解决依赖重复配置)
多个子模块如果都需要相同的依赖(如 Lombok),可以在父工程中统一配置,子工程继承即可。
父工程(tlias-parent) 打包方式为 pom:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.8</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itheima</groupId>
<artifactId>tlias-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
Maven打包方式:
jar:普通模块打包,springboot项目基本都是jar包(内嵌tomcat运行)
war:普通web程序打包,需要部署在外部的tomcat服务器中运行
pom:父工程或聚合工程,该模块不写代码,仅进行依赖管理
子工程 继承父工程:
<parent>
<groupId>com.itheima</groupId>
<artifactId>tlias-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../tlias-parent/pom.xml</relativePath>
</parent>
<artifactId>tlias-utils</artifactId>
<version>1.0-SNAPSHOT</version>
父工程中 <dependencies> 里的依赖,子工程会自动继承。
注意:
在子工程中,配置了继承关系之后,坐标中的groupId是可以省略的,因为会自动继承父工程的 。
relativePath指定父工程的pom文件的相对位置(如果不指定,将从本地仓库/远程仓库查找该工程)。
../ 代表的上一级目录
2.2 版本锁定(dependencyManagement)
如果某些依赖只在部分子模块中使用,但希望统一版本,可以在父工程中使用
<dependencyManagement>:
<!--统一管理依赖版本-->
<dependencyManagement>
<dependencies>
<!--JWT令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
</dependencyManagement>
子模块使用时无需指定 version:
<dependencies>
<!--JWT令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>
区别:
<dependencies>会直接继承,<dependencyManagement>只管理版本,不强制引入。
还可以通过 自定义属性 集中管理版本号:

<properties>
<jwt.version>0.9.1</jwt.version>
</properties>
...
<version>${jwt.version}</version>
2.3 聚合(一键构建)
分模块后,要打包整个项目需要分别对每个模块执行
install,非常繁琐。
聚合:将多个模块组织成一个整体,同时进行项目的构建。
聚合工程:一个不具有业务功能的“空”工程(有且仅有一个pom文件) 【PS:一般来说,继承关系中的父工程与聚合关系中的聚合工程是同一个】
作用:快速构建项目(无需根据依赖关系手动构建,直接在聚合工程上构建即可)
在父工程 pom.xml 中配置:
<!-- 聚合其他模块 -->
<modules>
<module>../tlias-pojo</module>
<module>../tlias-utils</module>
<module>../tlias-web-management</module>
</modules>
之后在父工程执行 mvn clean package,所有子模块都会一起构建。
聚合与继承通常放在同一个父工程中,既管理依赖,又管理构建。
继承与聚合对比
作用
聚合用于快速构建项目
继承用于简化依赖配置、统一管理依赖
相同点:
聚合与继承的pom.xml文件打包方式均为pom,通常将两种关系制作到同一个pom文件中
聚合与继承均属于设计型模块,并无实际的模块内容
不同点:
聚合是在聚合工程中配置关系,聚合可以感知到参与聚合的模块有哪些
继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己
3. 私服(Nexus)
3.1 为什么需要私服?
团队内部开发的通用模块(如
tlias-utils)无法上传到中央仓库,也无法从其他同事的本地仓库获取。此时需要在公司内部搭建一个 私服,作为团队共享的仓库。
3.2 私服介绍
-
私服:是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的中央仓库,用于解决团队内部的资源共享与资源同步问题。
-
依赖查找顺序:
-
本地仓库
-
私服仓库
-
中央仓库
-
-
注意事项:私服在企业项目开发中,一个项目/公司,只需要一台即可(无需我们自己搭建,会使用即可)。
3.3 资源上传与下载
步骤分析

资源上传与下载,我们需要做三步配置,执行一条指令。
-
第一步配置:在maven的配置文件中配置访问私服的用户名、密码。
-
第二步配置:在maven的配置文件中配置连接私服的地址(url地址)。
-
第三步配置:在项目的pom.xml文件中配置上传资源的位置(url地址)。
-
配置好了上述三步之后,要上传资源到私服仓库,就执行执行maven生命周期:
deploy。
私服仓库说明:
RELEASE:存储自己开发的RELEASE发布版本的资源。
SNAPSHOT:存储自己开发的SNAPSHOT发布版本的资源。
Central:存储的是从中央仓库下载下来的依赖。
项目版本说明:
RELEASE(发布版本):功能趋于稳定、当前更新停止,可以用于发行的版本,存储在私服中的RELEASE仓库中。
SNAPSHOT(快照版本):功能不稳定、尚处于开发中的版本,即快照版本,存储在私服的SNAPSHOT仓库中。
具体操作
私服已经搭建好了,已经启动起来了,我们可以访问私服测试:http://localhost:8081。

私服准备好了之后,我们要做如下几步配置:
1. 设置私服的访问用户名/密码(在自己maven安装目录下的conf/settings.xml中的servers中配置)
<server>
<id>maven-releases</id>
<username>admin</username>
<password>admin</password>
</server>
<server>
<id>maven-snapshots</id>
<username>admin</username>
<password>admin</password>
</server>
2. 设置私服依赖下载的仓库组地址(在自己maven安装目录下的conf/settings.xml中的mirrors中配置)
<mirror>
<id>maven-public</id>
<mirrorOf>*</mirrorOf>
<url>http://localhost:8081/repository/maven-public/</url>
</mirror>
3. 设置私服依赖下载的仓库组地址(在自己maven安装目录下的conf/settings.xml中的profiles中配置)
<profile>
<id>allow-snapshots</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>maven-public</id>
<url>http://localhost:8081/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</profile>
注意:原阿里云服务器关掉
4. IDEA的maven工程的pom文件中配置上传(发布)地址(直接在tlias-parent中配置发布地址)
<distributionManagement>
<!-- release版本的发布地址 -->
<repository>
<id>maven-releases</id>
<url>http://localhost:8081/repository/maven-releases/</url>
</repository>
<!-- snapshot版本的发布地址 -->
<snapshotRepository>
<id>maven-snapshots</id>
<url>http://localhost:8081/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
配置完成之后,我们就可以在tlias-parent中执行deploy生命周期,将项目发布到私服仓库中。

通过日志,我们可以看到,这几个模块打的jar包确实已经上传到了私服仓库中(由于当前我们的项目是SNAPSHOT版本,所以jar包是上传到了snapshot仓库中)。
那接下来,我们再来打开私服来看一下:

我们看到,我们项目中的这几个模块,在私服中都有了。 那接下来,当其他项目组的开发人员在项目中,就可以直接通过依赖的坐标,就可以完成引入对应的依赖,此时本地仓库没有,就会自动从私服仓库中下载。
备注说明:在真实的企业开发中,私服都是在远程服务器中的,并不是在本地的。这里我们只是为了方便演示。
总结
| 模块 | 核心知识点 |
|---|---|
| 配置优先级 | 命令行 > 系统属性 > properties > yml > yaml |
| Bean 管理 | 作用域(singleton/prototype)、第三方 Bean 用 @Bean |
| 自动配置原理 | @EnableAutoConfiguration → ImportSelector → 读取 .imports 文件 → @Conditional 按需加载 |
| 自定义 Starter | 两个模块(starter + autoconfigure),配置 .imports 文件 |
| 分模块设计 | 按功能拆分,便于维护和复用 |
| 继承与聚合 | 父工程管理依赖版本,聚合实现一键构建 |
| 私服 | 团队内部资源共享,nexus 搭建,deploy 上传 |









4548

被折叠的 条评论
为什么被折叠?



