Spring Boot启动流程详解
spring boot将日常企业应用研发中的各种场景都抽取出来,做成一个个的starter(启动器)并遵循"约定大于配置"原则,简化了传统Spring应用的初始化过程。
1准备环境
2创建上下文
3启动业务逻辑
main 方法
一.new SpringApplication()
1.应用类型推断
启动时通过检测类路径中是否存在javax.servlet.Servlet
相关类,自动判断是Web应用还是普通应用。Web应用会初始化嵌入式Servlet容器(如Tomcat)。
2.初始化器和监听器设置
从META-INF/spring.factories
文件加载并实例化配置的初始化器和监听器类。这是Spring Boot自动配置机制的核心,实现了组件的自动发现和注册。
3.设置主类
二.调用run方法
1.环境准备阶段
加载所有配置源包括:
- 默认配置文件(application.properties/yml)
- 系统环境变量
- JVM系统属性
- 命令行参数
构建完整的应用环境配置。
2.上下文创建
组件扫描和Bean装配
通过@ComponentScan
扫描指定包下的组件。
配合@Configuration
类完成Bean定义注册。
自动配置类通过@EnableAutoConfiguration
触发。
3.上下文刷新
完成所有Bean的实例化、依赖注入和初始化:
- 所有bean初始化完成
- 事件监听器注册完毕
- web项目启动内嵌的Tomcat
4.启动后处理
执行所有ApplicationRunner
和CommandLineRunner触发事件通知
实现类
5.最终发布ApplicationReadyEvent
表示应用完全启动。
IoC控制反转
核心概念
控制:对象的创建的控制权限;
反转:将对象的控制权限交给spring。
之前我们创建对象时用new,现在直接从spring容器中取,维护对象之间的依赖关系,降低对象之间的耦合度。
实现方式为DI,依赖注入,有三种注入方式:构造器、setter、字段@Autowired注入
依赖注入方式
// 构造器注入
@Service
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
// Setter注入
@Service
public class ServiceC {
private ServiceD serviceD;
@Autowired
public void setServiceD(ServiceD serviceD) {
this.serviceD = serviceD;
}
}
// 字段注入(实际属于反射注入)
@Service
public class ServiceE {
@Autowired
private ServiceF serviceF;
}
AOP面向切面编程
它是一种编程思想,通过 动态代理 和 字节码增强 技术,实现不修改源代码的情况下给程序动态添加功能的一种技术,可以降低代码的耦合度,便于管理,提高代码的可重用性。
AOP的实现方式有两种:
JDK动态代理:目标类实现了接口,代理类实现与目标类相同的接口。
CGLIB动态代理:目标类未实现接口,生成目标类的子类作为代理类。
AOP应用场景
1.权限控制
在方法执行前校验用户权限。
2.性能监控
统计方法执行时间,识别性能瓶颈。
3.缓存优化
在方法调用前检查缓存,避免重复查询数据库。
4.日志记录
在方法执行前后记录参数、耗时等信息。
5.事务管理
通过@Transactional注解实现事务的自动开启和提交。
@Autowired和@Resource注解的区别
@Autowired是Spring提供的按类型注入的注解,若存在多个同类型Bean需配合@Qualifier指定名称。@Resource是Java标准注解,默认按名称匹配,名称未匹配时按类型注入。前者依赖Spring框架,后者更通用且支持名称/类型双策略。
Spring事务管理
分为 编程式事务 和 声明式事务
编程式事务
通过手动编写代码显式控制事务的开启、提交、回滚,灵活性高但侵入性强,适合需要细粒度控制事务的特殊场景
声明式事务
通过 AOP(面向切面编程) 自动管理事务,事务控制逻辑与业务代码解耦,声明式事务的实现方式分为 注解驱动 和 XML 配置 两种。
spring 设计模式
工厂设计模式:用于管理bean对象,包括创建、初始化、销毁等
单例设计模式:确保全局唯一 饿汉式 懒汉式 静态内部类(推荐)双重检查锁定(DCL)
动态代理模式:Aop面相切面编程 jdk cglib
观察者模式:事件监听的作用
适配器模式:将不兼容的接口转换为客户端期望的接口,如springmvc中handler adapter适配不同的handler
模板方法模式:spring提供一些模板类,如jdbctemplate实现连接不同的数据库,将具体实现步骤放到子类
Spring循环依赖
指两个或多个 Bean 相互依赖,形成闭环(如 A → B → A),导致容器无法正常完成初始化。
循环依赖的情况
1.Setter类型的循环依赖,spring自动解决 @Lazy 延迟加载
2.构造方法的循环依赖,
3.Prototype类型Bean的循环依赖
Spring 通过 三级缓存机制 解决单例 Bean 的循环依赖问题
HashMap存放了不同阶段的bean key是名称 value是对象
一级缓存是存放初始化完成的bean ConcurrentHashMap
二级缓存放的是早期bean的半成品仅完成实例化 并未属性注入 ConcurrentHashMap
三级缓存存放的是beanFactory工厂生成早期bean对象 HashMap
wait()和sleep()的区别
synchronized和Lock
CAS比较并交换
是一种无锁的原子操作技术,通过比较内存值与预期值进行原子更新,用于在多线程环境下实现变量的线程安全修改。它通过 CPU 底层指令保证操作的原子性,避免了传统锁(如synchronized)带来的线程阻塞和上下文切换开销,是 Java 并发编程(JUC 包)的核心技术之一。
CAS操作可能遇到ABA问题:某变量被其他线程从A改为B又改回A,CAS误判未修改。
ABA问题的解决方案包括:
- 使用版本号,每次修改递增版本号,CAS同时检查值和版本号;
- 通过标记位或时间戳机制确保状态唯一性,避免值被重复使用。
volatile
volatile用于保证变量在多线程中的可见性,修改后立即同步到主内存,并禁止指令重排序。
其原理是通过内存屏障(Memory Barrier)实现
/get()在当前线程内共享变量。每个Thread内部维护ThreadLocalMap,以ThreadLocal实例为键存储独立副本。
实现原理
内部数据结构
• ThreadLocalMap:每个线程(Thread对象)内部维护的哈希表,键为ThreadLocal实例(弱引用),值为线程本地变量副本
乐观锁和悲观锁
乐观锁假设操作冲突少,只在提交时检查数据是否被修改(如版本号机制),适用于读多写少场景。悲观锁预先加锁(如行锁、表锁),确保独占操作,适合写多场景,但影响并发性能。前者减少锁竞争,后者保证强一致性。
应用场景
悲观锁适用场景
• 金融交易:确保转账操作的原子性,避免超卖或重复扣款。
• 库存管理:在高并发下单场景中,防止超卖(如秒杀系统)。
• 强一致性需求:如数据库事务的隔离级别为SERIALIZABLE时。
乐观锁适用场景
• 电商库存:读多写少场景,通过版本号控制库存更新。
• 配置系统:频繁读取配置,偶尔更新时避免锁竞争。
• 分布式系统:结合Redis的WATCH命令实现乐观锁