SpringBoot原理
起步依赖
假如我们没有使用SpringBoot
,用的是Spring框架进行web程序的开发,此时我们就需要引入web程序开发所需要的一些依赖。
当我们引入了 spring-boot-starter-web
之后,maven会通过**依赖传递**特性,将web开发所需的常见依赖都传递下来。
- 在SpringBoot给我们提供的这些起步依赖当中,已提供了当前程序开发所需要的所有的常见依赖(官网地址:https://docs.spring.io/spring-boot/docs/2.7.7/reference/htmlsingle/#using.build-systems.starters)。
依赖传递
- 比如:springboot-starter-web,这是web开发的起步依赖,在web开发的起步依赖当中,就集成了web开发中常见的依赖:json、web、webmvc、tomcat等。我们只需要引入这一个起步依赖,其他的依赖都会自动的通过Maven的依赖传递进来。
自动配置
SpringBoot的自动配置就是当spring(IOC)容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。
实现方案
首先在pom.xml
中 引入需要自动配置的jar包坐标
@ComponentScan
组件扫描
@SpringBootApplication
@ComponentScan({"com.example1","com.example2"}) //指定要扫描的包
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
- 使用繁琐
- 性能低
@Import导入
导入普通类
@Import(Common.class) //导入的类会被Spring加载到IOC容器中
@SpringBootApplication
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
导入配置类
- 配置类:
@Configuration
上面有个注解
@Configuration
public class HeaderConfig {
@Bean
public HeaderParser headerParser(){
return new HeaderParser();
}
@Bean
public HeaderGenerator headerGenerator(){
return new HeaderGenerator();
}
}
- 直接导入即可
@Import(HeaderConfig.class) //导入配置类
导入ImportSelector接口实现类
- ImportSelector接口实现类:
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//返回值字符串数组(数组中封装了全限定名称的类)
return new String[]{"com.example.HeaderConfig"};
}
}
- 直接导入即可
@Import(MyImportSelector.class) //导入ImportSelector接口实现类
!!使用第三方依赖提供的 @EnableXxxxx注解! 常用
如果基于以上方式完成自动配置,当要引入一个第三方依赖时,还要提前知道第三方依赖中有哪些配置类和哪些Bean对象.
让第三方依赖给提供一个注解,这个注解一般都以@EnableXxxx开头的注解,注解中封装的就是@Import注解
- 三方定义注解
- 注解里@Import()指定了要导入哪些bean对象或配置类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class) // 指定要导入哪些bean对象或配置类
public @interface EnableHeaderConfig {
}
- 使用时直接在启动类上加上@EnableHeaderConfig
@EnableHeaderConfig //使用第三方依赖提供的Enable开头的注解
@SpringBootApplication
public class SpringbootWebConfig2Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfig2Application.class, args);
}
}
如此, 可以自动配置第三方提供好的配置类和Bean对象.
自动配置的原理
三大注解
自动配置核心:@EnableAutoConfiguration .
启动类上面有个@SpringBootApplication
,是SpringBoot中最最重要的注解点开会发现:
圈外为元注解,@SpringBootApplication
由圈内三个部分组成:
- @SpringBootConfiguration : 该注解与@Configuration 注解作用相同, 声明配置类
- @EnableAutoConfiguration : 实现自动化配置的核心注解.
- @ComponentScan : 组件扫描, 默认扫描当前引导类所在的包及其所有子包
启动类中默认的静态方法就是返回一个IOC容器对象
@SpringBootApplication
public class TliasManager1Application {
public static void main(String[] args) {
SpringApplication.run(TliasManager1Application.class, args);
}
}
@EnableAutoConfiguration
- 点进去发现@Import({AutoConfigurationImportSelector.class}), 作用是将AutoConfigurationImportSelector.class内所有的Bean都交给IOC容器管理
- 点
AutoConfigurationImportSelector
进去发现: 实现了DeferredImportSelector
接口, 这个接口又实现了ImportSelector
接口,
ImportSelector接口
ImportSelector
是Spring框架中的一个重要接口,主要作用是在@Configuration类中动态决定需要导入哪些配置类。
- 其抽象方法return的是 要导入的配置类的全限定名数组.
AutoConfigurationImportSelector
- 重写了
ImportSelector
里的selectImports
方法, 这里通过getAutoConfigurationEntry来获得配置类的全限定名数组
- 继续追踪, 可以看到getAutoConfigurationEntry返回的是configurations, 而configurations又是通过getCandidateConfigurations获得的.
- 这个configurations具体包括了自动装配的是哪些Bean对象
- 继续追踪,这里的断言, 说名了自动装配类的文件存放位置, 主要找两个文件
- spring.factories
- org.springframework.boot.autoconfigure.AutoConfiguration.imports
目的地
- 选中一个类, 点这个靶子可以找到这俩文件
可以看到, 这俩文件里存放的都是Bean对象的全限定名
SpringBoot启动时,并不会将所有的Bean都交给IOC容器, 而是通过一些注解进行条件判断是否注入Bean.
自定义Start起步依赖
-
在实际的开发中, 经常会定义一些公共组件, 提供给各个项目团队使用.
-
在SpringBoot项目中, 一般会将这些公共组件封装为SpringBoot的starter, 传到maven的远程仓库(私服中).
-
然后 引入 对应坐标的依赖, 即可快速使用这些功能
- 配好依赖管理start, 自动配置会自动装配相关依赖
下面就用阿里云OSS云存储功能来创建起步依赖.如上图
准备
- 准备两个模块, 一个做starter, 一个做autoconfigure
- starter不需要写代码, 所以Src文件夹可以删掉不要
- 将阿里云服务的工具类和配置类放入配置模块
-
配置模块的pom.xml配置好阿里云相关依赖, 并设置好坐标
<groupId>aliyun-oss-spring-boot</groupId> <artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId> <version>1.0-SNAPSHOT</version>
建立联系
在starter模块的pom.xml中添加依赖, 和配置模块建立联系
<dependencies>
<dependency>
<groupId>aliyun-oss-spring-boot</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
使用
在需要使用此功能的模块引入依赖
<dependency>
<groupId>aliyun-oss-spring-boot</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
- 此时发现, 可以注入, 但是对象用不了
- 原因是当前模块启动类扫描不到其他模块, 所以工具类的这俩注解也没用了, 可以删掉.配置类也同理
完成自动配置
配置模块并没有将工具类送给IOC容器, 还需进行配置.
自动装配类
创建一个类 AliyunOssAutoConfiguration
@Configuration
public class AliyunOssAutoConfiguration {
// 将配置类作为参数传入, 目的是构建一个带参的工具类对象
@Bean //这下面会报错,原因看下面
public AliyunOSSOperator aliyunOSSOperator(AliyunOSSProperties aliyunOSSProperties) {
AliyunOSSOperator aliyunOSSOperator = new AliyunOSSOperator();
aliyunOSSOperator.setAliyunOSSProperties(aliyunOSSProperties); // 在aliyunOSSOperator上面加上了@Data
return aliyunOSSOperator; //将这个带参的工具类对象交给IOC容器
}
}
原因:
配置类没有注册为Bean, 它的对象不能直接使用.
解决办法:
加上@ConfigurationProperties(prefix = "aliyun.oss")
注册为Bean
装配类加上@EnableConfigurationProperties(AliyunOSSProperties.class)
来使用Bean
此时就不报错了,但是还没有完成自动配置.
- 还没有/需要给SpringBoot准备起步时要扫描的文件META-INF/spring.factories.
真正的自动配置
在resource里创建文件 META-INF/spring.factories
org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\
com.aliyun.oss.AliyunOssAutoConfiguration
- \ 代表下一行 , 可以忽略
- = 前面为Spring Boot定义的一个接口或抽象类,用于扩展数据库初始化依赖检测机制,理解为键即可
- 后面为要交给IOC的全限定名
执行流程
- 要使用此Bean的模块pom.xml文件中引入了starter, 程序启动首先找到这里
- 根据坐标定位到aliyun-oss-spring-boot-starter模块
- 而aliyun-oss-spring-boot-starter的pom.xml中引入了aliyun-oss-spring-boot-autoconfigure
- 定位到aliyun-oss-spring-boot-autoconfigure, 程序会自动去找META-INF/spring.factories文件
- 通过此全限定名找到相应的类
一看确实是配置类, 且有个@Bean注解, 此时才将这个类的对象return到IOC容器里
仍然报错
原因
-
Spring Boot版本兼容性
-
spring.factories 是 Spring Boot 1.x 和 2.x 中使用的自动配置机制,如果使用的是 Spring Boot 3.x,则需要改用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件进行自动配置声明。
解决办法
-
在META-INF下新建文件夹spring
-
新建文件 org.springframework.boot.autoconfigure.AutoConfiguration.imports
-
在文件内直接输入自动装配类的全限定名
com.aliyun.oss.AliyunOssAutoConfigurationd
- 此时可以看到不报错了, 可以正常使用上传功能
-
运行项目测试了一下功能, 没有出现问题
如果仍然报错
Caused by: java.lang.IllegalArgumentException: Class [com.aliyun.oss.AliyunOssAutoConfiguration] is not assignable to factory type [org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector]
- 此时可以看到不报错了, 可以正常使用上传功能
[外链图片转存中…(img-CivW09Ls-1754531343309)]
-
运行项目测试了一下功能, 没有出现问题
[外链图片转存中…(img-yu84c32D-1754531343310)]
如果仍然报错
Caused by: java.lang.IllegalArgumentException: Class [com.aliyun.oss.AliyunOssAutoConfiguration] is not assignable to factory type [org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector]
- 那就删掉之前不兼容的spring.factories这个文件再次运行项目