关于Spring的八股

发布于:2025-09-06 ⋅ 阅读:(12) ⋅ 点赞:(0)

目录

SpringBean的初始化过程

Bean的五种作用域

Bean的五种自动装配(通过配置文件)

Spring里的循环依赖问题

循环依赖的三种场景:

Spring解决单例Bean循环依赖的核心原理:

解决流程实例:(a依赖b,b依赖a,均为单例+setter注入)

无法解决的循环依赖场景及原因:

如何解决/避免循环依赖:

SpringBean的初始化过程

SpringBean的初始化过程是Spring容器创建和管理Bean的核心流程,涉及多个阶段和扩展点。以下是完整过程:

1.容器启动阶段:

(1)加载配置:Spring 容器(如ApplicationContext)加载配置元数据(XML、注解、Java 配置类等),解析出 Bean 的定义信息(BeanDefinition),包括类名、作用域、依赖关系、初始化方法等。

(2)注册 Bean 定义:将解析出的BeanDefinition注册到容器的BeanDefinitionRegistry中,此时还未实例化 Bean。

2.Bean实例化:(当容器首次请求Bean(如调用getBean())或触发懒加载时,开始实例化)

(1)选择构造器:根据 Bean 定义,容器选择合适的构造器(默认无参构造器,或通过@Autowired/XML 配置指定的构造器)。

(2)创建实例:通过反射调用构造器,生成 Bean 的原始对象(尚未设置属性)。

3.属性注入:

(1)依赖解析:根据 Bean 定义中的依赖关系(如@Autowired、@Resource或 XML 的<property>),容器查找并获取依赖的 Bean。

(2)注入依赖:将依赖的 Bean 通过 setter 方法、字段注入或构造器参数注入到当前 Bean 中。若使用自动装配(如byName/byType),容器会按规则自动匹配依赖。

4.初始化前:(先执行前置处理)

(1)Aware 接口回调:若 Bean 实现了Aware系列接口(如BeanNameAware、BeanFactoryAware、ApplicationContextAware),容器会调用对应的方法注入相关信息:

BeanNameAware:注入当前 Bean 的 id/name;

BeanFactoryAware:注入当前 BeanFactory 容器;

ApplicationContextAware:注入当前 ApplicationContext 容器。

(2)BeanPostProcessor 前置处理:容器调用所有BeanPostProcessor的postProcessBeforeInitialization方法,可对 Bean 进行修改(如 AOP 代理前置处理)。

5.初始化:(执行自定义初始化逻辑)

(1)PostConstruct注解方法:若 Bean 的方法标注了@PostConstruct,容器会在此时调用(JSR-250 规范)。

(2)初始化方法:

若 XML 配置中指定了init-method属性(如<bean init-method="init"/>);

或 Bean 实现了InitializingBean接口,调用其afterPropertiesSet()方法;

容器会执行这些方法(afterPropertiesSet优先级高于init-method)。

6.初始化后:

BeanPostProcessor 后置处理:容器调用所有BeanPostProcessor的postProcessAfterInitialization方法,此处是 AOP 动态代理的关键节点(如生成代理对象替换原始 Bean)。

7.Bean就绪:

经过上述步骤后,Bean 已完全初始化,放入容器的缓存中,供后续使用(单例 Bean 会一直存在于容器中,原型 Bean 则在每次请求时重新创建)。

8.销毁阶段:(当容器关闭时(如ApplicationContext.close()),单例 Bean 会执行销毁逻辑)

(1)@PreDestroy注解方法:调用标注@PreDestroy的方法(JSR-250 规范)。

(2)销毁方法:

若 XML 配置中指定了destroy-method属性;或 Bean 实现了DisposableBean接口,调用其destroy()方法;容器会执行这些销毁方法(destroy优先级高于destroy-method)。

Bean的五种作用域

正常spring框架支持两个:

singleton(默认);

prototype

在web项目中,会增加后三个:

singleton:使用该属性定义Bean时,IOC容器仅创建一个Bean实例,IOC容器每次返回的是同一个Bean实例。

prototype:使用该属性定义Bean时,IOC容器可以创建多个Bean实例,每次返回的都是一个新的实例。

request:该属性仅对HTTP请求产生作用,使用该属性定义Bean时,每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境。

session:该属性仅用于HTTP Session,同一个Session共享一个Bean实例。不同Session使用不同的实例。

global-session:该属性仅用于HTTP Session,同session作用域不同的是,所有的Session共享一个Bean实例。

Bean的五种自动装配(通过配置文件)

自动装配的核心配置:autowire属性

1.byName(按属性名匹配)

原理:Spring容器会查找目标bean的属性名与容器中其它bean的id值完全一致的依赖,找到后自动注入。目标bean的属性必须提供setter方法(因为注入过程本质是调用setter方法赋值)

定义Java类&XML配置文件

2.byType(按属性类型匹配)

原理:spring容器会查找目标bean的属性类型与容器中其他的bean的class类型(或子类,实现类)一致的依赖,找到后自动注入。目标bean的属性必须提供setter方法;容器中同类型的bean只能有一个。

3.Constructor(按构造器参数类型/名称匹配)

原理:spring容器会查找与目标bean构造器参数类型(或参数名,优先级低于类型)匹配的bean,自动注入到构造器中,完成bean的初始化。目标bean必须提供带参数的构造器(无参构造器可选,但推荐保留,避免spring初始化异常)

修改UserService&XML配置文件

4.Default(继承父容器的自动装配原则)

原理:default不是独立的装配模式,而是“继承上级配置”的规则:

若在<beans>标签中通过default-autowire属性设置了全局自动装配模式(如default-autowire="byType"),则autowire="default"的 bean 会继承该全局模式;

若<beans>标签未设置default-autowire,则default等价于no(不自动装配)。

配置全局自动装配模式

5.No(不自动装配,默认值)

原理:autowire="no"表示关闭自动装配,所有依赖必须通过配置文件显式指定(如<property>标签),是 Spring 的默认行为。

XML配置文件

Spring里的循环依赖问题

循环依赖的三种场景:

1.构造器注入循环依赖:两个 Bean 通过构造器相互依赖(如 A 的构造器需要 B,B 的构造器需要 A)。Spring 无法解决,会直接抛出异常。

2.Setter注入(或字段注入)的单例Bean循环依赖:两个单例 Bean 通过 setter 方法或字段注入相互依赖。Spring 可以解决,是最常见的可处理场景。

3.原型Bean循环依赖:原型(prototype)作用域的 Bean 之间的循环依赖(无论注入方式)。Spring 无法解决,会抛出异常。

Spring解决单例Bean循环依赖的核心原理:

Spring通过三级缓存机制解决单例Bean的循环依赖,核心思想:提前暴露未初始化完成的Bean,让依赖方可以引用。

三级缓存的定义:

Spring 在DefaultSingletonBeanRegistry中维护了三个缓存(Map):

一级缓存(singletonObjects):存储完全初始化完成的单例 Bean(最终可用的 Bean)。

二级缓存(earlySingletonObjects):存储提前暴露的未完全初始化的单例 Bean(仅实例化但未注入属性和执行初始化方法)。

三级缓存(singletonFactories):存储Bean 工厂对象(ObjectFactory),用于在需要时创建 Bean 的早期引用(可能是原始对象或代理对象)。

解决流程实例:(a依赖b,b依赖a,均为单例+setter注入)

1.初始化 A:

实例化 A(调用构造器创建原始对象)。

将 A 的工厂对象(ObjectFactory)放入三级缓存(singletonFactories),此时 A 尚未注入属性。

2.A 需要注入 B:

容器检查一级缓存,发现 B 不存在,开始初始化 B。

3.初始化 B:

实例化 B(调用构造器创建原始对象)。

将 B 的工厂对象放入三级缓存。

B 需要注入 A:容器检查一级缓存(A 不存在)→ 检查二级缓存(A 不存在)→ 从三级缓存获取 A 的工厂对象,生成 A 的早期引用,放入二级缓存(earlySingletonObjects),并删除三级缓存中的 A 工厂。

B 注入 A 的早期引用,完成属性注入和初始化,放入一级缓存(singletonObjects)。

4.A 完成初始化:

容器从一级缓存获取 B,注入 A 中。

A 完成初始化,放入一级缓存,删除二级缓存中的 A。

无法解决的循环依赖场景及原因:

1.构造器注入循环依赖:构造器注入要求依赖在 Bean 实例化阶段(调用构造器时)就必须存在,而此时依赖的 Bean 可能尚未实例化,无法提前暴露,导致循环等待。

2.原型Bean循环依赖:原型 Bean 每次请求都会重新创建,且 Spring 不缓存原型 Bean,无法提前暴露未初始化的实例,导致无限循环创建。

如何解决/避免循环依赖:

1.优先使用setter注入/字段注入,避免构造器注入的循环依赖;

2.重构代码消除循环依赖,从设计层面解耦,将共同依赖的逻辑抽取到第三方组件中,打破闭环;

3.使用@lazy注解(针对构造器注入),延迟依赖Bean的初始化(本质是创建代理对象暂时替代);

4.使用@DependsOn指定初始化顺序(适用于非直接依赖的场景)。


网站公告

今日签到

点亮在社区的每一天
去签到