SpringBoot自动化配置原理

一、概述

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多要扫描的包,这种方式会很繁琐。而且这种大面积的扫描性能也比较低。 缺点:

  1. 使用繁琐
  2. 性能低

结论: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容器管理。
相关推荐
Taylor不想被展开19 小时前
SpringBoot 项目集成 Flyway
java·spring boot·mysql
观望过往20 小时前
Spring Boot 高级特性:从原理到企业级实战
java·spring boot
喂完待续20 小时前
【序列晋升】38 Spring Data MongoDB 的统一数据访问范式与实践
java·spring·spring cloud·big data·序列晋升
郑洁文20 小时前
上门代管宠物系统的设计与实现
java·spring boot·后端·毕业设计·毕设
Jabes.yang20 小时前
互联网大厂Java面试:从Spring到Kafka的技术挑战
spring boot·spring cloud·eureka·kafka·mybatis·jpa·java面试
郝学胜-神的一滴20 小时前
QT与Spring Boot通信:实现HTTP请求的完整指南
开发语言·c++·spring boot·后端·qt·程序人生·http
汤姆yu21 小时前
2025版基于springboot的家政服务预约系统
java·spring boot·后端
拾贰_C21 小时前
【python_pytorch_ matplotlib】matplotlib不兼容:在jupyter中正常使用,转到pycharm中会报错无法运行
spring boot
Q_Q19632884751 天前
python+django/flask+springboot个性化旅游推荐系统(数据可视化) 景点推荐 路线匹配 用户画像建模 智能搜索筛选 图文展示系统
spring boot·python·django·flask·node.js
sunnyday04261 天前
Spring Boot中Bean Validation的groups属性深度解析
spring boot·后端·python