一、概述
SpringBoot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。接下来我们通过一段简单的代码来分析自动配置的原理:
二、简单代码案例分析
1、我们写入下面这个方法并测试
csharp
@Component
public class TokenParser {
public void parse(){
System.out.println("TokenParser ... parse ...");
}
}
2、在测试类中,添加测试方法
kotlin
@SpringBootTest
public class AutoConfigurationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
public void testTokenParse(){
System.out.println(applicationContext.getBean(TokenParser.class));
}
//省略其他代码...
}
3、执行测试方法

异常信息描述: 没有com.example.TokenParse类型的bean
说明:在Spring容器中没有找到com.example.TokenParse类型的bean对象
那为什么我们引入进来的第三方依赖当中的bean以及配置类为什么没有生效?
- 原因就是我们在类上添加
@Component
注解来声明bean对象时,还需要保证@Component
注解能被Spring的组件扫描到。 - SpringBoot项目中的
@SpringBootApplication
注解,具有包扫描的作用,但是它只会扫描启动类所在的当前包以及子包。当前包:com.itheima, 第三方依赖中提供的包:com.example(扫描不到) 那么如何解决以上问题的呢? - 方案1:
@ComponentScan
组件扫描 - 方案2:
@Import
导入(使用@Import
导入的类会被Spring加载到IOC容器中)
方案一
@ComponentScan
组件扫描
less
@SpringBootApplication
@ComponentScan({"com.itheima","com.example"}) //指定要扫描的包
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
重新执行测试方法,控制台日志输出:

大家可以想象一下,如果采用以上这种方式来完成自动配置,那我们进行项目开发时,当需要引入大量的第三方的依赖,就需要在启动类上配置N多要扫描的包,这种方式会很繁琐。而且这种大面积的扫描性能也比较低。 缺点:
- 使用繁琐
- 性能低
结论:SpringBoot中并没有采用以上这种方案。 2. ##### 方案二 @Import导入
-
导入形式主要有以下几种:
- 导入普通类
- 导入配置类
- 导入ImportSelector接口实现类
1). 使用@Import导入普通类:
less
@Import(TokenParser.class) //导入的类会被Spring加载到IOC容器中
@SpringBootApplication
public class SpringbootWebConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfigApplication.class, args);
}
}
重新执行测试方法,控制台日志输出:

2). 使用@Import导入配置类:
- 配置类
typescript
@Configuration
public class HeaderConfig {
@Bean
public HeaderParser headerParser(){
return new HeaderParser();
}
@Bean
public HeaderGenerator headerGenerator(){
return new HeaderGenerator();
}
}
- 启动类
less
@Import(HeaderConfig.class) //导入配置类
@SpringBootApplication
public class SpringbootWebConfig2Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfig2Application.class, args);
}
}
- 测试类
csharp
@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接口实现类
typescript
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//返回值字符串数组(数组中封装了全限定名称的类)
return new String[]{"com.example.HeaderConfig"};
}
}
- 启动类
less
@Import(MyImportSelector.class) //导入ImportSelector接口实现类
@SpringBootApplication
public class SpringbootWebConfig2Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfig2Application.class, args);
}
}
执行测试方法:

我们使用@Import注解通过这三种方式都可以导入第三方依赖中所提供的bean或者是配置类。
思考:如果基于以上方式完成自动配置,当要引入一个第三方依赖时,是不是还要知道第三方依赖中有哪些配置类和哪些Bean对象?
-
答案:是的。 (对程序员来讲,很不友好,而且比较繁琐) 思考:当我们要使用第三方依赖,依赖中到底有哪些bean和配置类,谁最清楚?
-
答案:第三方依赖自身最清楚。 结论:我们不用自己指定要导入哪些bean对象和配置类了,让第三方依赖它自己来指定。
怎么让第三方依赖自己指定bean对象和配置类?
-
比较常见的方案就是第三方依赖给我们提供一个注解,这个注解一般都以@EnableXxxx开头的注解,注解中封装的就是@Import注解 4). 使用第三方依赖提供的 @EnableXxxxx注解
-
第三方依赖中提供的注解
less
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)//指定要导入哪些bean对象或配置类
public @interface EnableHeaderConfig {
}
- 在使用时只需在启动类上加上@EnableXxxxx注解即可
less
@EnableHeaderConfig //使用第三方依赖提供的Enable开头的注解
@SpringBootApplication
public class SpringbootWebConfig2Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfig2Application.class, args);
}
}
执行测试方法:

以上四种方式都可以完成导入操作,但是第4种方式会更方便更优雅,而这种方式也是SpringBoot当中所采用的方式。
三、源码分析
知道了上面的案例后我们来看看源码,通过看源码来分析怎么样配置的 首先要从SpringBoot启动类上使用的核心注解@SpringBootApplication
开始分析:

@SpringBootConfiguration
标识当前类是一个配置类@EnableAutoConfiguration
@EnableXxx开头的注解,自动化配置核心@ComponentScan
组件扫描注解- 我们点进第一个注解进去看只是标识是个配置类
- 我们点进第二个注解
@EnableAutoConfiguration
进去看
这段代码我么注意@Import注解就是上文方案二的第三个方法是不是很像,导入实现类,那么我们接下来点进实现类里面去看看、

发现这个真的是继承ImportSelector接口就如上面案例 接下来我们看看这个AutoConfigurationImportSelector类中的一些代码来看看做了什么事情


点进去
点进去就能看到
这个字符串最终会和底下这个文件所对应
自动配置源码小结 自动配置原理源码入口就是
@SpringBootApplication
注解,在这个注解中封装了3个注解,分别是:
- @SpringBootConfiguration
- 声明当前类是一个配置类
- @ComponentScan
- 进行组件扫描(SpringBoot中默认扫描的是启动类所在的当前包及其子包)
- @EnableAutoConfiguration
- 封装了@Import注解(Import注解中指定了一个ImportSelector接口的实现类)
- 在实现类重写的selectImports()方法,读取当前项目下所有依赖jar包中
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
两个文件里面定义的配置类(配置类中定义了@Bean注解标识的方法)。 当SpringBoot程序启动时,就会加载配置文件当中所定义的配置类,并将这些配置类信息(类的全限定名)封装到String类型的数组中,最终通过@Import注解将这些配置类全部加载到Spring的IOC容器中,交给IOC容器管理。