SpringBoot
自动配置
组件扫描
示例要引入的工具类:
java
@Component
public class TokenParser {
public void parse(){
System.out.println("TokenParser ... parse ...");
}
}
测试类方法如下:
java
@SpringBootTest
public class AutoConfigurationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
public void testTokenParse(){
System.out.println(applicationContext.getBean(TokenParser.class));
}
}
-
当前测试方法运行时在Spring容器中没有找到com.example.TokenParse类型的bean对象。
-
因为在类上添加
@Component注解来声明bean对象时,还需要保证@Component注解能被Spring的组件扫描到。 -
SpringBoot项目中的
@SpringBootApplication注解,具有包扫描的作用,但是它只会扫描启动类所在的当前包以及子包。
-
因此。我们需要在启动类上面加上@ComponentScan来进行组件扫描:
JAVA
@ComponentScan({"com.XXX"}) //指定要扫描的包
@SpringBootApplication
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
除此之外,还可以使用@Import导入要扫描的类:
- 导入普通类
- 导入配置类
- 导入ImportSelector接口实现类
一、导入普通类示例:
- 启动类代码块:
JAVA
@Import(XXX.class)//导入的类会被Spring加载到IOC容器中
@SpringBootApplication
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
二、导入配置类示例:
- 启动类代码块:
JAVA
@Import(XXX.class) //导入配置类
@SpringBootApplication
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
三、使用@Import导入ImportSelector接口实现类:
- 启动类代码块:
JAVA
@Import(XXXImportSelector.class) //导入ImportSelector接口实现类
@SpringBootApplication
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
第三方提供的组件扫描
以上配置仍比较繁琐,实际上,一般第三方依赖都会给我们@EnableXXX注解
只需要在启动类上加入@EnableXXX注解就可以引入Bean对象
- 第三方注解:
JAva
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(XXXImportSelector.class)//指定要导入哪些bean对象或配置类
public @interface EnableXXX {
}
- 启动类代码块:
Java
@EnableXXX //使用第三方依赖提供的Enable开头的注解
@SpringBootApplication
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
自动配置原理
- 在启动类上有
@SpringBootApplication注解
java
@SpringBootApplication
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
在@SpringBootApplication注解中其实就封装了配置类注解 @SpringBootConfiguration标识当前类是一个配置类。除此之外还有组件扫描注解 @ComponentScan 来进行组件扫描*(SpringBoot中默认扫描的是启动类所在的当前包及其子包)*。
还有@EnableAutoConfiguration注解,而这个注解又封装了@Import注解来指定一个ImportSelection 接口的实现类,该实现类重写了**selectionImports()**方法,读取当前项目下所有依赖jar包中META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports两个文件里面定义的配置类(配置类中定义了@Bean注解标识的方法)。
于是,当SpringBoot程序启动时,就会加载配置文件当中所定义的配置类,并将这些配置类信息*(类的全限定名)*封装到String类型的数组中,最终通过@Import注解将这些配置类全部加载到Spring的IOC容器中,交给IOC容器管理。
值得注意的是,在声明Bean对象时,又会使用
@Conditional开头的相关注解来按照条件进行装配。只有满足了一定的条件,才会把bean对象注册到Spring的IOC容器中。
关于@Conditional相关注解
这些注解都是用来判定是否满足条件,
if { true }则将对应bean对象注册到Spring IOC容器中。
条件装配注解:
@ConditionalOnClass:判断环境中 有 对应字节码文件,才注册bean到IOC容器。@ConditionalOnMissingBean:判断环境中 没有 对应的bean(类型或名称),才注册bean到IOC容器。@ConditionalOnProperty:判断配置文件中 有 对应属性和值,才注册bean到IOC容器。
根据自动配置原理手搓一个aliyun-oss-spring-boot-starter上传文件
-
创建aliyun-oss-spring-boot-starter 模块进行阿里云依赖的统一管理,并且引入aliyun-oss-spring-boot-autoconfigure 这样在使用时只需要引入starter起步依赖即可。不存放任何代码文件。
-
aliyun-oss-spring-boot-starter模块中的pom文件配置:

springboot会通过依赖传递自动将autoconfigure依赖也一并传递下来。
-
创建aliyun-oss-spring-boot-autoconfigure 模块,创建AliyunOSSProperties 实体类,AliyunOSSOperator 工具类,AliOSSAutoConfiguration 配置类。除此之外,还要再引入有关于阿里云OSS的各项依赖信息。
AliyunOSSOperator 工具类:
(详细源码置于文末)

值得注意的是,需要设置一个有参构造函数 来将AliyunOSSProperties 对象传递给工具类。
AliyunOSSProperties 实体类:
java
@Data
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOSSProperties {
private String endpoint;
private String bucketName;
private String region;
}
此时的AliyunOSSProperties类还未通过 @EnableConfigurationProperties 注册并标记为 Spring 组件
因此,要定义一个自动配置类 AliOSSAutoConfiguration 配置类,在该自动配置类当中来声明 bean对象。
AliOSSAutoConfiguration 配置类:
Java
@EnableConfigurationProperties(AliyunOSSProperties.class)
@Configuration
public class AliyunOSSAutoConfiguration{
@Bean
@ConditionalOnMissingBean
public AliyunOSSOperator aliyunOSSOperator(AliyunOSSProperties aliyunOSSProperties){
return new AliyunOSSOperator(aliyunOSSProperties);
}
}
@EnableConfigurationProperties 注解底层封装了**@Import注解,而前面又了解到 @Import**注解可以导入要扫描的普通类,于是,这里就成功地将AliyunOSSProperties.class引入了IOC容器中声明为Bean对象。
不可忽略的十分重要的最后一步:
在 aliyun-oss-spring-boot-autoconfigure 模块中的resources下,新建自动配置文件 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。

文件内信息:
imports
com.aliyun.oss.AliyunOSSAutoConfiguration
其实就是AliyunOSSAutoConfiguration配置类的全类名。
最后就可以创建一个测试工程来检查功能的实现了
需要在工程的pom文件下引入已经aliyun-oss-spring-boot-starter起步依赖:
XML
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
并且在yml配置文件中配置好阿里云的配置信息:
YAML
aliyun:
oss:
endpoint: 填入地址
bucketName: 仓库名称
#时区信息
region: cn-beijing
UploadController类中的编码:
Java
@RestController
public class UploadController {
@Autowired
private AliyunOSSOperator aliyunOSSOperator;
@PostMapping("/upload")
public String upload(MultipartFile image) throws Exception {
//上传文件到阿里云 OSS
String url = aliyunOSSOperator.upload(image);
System.out.println(url);
return url;
}
}
启动工程,在Apifox中发送请求:

测试成功,返回了url地址信息,完结撒花。
以后在其他项目中直接把依赖引入进来并且配置有关OSS信息就可以直接使用了,虽然写起步依赖挺麻烦的,但是十分方便了未来的复用。
摸清楚spring boot原理对于项目还是很有帮助的。
AliyunOSSOperator 工具类详细源码:
Java
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
public class AliyunOSSOperator{
/*通过@value注解单个属性注入
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
@Value("${aliyun.oss.endpoint}")
private String endpoint;
// 填写Bucket名称,例如examplebucket。
@Value("${aliyun.oss.bucketName}")
private String bucketName;
// 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。
@Value("${aliyun.oss.region}")
private String region;*/
//@ConfigurationProperties注解注入
private AliyunOSSProperties aliyunOSSProperties;
/* 有参构造 */
public AliyunOSSOperator(AliyunOSSProperties aliyunOSSProperties) {
this.aliyunOSSProperties = aliyunOSSProperties;
}
public String upload(MultipartFile file) throws Exception {
String endpoint = aliyunOSSProperties.getEndpoint();
String bucketName = aliyunOSSProperties.getBucketName();
String region = aliyunOSSProperties.getRegion();
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
//1.获取原始文件名称
String originalFilename = file.getOriginalFilename();
//2.获取原始文件名后缀
String extension = "";
int lastDotIndex = originalFilename.lastIndexOf('.');
//2.1 IF语句判断lastDotIndex是否大于0,大于0则截取,避免"."为文件名称开头
if (lastDotIndex > 0) {
extension = originalFilename.substring(lastDotIndex);
}
//3.生成新文件名
String newFileName = UUID.randomUUID().toString()+extension;
//获取当前系统日期的字符串,格式为yyyy/MM
String dir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM"));
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
String objectName = dir + "/" + newFileName;
// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
//String filePath= "";
// 创建OSSClient实例。
// 当OSSClient实例不再使用时,调用shutdown方法以释放资源。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
// 创建PutObjectRequest对象。
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName,file.getInputStream());
// 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
// ObjectMetadata metadata = new ObjectMetadata();
// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
// metadata.setObjectAcl(CannedAccessControlList.Private);
// putObjectRequest.setMetadata(metadata);
// 上传文件。
PutObjectResult result = ossClient.putObject(putObjectRequest);
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
//将文件的URL地址作为返回值
return endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + objectName;
}
}
AliyunOSSProperties 实体类:
Java
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOSSProperties {
private String endpoint;
private String bucketName;
private String region;
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public String getBucketName() {
return bucketName;
}
public void setBucketName(String bucketName) {
this.bucketName = bucketName;
}
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
@Override
public String toString() {
return "AliyunOSSProperties{" +
"endpoint='" + endpoint + '\'' +
", bucketName='" + bucketName + '\'' +
", region='" + region + '\'' +
'}';
}
}
AliOSSAutoConfiguration 配置类:
java
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@EnableConfigurationProperties(AliyunOSSProperties.class)
@Configuration
public class AliyunOSSAutoConfiguration{
@Bean
@ConditionalOnMissingBean
public AliyunOSSOperator aliyunOSSOperator(AliyunOSSProperties aliyunOSSProperties){
return new AliyunOSSOperator(aliyunOSSProperties);
}
}
aliyun-oss-spring-boot-starter模块中的pom文件配置:
xml
<?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 https://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.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>aliyun-oss-spring-boot-autoconfigure</name>
<description>aliyun-oss-spring-boot-autoconfigure</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.4</version>
<scope>compile</scope>
</dependency>
<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>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
</project>
aliyun-oss-spring-boot-starter模块pom文件:
xml
<?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 https://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.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>aliyun-oss-spring-boot-starter</name>
<description>aliyun-oss-spring-boot-starter</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>