前端视角 Java Web 入门手册 5.2:真实世界 Web 开发——Spring Boot 应用启动流程

1. 概述

Spring Boot 通过简化 Spring 应用的配置和部署,极大地加快了开发速度。启动一个 Spring Boot Web应用涉及多个步骤,从引导应用到监听和处理 HTTP 请求。依赖注入作为 Spring 框架的核心特性,在应用启动和运行中起到了至关重要的作用

2. 启动流程的关键步骤

2.1 main() 方法与 SpringApplication.run()

入口点 :Spring Boot应用的启动从包含 main() 方法的主类开始。该方法调用 SpringApplication.run(),这是启动整个Spring Boot应用的关键步骤

java 复制代码
@SpringBootApplication
public class MySpringBootApp {

    public static void main(String[] args) {
        SpringApplication.run(MySpringBootApp.class, args);
    }
}
  • @SpringBootApplication:这是一个复合注解,包含了 @Configuration、@EnableAutoConfiguration 和 @ComponentScan
  • SpringApplication.run():启动Spring Boot应用,创建并刷新应用上下文

2.2 创建应用上下文(ApplicationContext)

SpringApplication.run() 方法的核心职责之一是创建 应用上下文(ApplicationContext) ,这是Spring应用的核心容器,负责管理和配置应用中的所有Beans。

步骤:

  1. 初始化Spring应用:确定应用类型
  2. 创建 ApplicationContext:具体实现通常是 AnnotationConfigServletWebServerApplicationContext,适用于基于注解的配置和Servlet Web服务器
  3. 加载配置类 :解析 @SpringBootApplication 和其他配置注解,加载 Bean 定义

2.3 自动配置(Auto-Configuration)

Spring Boot 的自动配置功能通过推断项目所需要的配置,自动配置Spring应用,减少大量繁琐的手动设置

工作原理:

  • 条件注解 :如 @ConditionalOnClass、@ConditionalOnMissingBean,根据特定条件决定是否应用某个配置
  • 配置类加载 :spring.factories 文件中列出的自动配置类被加载并应用
java 复制代码
@Configuration
@ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
public class WebMvcAutoConfiguration {
    // 自动配置Web MVC相关的Bean
}

优点:

  • 简化配置:自动配置常用组件,如数据源、MVC、模板引擎等
  • 可扩展性:开发者可以通过覆盖默认配置或定义新的配置类来自定义应用

2.4 组件扫描与Bean的创建

Spring Boot 通过 组件扫描(Component Scanning) 自动发现和注册 Beans。所有被注解标记的类(如 @Component、@Service、@Repository、@Controller 等)都会被Spring容器管理

步骤:

  1. 启动组件扫描:位于主类所在的包及其子包
  2. 识别候选组件:扫描带有特定注解的类
  3. 创建Bean实例:根据依赖注入的规则创建和初始化 Bean
java 复制代码
@Service
public class GreetingService {

    public String greet() {
        return "Hello, Spring Boot!";
    }
}

@RestController
public class GreetingController {

    private final GreetingService greetingService;

    @Autowired // 自动注入GreetingService
    public GreetingController(GreetingService greetingService) {
        this.greetingService = greetingService;
    }

    @GetMapping("/greet")
    public String greet() {
        return greetingService.greet();
    }
}

2.5 依赖注入(Dependency Injection, DI)详解

依赖注入是 Spring 框架的核心特性之一,它通过外部化对象的依赖关系,降低了代码的耦合度,提高了模块的可测试性和可维护性。在 Spring Boot 应用的启动过程中,依赖注入起到了至关重要的作用

2.5.1 依赖注入的原理

依赖注入遵循 控制反转(Inversion of Control, IoC) 的原则,即对象不再自行创建或查找其依赖,而是由外部容器(如 Spring 的 ApplicationContext)负责注入所需的依赖

核心概念:

  • IoC容器:Spring 的 ApplicationContext 即为 IoC 容器,负责管理Bean的生命周期和依赖关系
  • Bean:由 Spring 容器管理的对象
  • 依赖:Bean 所需要的其它 Bean 或资源

2.5.2 依赖注入的方式

Spring 支持多种依赖注入方式,主要包括:

  1. 构造器注入(Constructor Injection) :通过构造函数注入依赖,推荐使用
  2. Setter注入(Setter Injection) :通过 Setter 方法注入依赖
  3. 字段注入(Field Injection) :直接在字段上使用 @Autowired 注解注入依赖,不推荐使用,因为不利于测试和可维护性
  • 构造器注入:
java 复制代码
@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // 业务逻辑方法
}
  • Setter注入:
java 复制代码
@Service
public class UserService {
    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // 业务逻辑方法
}
  • 字段注入(不推荐):
java 复制代码
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    // 业务逻辑方法
}

2.5.3 Bean 的生命周期与依赖注入

在 Spring Boot 应用启动过程中,Beans 的创建和依赖注入遵循一定的生命周期:

  1. 实例化(Instantiation) :Spring 容器根据 Bean 定义创建 Bean 的实例
  2. 填充属性(Populate Properties) :注入 Bean 的依赖
  3. 调用初始化方法(Initialization) :执行Bean的初始化逻辑,如实现 InitializingBean 接口的 afterPropertiesSet() 方法,或使用 @PostConstruct 注解的方法
  4. 就绪使用(Ready to Use) :Bean 已准备好被应用程序使用
  5. 销毁(Destruction) :应用关闭时,执行Bean的销毁逻辑,如实现 DisposableBean 接口的 destroy() 方法,或使用 @PreDestroy 注解的方法
java 复制代码
@Service
public class ExampleService implements InitializingBean, DisposableBean {
    
    @Autowired
    private AnotherService anotherService;
    
    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化逻辑
    }
    
    @Override
    public void destroy() throws Exception {
        // 销毁逻辑
    }
}

使用 @PostConstruct 和 @PreDestroy 注解:

java 复制代码
@Service
public class ExampleService {
    
    @Autowired
    private AnotherService anotherService;
    
    @PostConstruct
    public void init() {
        // 初始化逻辑
    }
    
    @PreDestroy
    public void cleanup() {
        // 销毁逻辑
    }
}

2.5.4 依赖注入在启动流程中的作用

在 Spring Boot Web 应用的启动过程中,依赖注入涉及以下几个关键步骤:

  1. 组件扫描 :Spring扫描指定包下的所有类,识别带有注解(如 @Component、@Service、@Repository、@Controller 等)的 Bean
  2. 创建Bean实例:根据扫描到的Bean定义,Spring创建Bean的实例。
  3. 解析依赖关系:Spring 识别 Bean 之间的依赖关系,并根据依赖注入方式(构造器、Setter、字段)注入所需的依赖
  4. 初始化Bean:执行 Bean 的初始化方法,完成 Bean 的准备工作
  5. 注册Bean到容器:将 Bean 注册到 ApplicationContext 中,供其它组件使用

假设有以下组件:

  • UserRepository :数据访问层接口,继承自 JpaRepository
  • UserService :服务层,依赖 UserRepository
  • UserController :控制器,依赖 UserService
java 复制代码
// UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // 自定义查询方法
}

// UserService.java
@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    // 其他业务逻辑方法
}

// UserController.java
@RestController
@RequestMapping("/users")
public class UserController {
    
    private final UserService userService;
    
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }
    
    // 其他API端点
}

启动流程中的依赖注入:

  1. 组件扫描:Spring 扫描 com.example.hellospring 及其子包,发现三个 Bean:UserRepository、UserService、UserController
  2. 创建 UserRepository 实例:Spring 根据接口创建动态代理实例,并注入到容器中
  3. 创建 UserService 实例:Spring 识别 UserService 构造器中的 UserRepository 依赖,并注入 UserRepository 实例
  4. 创建 UserController 实例:Spring识别 UserController 构造器中的 UserService 依赖,并注入 UserService 实例
  5. 初始化和注册 :
    • 所有 Bean 的初始化方法被调用,完成 Bean 的配置和准备
    • Bean 被注册到 ApplicationContext 中,供应用程序使用

这样通过依赖注入,Spring Boot 确保各个组件之间的耦合度低,且责任清晰,便于测试和维护

2.6 设置嵌入式服务器(Embedded Server)

对于 Web 应用,Spring Boot 通常使用嵌入式服务器(如Tomcat、Jetty 或 Undertow)来运行应用,避免了传统的部署方式

  1. 解析依赖 :如 spring-boot-starter-web 会引入 Tomcat 依赖
  2. 配置服务器:根据配置文件和自动配置设置服务器参数
  3. 创建服务器实例:创建一个 Tomcat 服务器,并配置端口(默认8080)

2.7 启动嵌入式服务器并监听请求

一旦嵌入式服务器被配置和创建,它会被启动并开始监听指定端口的 HTTP 请求

  1. 初始化服务器:启动 Tomcat 容器
  2. 部署应用:将 Spring 应用部署到服务器中,注册 Servlet、过滤器等
  3. 开始监听:服务器开始接受并处理来自客户端的 HTTP 请求

2.8 请求处理流程

当服务器接收到 HTTP 请求时,以下步骤会被执行:

  1. DispatcherServlet 接收请求:作为前端控制器,负责分发请求到具体的处理器
  2. 获取 Handler:DispatcherServlet 通过 HandlerMapping 找到对应的 Controller 方法
  3. 调用 Controller 方法:执行业务逻辑,通常涉及服务层和数据访问层
  4. 返回响应:将结果打包成 HTTP 响应,并通过 DispatcherServlet 发送回客户端

处理流程:

  1. 请求被 DispatcherServlet 接收
  2. HandlerMapping 找到 GreetingController.greet() 方法
  3. 调用 greet() 方法,返回 "Hello, Spring Boot!"
  4. DispatcherServlet 将响应返回给客户端

3. 启动过程中的关键类与组件

理解 Spring Boot 启动过程的关键类和组件有助于深入了解其工作机制。以下是一些重要的类和组件:

  • SpringApplication:核心类,用于启动 Spring Boot 应用。配置应用上下文,触发自动配置等
  • ApplicationContext:Spring 的 IoC 容器,管理和配置应用中的所有 Beans
  • AutoConfiguration:自动配置类,基于项目依赖和配置自动配置 Spring 组件
  • DispatcherServlet:Spring MVC 的核心 Servlet,负责处理和分发 HTTP 请求
  • EmbeddedServletContainerFactory:用于创建嵌入式服务器实例的工厂类
  • BeanFactory 和 BeanDefinition:底层接口和类,定义和管理Bean的创建与注入
  • Environment:封装了应用的环境信息,如配置属性、系统属性等
  • Banner:Spring Boot 启动时显示的横幅,可以自定义或关闭

4. 启动流程图

Spring Boot Web 应用的启动流程图大致如下,可以更直观地理解整个过程:

plain 复制代码
+--------------------+
|     main() 方法     |
|  (调用 SpringApplication.run()) |
+---------+----------+
|
v
+---------+----------+
| SpringApplication  |
|     初始化         |
+---------+----------+
|
v
+---------+----------+
| 创建 ApplicationContext |
|    (IoC 容器)         |
+---------+----------+
|
v
+---------+----------+
|    自动配置 Auto-Configuration     |
|   (根据依赖和配置)                |
+---------+----------+
|
v
+---------+----------+
| 组件扫描与Bean创建   |
| @Component, @Service, etc. |
+---------+----------+
|
v
+---------+----------+
|   依赖注入(DI)    |
|    注入Bean依赖      |
+---------+----------+
|
v
+---------+----------+
|   设置嵌入式服务器  |
|   (Tomcat, Jetty, etc.)|
+---------+----------+
|
v
+---------+----------+
| 启动嵌入式服务器并监听请求 |
+---------+----------+
|
v
+---------+----------+
| 处理HTTP请求       |
| DispatcherServlet -> Controllers |
+--------------------+

5. 总结

通过深入了解 Spring Boot Web 应用的启动流程,特别是依赖注入的部分,可以更好地理解其工作原理,从而更有效地开发和调试应用:

  • 入口点 :应用启动从 main() 方法开始,调用 SpringApplication.run()
  • 应用上下文 :通过 ApplicationContext 管理和配置所有 Beans,实现依赖注入和 IoC
  • 自动配置:根据项目依赖和配置自动配置 Spring 组件,简化开发过程
  • 组件扫描:自动发现和注册带有特定注解的 Bean,支持依赖注入
  • 依赖注入:通过构造器、Setter 或字段注入方式,管理 Bean 之间的依赖关系
  • 嵌入式服务器:内置服务器(Tomcat)使应用可独立运行,简化部署
  • 请求处理:DispatcherServlet 作为前端控制器,负责分发和处理 HTTP 请求
相关推荐
小π军1 小时前
操作系统面试题
java·linux·服务器
倔强的石头1061 小时前
应用账号最小权限实践:读写账号、报表账号、运维账号分层
java·运维·开发语言
Shaoshing2 小时前
ThreadLocal
java·jvm·threadlocal
写后端的胖头鱼2 小时前
一文详解CAS
java·jvm·spring·cas·乐观锁·自旋
鱼宵2 小时前
Spring AI 提示词模板:{变量} 参数化 + few-shot,一条提示词反复用
java·人工智能·spring·few-shot·提示词工程·springai
调试人生的显微镜2 小时前
iOS开发入门:Interface Builder、基础控件及UITextField详解
后端·ios
泡海椒2 小时前
巡检照片与工单附件:jquick-pdf 图片嵌入的业务实战
后端
蜗牛互联网2 小时前
Python消费Responses SSE事件:增量文本、超时与取消
java·开发语言·人工智能·后端·python
AI持续学习2 小时前
文档权限变更如何回归测试?账号、空间、文件和分享链接用例清单
后端