1. 什么是循环依赖

两个及以上的类之间相互依赖。模块A依赖于模块B,模块B依赖于模块A,导致依赖链的循环,无法确定加载/初始化顺序。

=> 多个Bean循环引用导致Spring容器无法正常初始化它们。

延迟某些Bean的初始化时间,使用@Lazy进行懒加载,只有当实际使用了该对象才创建。

2. Spring如何解决循环依赖

提前暴露未完全创建的Bean

三级缓存解决:

  • 一级缓存(Single Objects Map):用于初始化单例Bean; (成品)
  • 二级缓存(Early Singleton Objects Map): 用于存储尚未完全初始化,但实例化的Bean,用于提取暴露对象,避免循环依赖问题;(半成品,成员变量未初始化)
  • 三级缓存(Singleton Factories Map): 用于存储对象工厂,可以通过工厂创建早期Bean

**解决步骤:**例如AB两个相互依赖,三级缓存策略

  • 创建A,查询一级缓存看看有没有完全体B,没有则看看二级缓存有没有半成品B,都没有则创建A的Bean,调用ceateBean方法(实例化,属性注入,初始化);
  • 之后,A往三级缓存加入一个A的getObject方法
  • 到了属性注入,因为A依赖B,那么需要创建B。同样的路线,B查询到二级缓存都没发现A,调用createBean创建B实例。到了B的属性注入,发现三级缓存有A工厂,调用getObject创建半成品A,放到二级缓存中,完成B的第二步属性注入。后面initializeBean完成B的创建,并放到一级缓存中。
  • 回到A,A调用一级缓存的B完成注入。

未解决的问题:

而如果说 A 是构造器注入,B 是 set 注入。则说明 A 需要 B 的时刻提前了,在实例化 new A(B b)的时候就需要 B。此时 A 没有往三级缓存放getObject,因此到了创建依赖 B的时候,无法获取 A的 getObject 工厂方法,只能继续 new A,造成循环依赖的死循环。

4. Spring重要的模块组成

Core Container 核心容器:

  • Spring Core:提供依赖注入DI和控制反转IOC的实现。
  • Spring Beans:负责管理Bean的定义和生命周期,通过IOC完成Bean的创建、依赖注入、初始化、销毁等操作;
  • Spring Context:基于Core和Beans的高级容器,提供类似JNDI的上下文功能,包含国际化、事件传播、资源访问等功能;
  • Spring Expression Language:用于运行时查询和操作对象的值。

AOP面向切面编程

  • Spring AOP:提供面向切面编程的功能,可以在方法执行前后或抛出异常时动态插入额外的业务逻辑。

Data Access 数据访问

  • Spring JDBC:操作数据库
  • Spring ORM:支持注意ORM框架,简化持久层开发
  • Spring Transaction事务管理:提供声明式事务和编程式的事务管理机制

Web层

  • Spring Web:提供基础Web开发支持,包括Servlet Api的集成

  • Spring MVC:实现了Model-View——Controller(MVC)模式的框架,用于构建HTTP请求的Web应用;

image-20250415185859345

5. Spring IOC

控制反转,通过依赖注入让容器负责管理对象的创建和管理

  • 核心:对象的创建和依赖关系交由IOC容器进行负责。
  • 依赖注入:构造器注入,setter注入或接口注入。

控制:控制对象的创建过程

反转:创建对象的主题变为Spring

6. Spring Bean

Spring应用中的对象

生命周期

  • 实例化
  • 依赖注入:将依赖的其他对象注入进来
  • 初始化:如果Bean实现了Initializing接口或者@PostConstruct,Spring依赖注入后会调用该初始化方法;
  • 销毁

创建方式:

  • XML配置
  • 基于注解:@Component @Service @Repository @Controller等
  • Java配置类:通过结合@Configuration和@Bean实现

7. Spring注入方式

  • 构造器注入
  • setter注入
  • 字段注入 @Autowired
  • 方法注入 @Autowired
  • 接口回调注入:注入实现类

8. AOP面向切面

  • 核心:将与业务逻辑无关的横切关注点抽取出来,通过声明式动态的应用到业务方法中,而不是嵌入业务逻辑中。

  • 关键概念:切面(Aspect)、连接点(Joint Point)、通知(advice)、切入点(PointCut)和织入(Weaving)

1@Aspect
2@Component
3public class LoggingAspect {
4
5    @Before("execution(* UserService.createUser(..))")
6    public void logBefore() {
7        System.out.println("准备创建用户...");
8    }
9}

9. Spring拦截链的实现

责任链模式,指一系列拦截器依次生效

  • HandlerInterceptor(MVC拦截器):用于拦截HTTP请求
  • Filter(过滤器):基于Servlet API的过滤器
  • AOP拦截链(切面):实现方法的前后处理。@Before @After @Around

💡在Spring AOP中,@before和@after注解都对应一个Interceptor拦截器,

image-20250415205311425

责任链模式

10. Spring MVC知识点

基于经典的MVC模式 (Model-View-Controller)来开发Web应用的模块。

基于Servlet API构建,核心是DispatcherServlet, 即请求分发器,将HTTP请求映射到控制器方法中,并将数据返回给视图层进行渲染

Model:业务模型和数据模型 分别是Servie、Repository;

Controller:分别是DispatcherServlet和Controller;(分发&处理)

主要功能: 请求映射、数据绑定、视图解析、表单处理、异常处理等。

工作流程:

  • HTTP请求 => DispatcherServlet 根据URL进行分发
    • HandlerMapping:缓存URL到具体处理器对象的映射关系
    • HandlerAdapter:DispatcherServlet通过该适配器间接调用Handler
  • URL => Controller 接收请求并进行业务处理
  • ModelAndView:数据封装到模型对象中。
  • ViewResolver:负责呈现数据,前后端分离,不需要这个。

11. Spring MVC的具体工作原理

image-20250415210418513

  • DispatcherServlet接收请求

  • 请求映射:根据请求URL查询HandlerMapping,返回Handler执行链(拦截器和处理器)

  • 依次执行拦截器preHandle()xN=>Controller,若中断则直接转到afterComplete()

  • 获取HandlerAdapter:通过HandlerAdapter执行handle方法,解决不同处理器的差异

  • 拦截器postHandle()

  • AfterCompletion()

  • 返回响应数据

12. Spring中的设计模式

工厂模式:BeanFactory

模板方法:RestTemplate、JDBCTemplate,RedisStringTemplate

代理模式:AOP

单例模式:IOC下的Bean

责任链模式:SpringMVC拦截器

观察者模式:Spring中的监听器

适配器模式:handlerAdapter

13 Spring中的事务传播行为

父事务与子事务之间的关系?一个事务被另一个事务调用

作用:定义和管理事务边界,定义多个事务方法嵌套时,是否开启新事务、复用事务还是挂起事务。。。

  • Propagation_Required 必须传播
    • 最常用的事务传播行为,如果存在事务,执行的方法会加入到该事务中,若不存在,则会创建一个新事务;
    • 例如下单场景,扣减库存、生产订单记录、更新用户积分等多个操作,应该在一个事务中,以保证数据的一致性
  • Propagation_SUPPORTS 支持的传播行为
    • 当前存在事务则加入,否则非事务方式执行
    • 例如电商场景下,用户查看商品评论列表,这是正常操作。但是在一个事务中调用该方法(进行商品推广活动统计时,同时查看商品评论列表),则可以加入该事务。
  • Propagation_MANDATORY 强制的传播行为
    • 必须要求存在当前事务,否则抛出异常
    • 场景:促销活动中,进行优惠计算并更新订单金额和扣减库存时,必须在同一个事务中。因为这两是紧密关联的。
  • Propagation_REQUIRES_NEW 需要新事务的传播行为
    • 无论是否存在事务,调用的子方法都会创建一个新的事务。
    • 独立小团体
    • 例子:业务操作和日志操作,这俩相对独立的操作,即使业务失败,日志也应该成功
  • Propagation_NOT_SUPPORTED 不支持事务的传播行为
    • 子方法以非事务的方式执行,若存在事务,先挂起
    • 场景:xxx
  • Propagation_NERVER 不允许事务的传播
    • 要求当前绝对不能存在事务,否则抛出异常
    • 例如: 计算商品的推荐指数时,不涉及事务操作
  • Propagation_NESTED 嵌套传播行为
    • 若当前存在事务,方法将在嵌套事务中执行,否则创建新事务
    • 例子:多个步骤,检查库存,生成订单,发送通知等。生成订单时异常,仅回滚这个子事务,不影响前面的检查库存操作。

14. Spring IOC容器初始化过程

四阶段

  • 启动阶段
    • 配置加载:加载配置文件or配置类,XML配置文件、JAVA配置类或配置注解
    • 创建容器:Spring创建IOC容器(BeanFactory\ApplicationContext), 加载和管理Bean
  • Bean定义和注册阶段
    • 解析Bean定义,注册到容器中
  • 实例化和依赖注入
    • 实例化:根据BeanDefinition创建Bean实例
    • 依赖注入:根据构造函数、Setter方法、字段注入,将依赖注入到Bean中
  • 初始化
    • BeanPostProcessor:处理器会在Bean初始化完成后执行
    • @Post Construct Bean的初始化方法

image-20250415213456343

15 Qualifier注解

例如:当多个接口有多个实现时,指定哪个具体的Bean,消除歧义

 1@Component
 2public class Client {
 3
 4    private final Service service;
 5
 6    @Autowired
 7    public Client(@Qualifier("serviceImpl1") Service service) {
 8        this.service = service;
 9    }
10
11    public void doSomething() {
12        service.serve();
13    }
14}

@Primary 多个Bean实现了继承了某个类,依赖注入优先为带有@Primary的类

 1@Component
 2@Primary
 3public class DefaultService implements Service {
 4    public void serve() {
 5        System.out.println("Default Service");
 6    }
 7}
 8
 9@Component
10@Qualifier("specificService")
11public class SpecificService implements Service {
12    public void serve() {
13        System.out.println("Specific Service");
14    }
15}
16
17@Component
18public class Client {
19
20    private final Service service;
21
22    @Autowired
23    public Client(@Qualifier("specificService") Service service) {
24        this.service = service;
25    }
26
27    public void doSomething() {
28        service.serve();
29    }
30}

16 事务的失效场景

使用@Transactional声明式事务时,存在以下事务失效情况

  • ⭐rollbackFor:没设置对,默认为(RuntimeException or Error回滚才生效),而自定义异常和IOException等并不会回滚

    1@Transactional(rollbackFor = Exception.class)
    
  • ⭐异常被捕获了,异常被catch了,仅打印log,并没有将异常往回抛出。

  • ⭐同一个类中方法调用,因为事务是基于动态代理实现的,自己调用自己的方法不会走代理方法

    • 只有通过代理对象调用的方法才会被事务拦截器增强
    • 当类内部方法A调用方法B时,实际上是this.methodB()调用,而不是proxy.methodB()
    1((XXXService)AopContext.currentProxy())
    2            .applyMethod(xxx);
    
  • @Transactional应用在非public修饰上,Spring事务判断非公开方法不执行事务

  • ⭐事务传播设置不当,设置为Requires_new,会创建子事务,两个无关

  • 多线程环境,@Transactional基于ThreadLocal,因此多线程不能保持事务同步。

17. Spring 启动过程

  • 创建<容器>
    • 根据配置文件中的信息创建容器 ApplicationContext,容器启动阶段实例化BeanFactory,并加载容器总的BeanDefinitions
  • 加载<配置>
    • 读取XML配置文件,JAVA Config类,和基于注解的Bean,包括数据库连接配置、AOP配置等等。
  • 注册Bean<定义>
    • 解析配置文件中定义的BeanDefinition和声明的Bean元数据
  • **<实例化>**Bean:实例化Bean对象,并放入容器中进行管理
  • <注入依赖>
    • 注入Bean对象中的依赖对象,构造器,setter,字段注入
  • <初始化>Bean
    • InitializingBean,or afterPropertiesSet ? 这是什么呢 & BeanPostProcessors

18. @Value注解

注入外部资源的

  • 配置文件注入

例子:

1app.name=MyApp
2app.version=1.0.0
 1import org.springframework.beans.factory.annotation.Value;
 2import org.springframework.stereotype.Component;
 3
 4@Component
 5public class AppConfig {
 6
 7    @Value("${app.name}")
 8    private String appName;
 9
10    @Value("${app.version}")
11    private String appVersion;
12
13    public String getAppName() {
14        return appName;
15    }
16
17    public String getAppVersion() {
18        return appVersion;
19    }
20}    

19 常用的注解

  • @PostConstruct:Bean初始化完成后调用

    • 提前加载数据
  • @PreDestory: Bean销毁前调用

    • 关闭连接,释放资源
  • @Scheduled 定时任务

  • @Conditional 有条件的装配Bean对象

    1@Conditional(OnLinuxCondition.class)  // 仅这个类存在时才装配这个Bean到容器中
    2@Bean
    3public MyService myService() {
    4    return new MyService();
    5}
    
  • @ControllerAdvice 全局异常处理

    1@ControllerAdvice
    2public class GlobalExceptionHandler {
    3
    4    @ExceptionHandler(Exception.class)
    5    public ResponseEntity<String> handleAllExceptions(Exception ex) {
    6        return new ResponseEntity<>("An error occurred: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    7    }
    8}