【Springboot】介绍启动类和启动过程
- [【一】Spring Boot 启动类的注解](#【一】Spring Boot 启动类的注解)
- [【二】Spring Boot 项目启动的详细流程](#【二】Spring Boot 项目启动的详细流程)
-
- [【1】启动 Main 方法](#【1】启动 Main 方法)
- [【2】创建 SpringApplication 实例:](#【2】创建 SpringApplication 实例:)
- [【3】运行 SpringApplication 实例(run方法):这是最复杂的核心阶段。](#【3】运行 SpringApplication 实例(run方法):这是最复杂的核心阶段。)
- [【三】Spring Boot 生命周期中的扩展点及使用案例](#【三】Spring Boot 生命周期中的扩展点及使用案例)
-
- [【1】Application Events(应用事件) - 观察者模式](#【1】Application Events(应用事件) - 观察者模式)
- 【2】ApplicationContextInitializer(应用上下文初始化器)
- [【3】CommandLineRunner / ApplicationRunner](#【3】CommandLineRunner / ApplicationRunner)
- [【4】Spring Bean 生命周期钩子](#【4】Spring Bean 生命周期钩子)
- [【四】Spring Bean 生命周期中的重要组件](#【四】Spring Bean 生命周期中的重要组件)
-
- [【1】BeanPostProcessor (BPP) - 容器级后处理器](#【1】BeanPostProcessor (BPP) - 容器级后处理器)
- [【2】BeanFactoryPostProcessor (BFPP) - 工厂级后处理器](#【2】BeanFactoryPostProcessor (BFPP) - 工厂级后处理器)
- [【3】Aware 接口族 - 感知接口](#【3】Aware 接口族 - 感知接口)
- 【4】生命周期回调注解/接口
- 【5】生命周期执行顺序
【一】Spring Boot 启动类的注解
Spring Boot 启动类通常就是一个标注了 @SpringBootApplication的类,但这个注解是一个组合注解(Composite Annotation),理解它包含的元注解是关键。
【1】核心注解:@SpringBootApplication
这是启动类上唯一必须的注解,它整合了三个核心注解的功能:
(1)@SpringBootConfiguration:Spring容器会从该类中加载Bean定义
(1)作用:表明当前类是一个配置类。它的底层是 @Configuration,这意味着Spring容器会从该类中加载Bean定义(@Bean注解的方法)。
(2)为什么不用 @Configuration:@SpringBootConfiguration是Spring Boot提供的,语义上更明确地指出这是主配置类,但在功能上与 @Configuration无异。
(2)@EnableAutoConfiguration:自动配置依赖
(1)作用:启用Spring Boot的自动配置机制。这是Spring Boot魔法(Convention over Configuration)的核心。
(2)原理:这个注解会通知Spring Boot根据你添加的jar包依赖(如classpath下是否存在DataSource、SpringMVC等类),自动猜测并配置你需要的Bean。例如,当你引入了spring-boot-starter-web,它会自动配置Tomcat和Spring MVC。
(3)@ComponentScan:自动扫描并注册Bean
(1)作用:自动扫描并注册Bean。默认会扫描启动类所在包及其所有子包下的所有组件,包括@Component, @Service, @Repository, @Controller等注解的类,并将它们注册到Spring容器中。
(2)重要提示:这是为什么通常要把启动类放在项目根包(root package)下的原因。如果放在一个很深的包里,@ComponentScan可能无法扫描到其他重要的组件。
【2】其他常用注解(可根据需要添加)
虽然 @SpringBootApplication已经足够,但在某些场景下,你可能会在启动类上额外添加一些注解。
(1)@EnableScheduling
作用:启用Spring的定时任务功能。添加后,可以使用 @Scheduled注解来创建定时任务。
(2)@EnableAsync
作用:启用Spring的异步方法执行功能。添加后,可以使用 @Async注解来标记异步执行的方法。
(3)@EnableTransactionManagement
作用:启用注解式事务管理。不过,当Spring Boot检测到存在事务管理器(如引入了JDBC或JPA starter)时,此功能实际上是默认开启的。显式添加此注解只是为了代码意图更清晰。
(4)@EnableCaching
作用:启用Spring的注解驱动缓存功能。添加后,可以使用 @Cacheable, @CacheEvict等注解。
(5)@Import
作用:用于导入其他配置类(通常是@Configuration类),这些配置类可能不在@ComponentScan的扫描路径下。例如:@Import({CustomConfig.class, AnotherConfig.class})。
总结:对于大多数标准应用,只使用 @SpringBootApplication注解就足够了。其他注解如 @EnableScheduling等,应根据具体功能需求选择性添加。
【3】@MapperScan
@MapperScan注解的主要目的是告诉 MyBatis 应该去哪个或哪些包路径下扫描 Mapper 接口,并自动将其注册为 Spring 容器中的 Bean(MapperFactoryBean)。
(1)解决了什么问题?
在没有这个注解之前,你需要在每个 Mapper 接口上手动添加 @Mapper注解,或者手动配置 MapperFactoryBean,非常繁琐。@MapperScan通过包扫描的方式,实现了批量、自动的注册,极大简化了配置。
(2)底层机制:
当 Spring 容器启动时,它会处理 @MapperScan注解。MyBatis-Spring 整合模块会为指定的包路径创建 ClassPathMapperScanner,该扫描器会找到所有接口,并为每个接口动态创建一个 MapperFactoryBean的 BeanDefinition 注册到 Spring 容器中。MapperFactoryBean是一个 FactoryBean,它的 getObject()方法会使用 SqlSession为原始 Mapper 接口创建一个动态代理实例,并将这个代理实例作为 Bean 交给 Spring 管理。这就是为什么你可以在 Service 层直接 @Autowired一个接口的原因。
java
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import @SpringBootApplication
// 方式一:直接扫描一个包
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
// 方式二:扫描多个包
@MapperScan(basePackages = {"com.example.mapper", "com.example.other.dao"})
// 方式三:类型安全的扫描(推荐)
// 创建一个空的标记接口,放在 com.example.mapper 包下
public interface MapperScanMarker {
// 无任何方法,仅作为包路径标记
}
@SpringBootApplication
// 扫描标记接口所在的包
@MapperScan(basePackageClasses = MapperScanMarker.class)
public class DemoApplication {
// ...
}
【4】自动装配原理
(1)自动装配的核心思想
要解决的问题: 传统 Spring 应用需要大量手动配置(如 XML、Java Config)来集成第三方库或框架(例如数据源、事务管理器、MVC 等)。这个过程繁琐且容易出错。
解决方案: Spring Boot 自动装配通过预先定义的条件,在检测到项目的特定依赖、配置和类路径后,自动为应用注入所需的 Bean 并完成配置。简单来说,就是 "如果我在类路径上看到了 X,并且你没有自己配置 Y,那么我就自动给你配置一个默认的 Y"。
(2)实现原理的核心组件
(1)@SpringBootApplication与 @EnableAutoConfiguration
一切的起点是主启动类上的 @SpringBootApplication注解。它是一个组合注解(Composite Annotation),其核心功能之一由 @EnableAutoConfiguration提供。
java
@SpringBootApplication // 这是一个元注解,整合了其他注解的功能
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@EnableAutoConfiguration本身又是一个开关注解,它通过 @Import导入了最核心的加载器:AutoConfigurationImportSelector。
(2)AutoConfigurationImportSelector
这个类是自动装配的大脑。它的核心任务是决定需要导入哪些自动配置类。
工作原理:AutoConfigurationImportSelector会读取项目 classpath 下所有 JAR 包中的 META-INF/spring.factories 文件。
关键文件:在 spring-boot-autoconfigure-x.x.x.x.jar中,META-INF/spring.factories文件是一个键值对形式的配置文件。其中 EnableAutoConfiguration这个 key 后面列出了一长串的自动配置类(XXXAutoConfiguration)。
spring.factories文件片段示例:
java
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
# ... 省略上百个配置类
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
AutoConfigurationImportSelector会获取所有这些配置类的全限定名,并将它们作为候选配置类。
(3)@Conditional条件注解家族 - 灵魂所在
仅仅将候选配置类全部加载是不行的,Spring Boot 需要根据当前应用的环境来智能判断哪些配置类应该真正生效。这就是 @Conditional系列注解的作用。
这些注解是自动装配的"开关",它们会在配置类或 Bean 被加载前进行条件判断,只有满足所有条件,配置才会生效。
(4)XXXAutoConfiguration自动配置类
这些是具体的执行者。每个 XXXAutoConfiguration都是一个标准的 Spring 配置类(@Configuration),其内部使用 @Bean方法来定义组件,并且方法上通常装饰着各种 @Conditional注解。
【二】Spring Boot 项目启动的详细流程
Spring Boot 的启动流程可以看作是传统 Spring 应用启动流程的一个高度封装和自动化版本。其核心是 SpringApplication.run(Application.class, args)方法。
以下是其详细步骤:
【1】启动 Main 方法
JVM 调用应用程序的 main方法。
【2】创建 SpringApplication 实例:
(1)在 run方法内部,首先会创建一个 SpringApplication实例。
(2)这个实例会进行一些初始化工作,例如:
1-推断应用类型:判断是普通的Servlet应用(Spring MVC)还是响应式Web应用(WebFlux)。
2-初始化器(Initializers):加载 META-INF/spring.factories文件中配置的 ApplicationContextInitializer。
3-监听器(Listeners):加载 META-INF/spring.factories文件中配置的 ApplicationListener(应用监听器)。这些监听器将用于接收整个启动过程中发布的各种事件。
【3】运行 SpringApplication 实例(run方法):这是最复杂的核心阶段。
(1)a. 启动计时器 & 发布开始事件:启动一个计时器,并发布 ApplicationStartingEvent事件。此时容器还未创建,任何Bean都未初始化。
(2)b. 准备环境(Environment):创建并配置应用运行环境(Environment),读取所有配置源(application.properties/yaml、系统属性、环境变量等)。发布 ApplicationEnvironmentPreparedEvent事件。
(3)c. 创建应用上下文(ApplicationContext):根据第一步推断的应用类型,创建对应的 ApplicationContext(例如,对于Servlet应用,创建 AnnotationConfigServletWebServerApplicationContext)。
(4)d. 准备应用上下文:
将环境(Environment)设置到上下文中。
执行所有 ApplicationContextInitializer的 initialize方法,对上下文进行自定义初始化。
发布 ApplicationContextInitializedEvent事件。
(5)e. 刷新应用上下文(核心中的核心):调用上下文的 refresh()方法。这一步完成了传统Spring应用容器的所有初始化工作:
加载Bean定义:解析启动类(因为它是@Configuration),执行@ComponentScan扫描并注册所有Bean定义。
执行自动配置:执行 @EnableAutoConfiguration逻辑,加载 spring-boot-autoconfigurejar 包中 META-INF/spring.factories文件里的所有自动配置类(XXXAutoConfiguration),根据条件(@ConditionalOnXxx)判断是否需要配置相应的Bean。
初始化Bean:实例化所有非懒加载的单例Bean。
(6)f. 后置处理:执行 CommandLineRunner和 ApplicationRunner接口的实现Bean。
(7)g. 启动完成:发布 ApplicationStartedEvent事件,启动计时器停止,打印启动耗时日志。
整个流程伴随着事件的发布,允许开发者通过监听这些事件在特定阶段插入自定义逻辑。
【三】Spring Boot 生命周期中的扩展点及使用案例
Spring Boot 在整个生命周期中提供了大量"钩子"(Hook),允许开发者介入并执行自定义逻辑。以下是一些最重要的扩展点:
【1】Application Events(应用事件) - 观察者模式
通过实现 ApplicationListener接口或使用 @EventListener注解来监听特定事件。
常用事件:
ApplicationStartingEvent:应用刚启动,任何处理都还未进行。
ApplicationEnvironmentPreparedEvent:环境已准备完毕,上下文还未创建。
ApplicationContextInitializedEvent:上下文已创建且初始化器已被调用,但Bean定义还未加载。
ApplicationPreparedEvent:Bean定义已加载,但Bean还未实例化。
ApplicationStartedEvent:上下文已刷新,所有Bean已实例化,CommandLineRunner/ApplicationRunner还未执行。
ApplicationReadyEvent:应用已完全启动,可以正常接收请求。
案例:在应用启动成功后打印一条日志java
java
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class StartupNotifier {
@EventListener(ApplicationReadyEvent.class)
public void onAppReady() {
System.out.println("🎉 Application is now ready and can serve traffic!");
// 可以在这里执行一些启动后的检查,比如检查数据库连接状态等
}
}
【2】ApplicationContextInitializer(应用上下文初始化器)
在 ApplicationContext刷新(refresh)之前,对其执行自定义的初始化操作。
案例:在上下文准备阶段设置一个自定义属性java
java
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 向环境中添加一个属性
applicationContext.getEnvironment().getSystemProperties().put("myCustomProperty", "initialized");
}
}
注册方式:需要在 META-INF/spring.factories文件中注册。
复制org.springframework.context.ApplicationContextInitializer=com.example.MyInitializer
【3】CommandLineRunner / ApplicationRunner
在应用上下文刷新完成后、应用完全启动之前,执行一些特定的代码。非常适合进行数据初始化、缓存预热等操作。两者功能几乎一样,区别在于参数:
CommandLineRunner:提供原始的字符串数组参数 String... args(即main方法的args)。
ApplicationRunner:提供更结构化的 ApplicationArguments对象来解析参数。
案例:应用启动后初始化一些数据java
java
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class DataLoader implements CommandLineRunner {
private final UserRepository userRepository;
public DataLoader(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public void run(String... args) throws Exception {
// 检查数据库是否已有数据,如果没有则插入默认数据
if (userRepository.count() == 0) {
User admin = new User("admin", "admin@example.com");
userRepository.save(admin);
System.out.println("Initial admin user created.");
}
}
}
【4】Spring Bean 生命周期钩子
这是 Spring 框架本身的扩展点,在 Spring Boot 中同样适用。
@PostConstruct:在Bean的依赖注入完成后,初始化方法(InitializingBean.afterPropertiesSet)之前执行。
InitializingBean接口:实现 afterPropertiesSet()方法,在所有属性设置完成后执行。
@PreDestroy:在Bean被容器销毁之前执行。
案例:Bean初始化后连接资源java
java
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class ResourceConnector {
@PostConstruct
public void connect() {
System.out.println("Connecting to external resource...");
// 初始化连接
}
@PreDestroy
public void disconnect() {
System.out.println("Disconnecting from external resource...");
// 关闭连接,释放资源
}
}
【四】Spring Bean 生命周期中的重要组件
【1】BeanPostProcessor (BPP) - 容器级后处理器
(1)作用:这是Spring框架最强大、最核心的扩展接口。它作用于整个ApplicationContext,对所有Bean的初始化过程进行拦截和增强。你可以把它想象成一个"Bean加工流水线"。
(2)两个核心方法:
1-postProcessBeforeInitialization(Object bean, String beanName):在Bean的初始化回调方法(如@PostConstruct)之前执行。可以对Bean进行包装或替换(例如返回一个代理对象,AOP就是基于此实现的)。
2-postProcessAfterInitialization(Object bean, String beanName):在Bean的初始化回调方法之后执行。此时Bean已基本完成初始化。
(3)重要实现:AutowiredAnnotationBeanPostProcessor(处理@Autowired注解)、CommonAnnotationBeanPostProcessor(处理@PostConstruct、@Resource等)、AnnotationAwareAspectJAutoProxyCreator(负责AOP动态代理)。
【2】BeanFactoryPostProcessor (BFPP) - 工厂级后处理器
(1)作用:在Bean实例化之前,可以读取、修改Bean的定义(BeanDefinition)。它操作的是"蓝图",而不是Bean实例本身。
(2)核心方法:postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
(3)经典案例:PropertySourcesPlaceholderConfigurer(处理 ${...}占位符)就是在此时将Bean定义中的占位符替换为实际的属性值。
【3】Aware 接口族 - 感知接口
(1)作用:让Bean能"感知"到容器本身和某些特定的资源。这些接口的回调发生在BeanPostProcessor之前,初始化方法之后。
(2)常用接口:
1-BeanNameAware:感知自己在容器中的Bean名称。
2-ApplicationContextAware:感知自己所在的ApplicationContext(这是手动获取Bean的一种方式)。
3-BeanFactoryAware:感知创建自己的BeanFactory。
【4】生命周期回调注解/接口
(1)作用:定义Bean自身初始化和销毁时的行为。
(2)初始化:
JSR-250注解:@PostConstruct(推荐使用,标准注解)。
Spring接口:InitializingBean及其 afterPropertiesSet()方法。
XML配置:init-method属性。
(3)销毁:
JSR-250注解:@PreDestroy(推荐使用)。
Spring接口:DisposableBean及其 destroy()方法。
XML配置:destroy-method属性。
【5】生命周期执行顺序
执行顺序:BeanFactoryPostProcessor-> BeanPostProcessor的Before-> Aware接口 -> @PostConstruct-> InitializingBean-> init-method-> BeanPostProcessor的After-> ... -> @PreDestroy-> DisposableBean-> destroy-method。