一个注解启动整个项目?@SpringBootApplication 到底做了什么?

刚接触 SpringBoot 的时候,我一直觉得这玩意很神奇。

以前启动 Spring 项目:

<context:component-scan/>
<bean/>
<bean/>
<bean/>

各种配置写一堆。

后来接触 SpringBoot。

发现启动类就这么几行:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

项目居然就启动了。

第一次看到的时候我特别疑惑:

这个注解到底干了什么?

为什么删掉它项目直接启动失败?

为什么只加一个注解就能扫描 Bean、加载配置、自动配置各种组件?

今天我们就把它拆开看看。


先说结论

很多人以为:

@SpringBootApplication

是一个超级注解。

其实不是。

点进去你会发现:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {

}

看到这里是不是突然没那么神秘了。

它本质上就是:

三个注解的组合

第一个:@ComponentScan

这个大家最熟。

它负责扫描 Bean。

例如:

@Service
public class UserService {

}
@RestController
public class UserController {

}

Spring 为什么能发现它们?

因为:

@ComponentScan

启动时会扫描当前包以及子包。

然后把这些类注册到容器。

如果把它去掉:

@SpringBootApplication

改成:

@SpringBootConfiguration
@EnableAutoConfiguration

你会发现:

Controller 不见了
Service 不见了
Repository 不见了

因为根本没人扫描它们。


第二个:@SpringBootConfiguration

继续点进去:

@Configuration
public @interface SpringBootConfiguration {

}

是不是很眼熟?

实际上就是:

@Configuration

的一个包装。

什么意思?

告诉 Spring:

当前类是配置类

例如:

@Bean
public UserService userService() {
    return new UserService();
}

Spring 就会把它注册成 Bean。

所以:

@SpringBootConfiguration

其实没有什么黑科技。

就是:

把启动类变成配置类

第三个:@EnableAutoConfiguration

真正厉害的来了。

很多人以为:

@SpringBootApplication

最核心的是扫描。

其实不是。

真正的灵魂是:

@EnableAutoConfiguration

因为自动配置全靠它。


什么叫自动配置

举个例子。

项目里引入:

mybatis-spring-boot-starter

什么都不写。

居然就能直接:

@Mapper
public interface UserMapper {

}

正常工作。

再引入:

spring-boot-starter-data-redis

RedisTemplate 又自动出现了。

很多人会觉得:

SpringBoot 好智能

但问题来了。

它怎么知道你想创建这些 Bean?


Debug 一下就明白了

断点打到:

SpringApplication.run()

一路跟进去。

最终会进入:

@EnableAutoConfiguration

然后继续进入:

AutoConfigurationImportSelector

看到这里很多人第一次懵。

名字很长。

其实翻译过来就一句话:

自动配置导入选择器

它负责决定:

哪些配置要加载
哪些配置不要加载

SpringBoot 真正干的事情

很多人觉得:

SpringBoot 会自动创建 Bean

其实不准确。

更准确地说:

SpringBoot 自动导入配置类

例如:

RedisAutoConfiguration
DataSourceAutoConfiguration
WebMvcAutoConfiguration

这些配置类里面本来就写好了:

@Bean

SpringBoot 只是帮你导进来。

然后 Spring 再正常创建 Bean。

所以流程其实是:

SpringBoot
    ↓
导入配置类
    ↓
Spring
    ↓
创建 Bean

为什么引入依赖就能生效

到这里很多人会发现新的问题。

既然自动配置这么厉害。

那它怎么知道:

RedisAutoConfiguration

在哪?

DataSourceAutoConfiguration

又在哪?

答案就在 Starter 里面。

每个 Starter 都提前声明好了:

我有哪些自动配置类

启动时 SpringBoot 全部读取。

然后根据条件决定是否加载。

所以:

引入 Starter
       ↓
发现自动配置类
       ↓
判断条件
       ↓
注册 Bean

这就是自动配置的核心流程。


为什么说 SpringBoot 最大的贡献不是自动配置

很多人学到这里会觉得:

SpringBoot = 自动配置

其实只说对了一半。

更准确的说:

SpringBoot = 自动配置 + 条件装配

因为它不会无脑创建 Bean。

例如:

项目里没有 Redis 依赖。

RedisAutoConfiguration

不会加载。

项目里没有 MySQL 驱动。

DataSourceAutoConfiguration

不会加载。

这就是 SpringBoot 强大的地方。


现在再看启动类

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

实际上它做了三件事:

扫描 Bean

把启动类变成配置类

开启自动配置

其中最重要的就是:

@EnableAutoConfiguration

没有它。

SpringBoot 自动配置体系基本就没了。


看到这里。

你应该已经明白:

@SpringBootApplication

并不是什么神秘黑科技。

它只是把:

@ComponentScan

@Configuration

@EnableAutoConfiguration

组合到了一起。

真正让 SpringBoot 变得强大的。

是背后的自动配置机制。


上一篇:

《SpringBoot 到底帮 Spring 自动做了什么?为什么突然不用写 XML 了?》

下一篇:

《为什么引入一个 Starter 就能自动生效?SpringBoot 自动配置原理》


评论区聊聊:

你有没有想过:为什么只引入一个 Starter,项目就突然多出了一堆 Bean?

更多推荐