Spring Boot启动流程
1. 启动类与main
方法
- 入口点:Spring Boot应用通常有一个带有
@SpringBootApplication
注解的主类,并包含一个public static void main(String[] args)
方法。@SpringBootApplication
是一个组合注解,包含了:@Configuration
: 标记该类为配置类。@EnableAutoConfiguration
: 自动根据classpath中的依赖进行配置。@ComponentScan
: 自动扫描指定包及其子包下的组件(如@Component
,@Service
,@Repository
,@Controller
等)。
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2. 创建SpringApplication
实例
- 构造函数:
- 推断应用类型:检查classpath中是否存在特定的类(如Servlet、Reactive相关的类),以确定是Servlet还是Reactive应用。
- 加载初始器和监听器:从
META-INF/spring.factories
文件中加载默认的ApplicationContextInitializer
和ApplicationListener
。
3. SpringApplication.run()
方法执行
环境准备
- 创建并配置Environment:基于命令行参数、系统属性、环境变量等初始化
Environment
对象。- 可以通过
--spring.config.location
或SPRING_CONFIG_LOCATION
环境变量指定外部配置文件的位置。
- 可以通过
- 触发事件:发布
ApplicationEnvironmentPreparedEvent
事件,允许外部工具或开发者自定义环境设置。- 可以通过实现
ApplicationListener<ApplicationEnvironmentPreparedEvent>
来自定义逻辑。
- 可以通过实现
打印Banner
- 如果启用了banner,则显示项目启动的图形化标识。
- 可以通过设置
spring.main.banner-mode
为off
来关闭banner。
- 可以通过设置
创建ApplicationContext
- 根据应用类型选择合适的
ApplicationContext
实现(例如,对于Web应用通常是AnnotationConfigServletWebServerApplicationContext
)。- 对于非Web应用,可能是
AnnotationConfigApplicationContext
。
- 对于非Web应用,可能是
加载Sources
- 将所有指定的source(通常是带有
@Configuration
注解的类)注册到ApplicationContext
中。- 这包括了自动扫描到的组件和显式声明的配置类。
刷新上下文前的准备工作
- 触发
ApplicationContextInitializedEvent
事件。 - 调用所有的
ApplicationContextInitializer
进行额外的配置。- 这些initializer可以在刷新之前对context进行进一步的定制。
4. ApplicationContext刷新过程
BeanFactory准备
- 设置bean工厂的各种属性,包括转换服务、属性编辑器等。
- 配置了诸如
PropertyEditorRegistrar
等用于处理属性值转换的机制。
- 配置了诸如
BeanDefinition加载
- 扫描classpath:寻找带有
@Component
,@Service
,@Repository
,@Controller
等注解的类,并将其注册为bean definitions。- 使用
ClassPathScanningCandidateComponentProvider
扫描指定包下的组件。 - 条件注解(如
@ConditionalOnClass
,@ConditionalOnMissingBean
等)确保仅在满足特定条件时才生效。
- 使用
自动配置
- 自动配置机制:通过
@EnableAutoConfiguration
触发自动配置过程。它会根据classpath中的依赖自动配置相应的beans。- 自动配置类位于
spring-boot-autoconfigure
模块中,它们通过spring.factories
文件被发现并加载。 - 每个自动配置类都可能有条件注解,确保仅在满足特定条件时才生效。
- 自动配置类位于
BeanFactoryPostProcessor执行
- 处理各种后处理器,如
PropertySourcesPlaceholderConfigurer
用于解析占位符。- 解析
${...}
形式的属性值,替换为实际值。
- 解析
Bean实例化
- 根据bean definitions实例化单例bean,并进行依赖注入。
- 初始化bean时,可能还会执行一些生命周期回调方法,如
@PostConstruct
标注的方法。 - 如果需要AOP代理,则在此阶段生成代理对象。
- 初始化bean时,可能还会执行一些生命周期回调方法,如
5. 刷新完成后的工作
- 触发ContextRefreshedEvent:表示
ApplicationContext
已完全刷新。 - 执行CommandLineRunner/ApplicationRunner beans:如果有任何实现了
CommandLineRunner
或ApplicationRunner
接口的beans,它们的方法会被调用。- 这些runner可以用来执行最后的初始化操作。
6. 应用就绪
- 触发ApplicationReadyEvent:表示应用已经准备好接收请求或执行预定的任务。
- 开启web服务器:如果是Web应用,此时嵌入式的Tomcat服务器将会启动,并开始监听HTTP请求。
7. 错误处理
- 错误处理机制:Spring Boot提供了强大的错误处理机制,包括但不限于自定义错误页面、全局异常处理器等,以增强应用的健壮性。
- 可以通过实现
ErrorController
来自定义错误页面。 - 使用
@ControllerAdvice
和@ExceptionHandler
来处理全局异常。
- 可以通过实现
额外细节
-
外部化配置:支持多种方式的外部化配置,如命令行参数、环境变量、properties/yaml文件等。
- 可以通过
application.properties
或application.yml
文件进行配置。 - 支持使用
@Value
注解直接注入配置值。 - 可以通过
spring.profiles.active
指定激活的profile,从而加载不同的配置。
- 可以通过
-
健康检查和度量:通过Actuator模块提供的端点,可以监控应用的健康状况和性能指标。
- 提供了诸如
/actuator/health
、/actuator/metrics
等端点。 - 可以自定义健康指示器(Health Indicator)来扩展健康检查功能。
- 提供了诸如
-
日志配置:Spring Boot默认支持多种日志框架,并可以通过简单的配置定制日志级别和输出格式。
- 支持Logback、Log4j2等日志框架,默认使用Logback。
- 可以通过
logging.level.*=DEBUG
等配置项调整日志级别。
关键技术细节
- 条件注解:如
@ConditionalOnClass
,@ConditionalOnMissingBean
等,使得Spring Boot能够根据当前环境动态地启用或禁用某些配置。 - 事件驱动模型:Spring Boot在整个启动过程中使用了大量的事件发布和监听机制,这使得开发者可以在应用启动的不同阶段插入自定义逻辑。
- 例如,可以通过实现
ApplicationListener<ApplicationStartedEvent>
来自定义启动逻辑。
- 例如,可以通过实现
示例代码片段
以下是一个更完整的示例,展示了如何使用Spring Boot的基本结构,并结合了一些高级特性和配置:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(DemoApplication.class);
// 关闭banner
app.setBannerMode(Banner.Mode.OFF);
// 添加自定义的ApplicationContextInitializer
app.addInitializers(new MyCustomApplicationContextInitializer());
app.run(args);
}
}
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("MyCommandLineRunner executed");
}
}
@Configuration
public class MyCustomConfig {
@Bean
@ConditionalOnMissingBean(name = "customBean")
public String customBean() {
return "This is a custom bean";
}
}
// 自定义健康检查
@Component
public class CustomHealthIndicator extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
builder.up().withDetail("custom", "Everything is OK!");
}
}
// 全局异常处理器
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public ResponseEntity<Object> exception(Exception exception) {
return new ResponseEntity<>("An error occurred: " + exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// 自定义ApplicationContextInitializer
public class MyCustomApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
// 自定义环境配置
Map<String, Object> map = new HashMap<>();
map.put("my.custom.property", "customValue");
environment.getPropertySources().addFirst(new MapPropertySource("customProperties", map));
}
}