SpringBoot原理
约 1785 字大约 6 分钟
2025-03-02
1. 配置优先级
- 命令行参数(Program arguments) > java系统属性(VM options) > properties > yml(主流) > yaml
- 命令行运行jar文件:java [options] -jar < jar 文件 > [args...],options是设置java系统属性的,args是设置命令行参数的
2. bean管理
获取bean
Spring项目启动时会自动创建bean放在IOC容器中 (受到作用域及延迟初始化影响,这里主要针对于默认的单例非延迟加载的bean而言) ,可手动获取:
/*
* 获取bean对象
* */
@Autowired
private ApplicationContext applicationContext; // IOC容器对象
@Test
public void testGetBean(){
// 根据bean名称获取
DeptController bean1 = (DeptController) applicationContext.getBean("deptController");
System.out.println("bean1 = " + bean1);
// 根据bean类型获取
DeptController bean2 = applicationContext.getBean(DeptController.class);
System.out.println("bean2 = " + bean2);
// 根据bean名称和类型获取
DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
System.out.println("bean3 = " + bean3);
}
单例即获取的bean123是相同的对象,有着相同的地址。
- bean的作用域
注解@Lazy
:可以延迟初始化到使用bean对象时才实例化,而不是在Spring项目启动时
注解@Scope
:设置作用域
第三方bean
使用
@bean
注解,新建一个配置类声明第三方bean,比如dom4j的saxReader。后面使用时就可以使用@Autowrid
注入
@Configuration // 声明配置类
public class CommonConfig {
// 声明第三方bean
@Bean // 返回值交给IOC容器管理,成为bean对象
public SAXReader saxReader() {
return new SAXReader();
}
}
如果第三方bean需要依赖其它bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配。
3. SpringBoot原理
(1)起步依赖
根据maven的依赖传递,SpringBoot只需要引入一个起步依赖就可以依赖其所关联的所有依赖
(2)自动配置
- 当Spring容器启动后,一些配置类、bean对象会自动放入IOC容器,不需要手动声明,简化了配置操作
加载第三方依赖的bean和配置类
- 方案一:在启动类上使用
@ComponentScan
注解来扫描不同的包,将其他包中的类放入IOC容器,这种方案使用繁琐、性能低
@ComponentScan({"com.ham", "com.example"})
- 方案二:在启动类上使用
@Import
注解,它导入的类会被Spring加载到IOC容器中,可以导入的类有:普通类(HeaderParser)、配置类(HeaderConfig)、ImportSelector的接口实现类(MyImportSelector)
@Import({HeaderParser.class, HeaderConfig.class, MyImportSelector.class})
- 方案三:第三方依赖提供
@Enable***
命名形式的注解,他在定义时会封装@Import
注解将方法类导入IOC容器,所以只需加上@Enable***
注解就好了
源码跟踪
在3.4.2版本的Spring中,其自动配置原理如下:
- 进入
@SpringBootApplication
注解
- 可以看到引入了
@EnableAutoConfiguration
注解,进入后
- 可以看到它使用
@Import
注解导入了一个实现类AutoConfigurationImportSelector,进入后
- 可以看到它实现了DeferredImportSelector接口,进入后
- 可以看到它继承了ImportSelector接口,ImportSelector又声明了selectImports方法,返回的是一个String数组,DeferredImportSelector接口中也声明了selectImports方法
- 再次回到AutoConfigurationImportSelector类中,可以看到它重写了selectImports方法,
isEnabled
方法用于检查当前配置是否启用,当启用时调用getAutoConfigurationEntry方法,进入后
- 当配置启用时,调用getCandidateConfigurations
- 进入getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, this.getBeanClassLoader());
List<String> configurations = importCandidates.getCandidates();
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring/" + this.autoConfigurationAnnotation.getName() + ".imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
断言是否能读取到META-INF/spring/MyAutoConfiguration.imports自动配置文件,会放到list集合并返回。
如下图就可以找到Spring的自动配置文件:
打开就可以看到大量需要导入的类的全类名
随便进入一个都可以看到有@AutoConfiguration
注解,表名是个配置类,里面的方法都用@Bean
注解进行声明
条件装配注解(@Conditional)
作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring IOC容器中
位置:方法、类
注解@Conditional本身是一个父注解,派生出大量的子注解:
@ConditionalOnClass
:环境中有对应字节码文件,才注册bean到IOC容器。@ConditionalOnMissingBean
:环境中没有
对应的bean(“类型”(value属性)或“名称”(name属性)),才注册bean到IOC容器@ConditionalOnProperty
:配置文件中有对应属性和值,才注册bean到IOC容器。
(3)自定义Starter和autoConfigure案例
- 自定义阿里云oss工具类的起步依赖和自动配置,首先根目录新建两个SpringBoot模块,并删掉多余的文件,如下图所示:(新版可能要删掉iml文件)
- 然后在starter的pom文件中引入自定义的autoconfigure配置文件
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
- 再在autoconfigure的pom文件中引入aliyunoss的相关依赖
<!-- aliyunoss-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.4</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
- 还有web相关依赖,因为后面会用到MultipartFile
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 创建AliOSSProperties类
package com.aliyun.oss;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public String getAccessKeySecret() {
return accessKeySecret;
}
public void setAccessKeySecret(String accessKeySecret) {
this.accessKeySecret = accessKeySecret;
}
public String getBucketName() {
return bucketName;
}
public void setBucketName(String bucketName) {
this.bucketName = bucketName;
}
}
- 创建AliOSSUtils类
package com.aliyun.oss;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
/**
* 阿里云 OSS 工具类
*/
public class AliOSSUtils {
private AliOSSProperties aliOSSProperties;
public AliOSSProperties getAliOSSProperties() {
return aliOSSProperties;
}
public void setAliOSSProperties(AliOSSProperties aliOSSProperties) {
this.aliOSSProperties = aliOSSProperties;
}
// 使用自己的accessKeyId、accessKeySecret、bucketName
// @Value("${aliyun.oss.endpoint}")
// private String endpoint;
// @Value("${aliyun.oss.accessKeyId}")
// private String accessKeyId;
// @Value("${aliyun.oss.accessKeySecret}")
// private String accessKeySecret;
// @Value("${aliyun.oss.bucketName}")
// private String bucketName;
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile file) throws IOException {
// 获取参数
String endpoint = aliOSSProperties.getEndpoint();
String accessKeyId = aliOSSProperties.getAccessKeyId();
String accessKeySecret = aliOSSProperties.getAccessKeySecret();
String bucketName = aliOSSProperties.getBucketName();
// 获取上传的文件的输入流
InputStream inputStream = file.getInputStream();
// 避免文件覆盖
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);
//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}
}
- 创建AliOSSAutoConfiguration类
package com.aliyun.oss;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@EnableConfigurationProperties(AliOSSProperties.class) // 将AliOSSProperties导入IOC容器
@Configuration
public class AliOSSAutoConfiguration {
@Bean
public AliOSSUtils aliOSSUtils(AliOSSProperties aliOSSProperties) { // 将AliOSSProperties注入到AliOSSUtils中
AliOSSUtils aliOSSUtils = new AliOSSUtils(); // 创建AliOSSUtils对象
aliOSSUtils.setAliOSSProperties(aliOSSProperties); // 设置AliOSSProperties初始化
return aliOSSUtils;
}
}
- 最后创建imports文件,里面写自动配置类的全类名