常用框架知识
Spring
Spring IOC
IoC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。
为什么叫控制反转?
- 控制:指的是对象创建(实例化、管理)的权力
- 反转:控制权交给外部环境(Spring 框架、IoC 容器)
@Autowired和@Resource的区别
@Autowired
是 Spring 提供的注解,@Resource
是 JDK 提供的注解。Autowired
默认的注入方式为byType
(根据类型进行匹配),@Resource
默认注入方式为byName
(根据名称进行匹配)。- 当一个接口存在多个实现类的情况下,
@Autowired
和@Resource
都需要通过名称才能正确匹配到对应的 Bean。Autowired
可以通过@Qualifier
注解来显式指定名称,@Resource
可以通过name
属性来显式指定名称。 @Autowired
支持在构造函数、方法、字段和参数上使用。@Resource
主要用于字段和方法上的注入,不支持在构造函数或参数上使用。
注入Bean的方式
- 属性注入:
@Autowired
注解直接注入字段(最常用,但易忽视依赖可见性)。 - Setter注入:通过Setter方法赋值(灵活性高,支持可选依赖)。
- 构造器注入:通过构造函数赋值(推荐,保证依赖不可变且避免空指针,lombok中使用
@RequiredArgsConstructor
注解实现,属性添加final声明)
Bean的作用域
- singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
- prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续
getBean()
两次,得到的是不同的 Bean 实例。 - request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
- session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
- application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
- websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
Bean是线程安全的吗
Spring Bean 的线程安全性取决于其作用域(Scope)和内部状态设计
单例 Bean(Singleton,默认作用域)
- 非线程安全:所有线程共享同一实例,若包含可变成员变量(如计数器、缓存对象),多线程并发修改会导致数据竞争
@Service
public class CounterService {private int count = 0; // 共享状态,线程不安全public void increment() { count++; }
}
- 线程安全场景:无状态 Bean(无成员变量或仅含不可变对象)
// 定义了一个用户服务,它仅包含业务逻辑而不保存任何状态。
@Component
public class UserService {public User findUserById(Long id) {//...}//...
}
原型 Bean(Prototype)
- 通常线程安全:每次请求创建新实例,普通成员变量线程隔离
- 例外情况:若包含静态变量,仍会共享导致不安全
解决线程安全问题的方案
- 避免可变成员变量: 尽量设计 Bean 为无状态。
- 使用
ThreadLocal
: 将可变成员变量保存在ThreadLocal
中,确保线程独立。 - 使用同步机制: 利用
synchronized
或ReentrantLock
来进行同步控制,确保线程安全
Bean的生命周期
Spring Bean的生命周期4个阶段
- 通过反射实例化对象
- 通过PopulationBean方法进行属性赋值
- 初始化
- 销毁
1. 创建 Bean 的实例:Bean 容器首先会找到配置文件中的 Bean 定义(XML/注解/Java Config),然后使用 Java 反射 API来创建Bean 的实例。
2. Bean 属性赋值/填充:通过Setter、字段注入(@Autowired
)或构造器注入填充属性,解析@Value
注入配置值。
3. Bean 初始化:
- Aware接口回调:注入容器基础设施信息。
BeanNameAware
→BeanFactoryAware
→ApplicationContextAware
(顺序固定)。
- 初始化方法执行(按顺序调用):
-
@PostConstruct
(JSR-250注解) -
InitializingBean.afterPropertiesSet()
- 自定义
init-method
(XML或@Bean(initMethod)
)。
-
- BeanPostProcessor增强:
- 前置处理(
postProcessBeforeInitialization
):如@PostConstruct
的解析。 - 后置处理(
postProcessAfterInitialization
):如AOP代理对象的生成(AbstractAutoProxyCreator
)。
- 前置处理(
4.销毁 Bean:销毁并不是说要立马把 Bean 给销毁掉,而是把 Bean 的销毁方法先记录下来,将来需要销毁 Bean 或者销毁容器的时候,就调用这些方法去释放 Bean 所持有的资源。
- 如果 Bean 实现了
DisposableBean
接口,执行destroy()
方法。 - 如果 Bean 在配置文件中的定义包含
destroy-method
属性,执行指定的 Bean 销毁方法。或者,也可以直接通过@PreDestroy
注解标记 Bean 销毁之前执行的方法。
Spring如何解决循环依赖
spring靠三级缓存解决循环依赖问题。
Bean的生命周期是:实例化 -> 属性赋值 -> 初始化 -> 销毁。循环依赖发生在属性赋值阶段。
一级缓存:
Map<String, Object> singletonObjects
,成品对象单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例。二级缓存:
Map<String, Object> earlySingletonObjects
,早期曝光对象,用于保存实例化完成的 bean 实例。三级缓存:
Map<String, ObjectFactory<?>> singletonFactories
,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。
解决循环依赖过程
1. 创建 Bean A
- 实例化 A(调用构造器) → 将 A 的
ObjectFactory
存入三级缓存(singletonFactories
)。
2. 填充 A 的属性时发现依赖 B
- 触发创建 Bean B → 实例化 B → 将 B 的
ObjectFactory
存入三级缓存。
3. 填充 B 的属性时发现依赖 A
- 从一级缓存查找 A(未找到)→ 从二级缓存查找(未找到)→ 从三级缓存获取 A 的
ObjectFactory
→ 生成 A 的早期引用 → 将 A 的引用存入二级缓存(earlySingletonObjects
)→ 注入 B。
4. 完成 B 的初始化
- B 完成属性填充和初始化 → 将 B 放入一级缓存(
singletonObjects
)→ 删除二/三级缓存中的 B。
5. 完成 A 的初始化
- A 获取完整的 B 引用 → A 完成属性填充和初始化 → 将 A 放入一级缓存 → 删除二/三级缓存中的 A。
只用两级缓存够吗? 在没有 AOP 的情况下,确实可以只使用一级和二级缓存来解决循环依赖问题。但是,当涉及到 AOP 时,三级缓存就显得非常重要了,因为它确保了即使在 Bean 的创建过程中有多次对早期引用的请求,也始终只返回同一个代理对象,从而避免了同一个 Bean 有多个代理对象的问题。(如果没有三级缓存,那么二级缓存存的是)
在 Spring 的三级缓存机制中,缓存存储的对象类型与代理层级无关。以下以 Proxy3 → Proxy2 → Proxy1 → 原始对象
的多级代理链为例,结合三级缓存的作用分析存储内容:
- 三级缓存:只会存储原始对象的
ObjectFactory
工厂(与代理层级无关) - 二级缓存:首次从三级缓存生成的早期引用(即
Proxy1
),而非每个层级的代理对象- 存储的是 半成品 Bean(可能为原始对象或单层代理),而非完整代理链。
- 对于多级代理链,二级缓存仅存
Proxy1
,后续代理层级(如Proxy2
/Proxy3
)尚未生成
一级缓存:完整代理链的顶层代理对象(即
Proxy3
),而非所有代理层级或原始对象- Bean 初始化完成后(
@PostConstruct
执行完毕),通过BeanPostProcessor
生成剩余代理层级,最终形成Proxy3
并存入一级缓存。
- Bean 初始化完成后(
FactoryBean和BeanFactory区别
BeanFactory
Spring 的 IoC 容器根接口,管理所有 Bean 的生命周期(创建、依赖注入、销毁)
FactoryBean
一个特殊的 Bean 接口,本身是一个 Bean,但生产另一个对象,用于封装复杂对象的创建逻辑,例如:
- MyBatis 的
SqlSessionFactoryBean实现了
FactoryBean<SqlSessionFactory> - Spring AOP 的
ProxyFactoryBean
Spring AOP
Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。
Aop通知顺序
正常流程:
@Around开始 → @Before → 目标方法 → @AfterReturning → @After → @Around结束
异常流程:
@Around始 → @Before → 目标方法 → @AfterThrowing → @After → @Around终
Spring 版本差异:
- Spring 4:
@After
在@AfterReturning
之前执行(现已过时)。 - Spring 5.2.7+:调整为
@AfterReturning → @After
(正常流程)或@AfterThrowing → @After
(异常流程)
Aop通知类型
- Before(前置通知):目标对象的方法调用之前触发
- After (后置通知):目标对象的方法调用之后触发
- AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发
- AfterThrowing(异常通知):目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
- Around (环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法
Spring MVC工作流程
核心组件
DispatcherServlet
:核心的中央处理器,负责接收请求、分发,并给予客户端响应。HandlerMapping
:处理器映射器,根据 URL 去匹配查找能处理的Handler
,并会将请求涉及到的拦截器和Handler
一起封装。HandlerAdapter
:处理器适配器,根据HandlerMapping
找到的Handler
,适配执行对应的Handler
;Handler
:请求处理器,处理实际请求的处理器。ViewResolver
:视图解析器,根据Handler
返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给DispatcherServlet
响应客户端
视图阶段(JSP)流程
前后端分离阶段流程
Spring事务
Spring支持两种事务管理方式:
- 编程式事务:在代码中硬编码(在分布式系统中推荐使用) : 通过
TransactionTemplate
或者TransactionManager
手动管理事务,事务范围过大会出现事务未提交导致超时,因此事务要比锁的粒度更小。 - 声明式事务:在 XML 配置文件中配置或者直接基于注解(单体应用或者简单业务系统推荐使用) : 实际是通过 AOP 实现(基于
@Transactional
的全注解方式使用最多)
事务的传播行为
REQUIRED(默认)
- 特点:若当前存在事务则加入,否则创建新事务。
- 场景:常规业务方法(如订单创建),保证多个操作原子性。
- 注意:嵌套方法异常会导致整个事务回滚。
SUPPORTS
- 特点:当前有事务则加入,没有则以非事务方式执行。
- 场景:查询操作(如数据查询),可兼容事务但无需强制。
MANDATORY
- 特点:强制要求当前必须存在事务,否则抛出异常。
- 场景:核心业务(如支付验证),确保方法在事务中执行。
REQUIRES_NEW
- 特点:无论当前有无事务,都创建独立新事务;若存在事务则挂起。
- 场景:日志记录、审计等需独立提交的操作。
- 注意:新事务与父事务完全隔离,互不影响提交/回滚。
NOT_SUPPORTED
- 特点:强制非事务执行,若当前存在事务则挂起。
- 场景:耗时操作(如大数据统计),避免事务开销。
NEVER
- 特点:必须在非事务环境中执行,否则抛出异常。
- 场景:严格非事务场景(如只读缓存更新)。
NESTED
- 特点:
- 当前有事务时,创建嵌套子事务(基于保存点机制)。
- 子事务回滚不影响父事务,父事务回滚则子事务必然回滚。
- 场景:部分操作需独立回滚(如订单创建成功但库存扣减失败)。
- 依赖:需数据库支持保存点(如MySQL的InnoDB引擎)。
事务的隔离级别
TransactionDefinition.ISOLATION_DEFAULT
: 使用后端数据库默认的隔离级别,MySQL 默认采用的REPEATABLE_READ
隔离级别 Oracle 默认采用的READ_COMMITTED
隔离级别.TransactionDefinition.ISOLATION_READ_UNCOMMITTED
: 最低的隔离级别,使用这个隔离级别很少,因为它允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读TransactionDefinition.ISOLATION_READ_COMMITTED
: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生TransactionDefinition.ISOLATION_REPEATABLE_READ
: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。TransactionDefinition.ISOLATION_SERIALIZABLE
: 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事务失效的原因
1. 非public方法:Spring AOP默认只代理public方法,@Transactional
标注在private/protected方法上无效。
2. 自调用问题(同类内部调用):同一个类中,非事务方法A直接调用事务方法B时,不经过Spring代理(AOP基于动态代理),事务失效。
解决方法:
- 注入自身代理对象:
@Autowired private UserService selfProxy;
调用selfProxy.methodB()
。 - 使用AOP工具:
((UserService)AopContext.currentProxy()).methodB()
(需开启@EnableAspectJAutoProxy(exposeProxy=true)
)。 - 拆分方法到不同类中。
3. 异常类型不匹配:默认仅对RuntimeException
和Error
回滚。需指定回滚异常:@Transactional(rollbackFor = Exception.class)
4.
异常被捕获未抛出:try-catch捕获异常后未重新抛出,事务拦截器无法触发回滚。
5.数据库底层需要支持事务
Spring中的设计模式
一、创建型模式
单例模式(Singleton):Spring容器默认以单例模式管理Bean(如
@Service
、@Component
),确保每个Bean定义仅有一个实例,节省资源并提高性能。工厂模式(Factory):
BeanFactory
和ApplicationContext
作为核心工厂接口,负责Bean的创建、配置和管理,解耦对象实例化过程。原型模式(Prototype):通过
@Scope("prototype")
声明原型作用域的Bean,每次请求时创建新实例,适用于需要状态隔离的场景。
二、结构型模式
代理模式(Proxy):Spring AOP的核心实现,通过JDK动态代理(接口)或CGLIB(类)生成代理对象,实现日志、事务等横切逻辑。
适配器模式(Adapter):
Spring MVC:
HandlerAdapter
适配多种控制器(如@Controller
、HttpRequestHandler
)。AOP:适配不同通知类型(如
MethodBeforeAdviceAdapter
)。
装饰器模式(Decorator):
BeanPostProcessor
在Bean初始化前后动态增强功能(如缓存、监控),不修改原始类代码。门面模式(Facade):
JdbcTemplate
封装复杂数据库操作(连接管理、异常处理),提供简洁API。
三、行为型模式
模板方法模式(Template Method):
JdbcTemplate
、RestTemplate
等定义算法骨架(如事务流程),子类仅需实现特定步骤(如SQL执行)。观察者模式(Observer):事件驱动模型(
ApplicationEventPublisher
发布事件,ApplicationListener
监听响应),实现组件间松耦合通信。策略模式(Strategy):
视图解析:
ViewResolver
根据请求类型(JSP、Thymeleaf)选择不同实现。事务管理:支持JDBC、JTA等多种事务策略。
责任链模式(Chain of Responsibility):Spring Security的过滤器链(
FilterChain
)和Spring MVC的拦截器(HandlerInterceptor
),按顺序处理请求。
Spring Boot
Spring Boot 约定大约配置怎么理解
约定的内容及作用
约定项目结构和命名规范:
- Spring Boot鼓励开发者使用一致的项目结构,避免手动配置Spring组件扫描路径。例如,
@SpringBootApplication
注解默认会扫描同级包及子包中的组件。 - 配置文件只需用
application.yml
格式即可自动加载;使用application-环境.yml
时,它会根据环境判断是否加载,省去了XML配置。
- Spring Boot鼓励开发者使用一致的项目结构,避免手动配置Spring组件扫描路径。例如,
自动配置:
- 根据项目的依赖、类路径等条件,Spring Boot能自动配置各种组件。如默认使用SLF4J并集成logback作为日志实现,若需其他日志框架则需排除此包并引入新包。
- 引入
spring-boot-starter-web
包后,程序会自动识别为servlet项目并创建servlet容器;引入数据库依赖时,会自动配置数据源、事务管理器等组件。自动装配是这一思想的体现。
Starter:
starter
约定了spring.factories
配置中需要注册的类,使我们快速整合组件到Spring中。Spring Boot提供了许多现成的starter
,只需引入相应依赖即可生效。- 许多组件还附带了配置类(如
MultipartProperties
),其中包含默认值(如最大上传文件大小为1M),仅在需要修改时才需配置。
Spring Boot自动装配原理
核心机制,三个注解组成
大概可以把 @SpringBootApplication
看作是 @Configuration
、@EnableAutoConfiguration
、@ComponentScan
注解的集合。根据 SpringBoot 官网,这三个注解的作用分别是:
@EnableAutoConfiguration
:启用 SpringBoot 的自动配置机制@Configuration
:允许在上下文中注册额外的 bean 或导入其他配置类@ComponentScan
:扫描被@Component
(@Service
,@Controller
)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。
@EnableAutoConfiguration
是实现自动装配的重要注解
自动装配流程
1.加载自动配置类
@EnableAutoConfiguration
通过@Import
导入AutoConfigurationImportSelector
类。- 该类调用
SpringFactoriesLoader.loadFactoryNames()
,扫描所有META-INF/spring.factories
文件,加载其中声明的自动配置类(如org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
)
2.条件化筛选配置类
自动配置类使用条件注解动态决定是否生效,例如:
-
@ConditionalOnClass
:类路径存在指定类时生效(如存在DataSource
才加载数据库配置)。 -
@ConditionalOnMissingBean
:容器中无指定Bean时生效(避免重复覆盖用户自定义Bean)。 -
@ConditionalOnProperty
:配置文件属性匹配时生效(如spring.datasource.url
存在)
3.注册Bean与属性绑定
1.通过@Configuration
类中的@Bean
方法向容器注册Bean
2.属性绑定:配置类结合@EnableConfigurationProperties
加载XxxProperties
类,将application.properties
中的属性(如server.port
)注入到Bean中
如何实现一个Starter
创建 Maven 项目,例如
配置
pom.xml,引入Spring Boot
相关依赖
定义配置属性类
实现核心服务类
创建自动配置类,使用@Conditional等条件注解控制 Bean 加载
注册自动配置,在项目的resource目录下添加 META-INF/spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.config.DemoAutoConfiguration
如何实现全局异常管理
Spring Boot 应用程序可以借助 @RestcontrollerAdvice和 @ExceptionHandler 实现全局统一异
常处理。