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容器管理。
相关推荐
jstart千语2 小时前
【Spring AI】Advisors API—顾问(即拦截器)
java·人工智能·spring·ai
一碗绿豆汤3 小时前
JAVA+AI教程-第三天
java·spring
谢平康3 小时前
支持不限制大小,大文件分段批量上传功能(不受nginx /apache 上传大小限制)
java·vue.js·spring boot
都叫我大帅哥4 小时前
Spring AI MCP:让AI开发像“拼乐高”一样简单
java·spring·ai编程
啃火龙果的兔子5 小时前
nextjs+react接口会请求两次?
前端·spring·react.js
贰拾wan5 小时前
Spring中的设计模式
java·spring·设计模式
congvee5 小时前
springboot学习第5期 - spring data jpa
spring boot
夜斗小神社5 小时前
【黑马SpringCloud微服务开发与实战】(四)微服务02
spring·spring cloud·微服务
DanB246 小时前
Spring-boot实战demo
spring boot
灵犀学长7 小时前
Spring Boot Jackson 序列化常用配置详解
java·spring boot·后端