Spring面试题
# 为什么要使用spring框架
Spring 是一个轻量级的企业级应用框架,其核心是 IoC(控制反转) 和 AOP(面向切面编程),旨在简化企业级开发,让开发者专注于业务逻辑,而无需手动管理对象依赖或侵入式编码。
从spring本身的特性
- spring是一个轻量级的框架,它的基本版本呢只有两兆,
- spring通过IOC容器管理 Bean 的生命周期,以及通过DI实现松耦合,降低组件间的直接依赖,
- spring提供了AOP的面向切面编程功能,可以把我们的业务逻辑和系统功能之间,进行一个切分,
- MVC的框架,spring mvc呢提供的功能更加强大,且更加灵活的一个web框架的一个支持
- spring基于 AOP 实现统一事务控制,支持编程式 & 声明式事务,简化数据库操作。
- 丰富的生态 & 社区支持,Spring Boot(快速开发)、Spring Cloud(微服务)、Spring Data(数据访问)等
# 1. Spring框架中的单例bean是线程安全的吗?
不是线程安全的。当多用户同时请求一个服务时,容器会给每个请求分配一个线程,这些线程会并发执行业务逻辑。如果处理逻辑中包含对单例状态的修改,比如修改单例的成员属性,就必须考虑线程同步问题。Spring框架本身并不对单例bean进行线程安全封装,线程安全和并发问题需要开发者自行处理。
通常在项目中使用的Spring bean是不可变状态(如Service类和DAO类),因此在某种程度上可以说Spring的单例bean是线程安全的。如果bean有多种状态(如View Model对象),就需要自行保证线程安全。最简单的解决办法是将单例bean的作用域由“singleton”变更为“prototype”。
# 2. 什么是AOP?
AOP,即面向切面编程,在Spring中用于将那些与业务无关但对多个对象产生影响的公共行为和逻辑抽取出来,实现公共模块复用,降低耦合。常见的应用场景包括公共日志保存和事务处理。
# 3. 你们项目中有没有使用到AOP?
我们之前在后台管理系统中使用AOP来记录系统操作日志。主要思路是使用AOP的环绕通知和切点表达式,找到需要记录日志的方法,然后通过环绕通知的参数获取请求方法的参数,例如类信息、方法信息、注解、请求方式等,并将这些参数保存到数据库。
# 4. Spring中的事务是如何实现的?
Spring实现事务的本质是利用AOP完成的。它对方法前后进行拦截,在执行方法前开启事务,在执行完目标方法后根据执行情况提交或回滚事务。
# 5. Spring中事务失效的场景有哪些?
在项目中,我遇到过几种导致事务失效的场景:
- 如果方法内部捕获并处理了异常,没有将异常抛出,会导致事务失效。因此,处理异常后应该确保异常能够被抛出。
- 如果方法抛出非RuntimeException/Error,即检查型异常(checked exception),并且没有在
@Transactional
注解上配置rollbackFor
属性为Exception
,那么异常发生时事务可能不会回滚。 - 如果事务注解的方法不是公开(public)修饰的,也可能导致事务失效。
- 自调用:如果在类内部方法直接调用带@Transaction的方法,会绕过AOP代理,导致事务失效
# 6. Spring的bean的生命周期?
Spring的Bean生命周期主要分为五大步
实例化
- 通过
BeanDefinition
获取bean的定义信息(beanClassName、initMethodName、propertyValues、scope、lazyInit)。 - 通过反射调用构造函数或工厂方法创建Bean对象。
- 通过
属性赋值
进行bean的依赖注入:通过Setter方法、字段注入(如
@Autowired
)或构造器注入完成属性赋值处理循环依赖
Aware接口回调
- 若Bean实现了
Aware
接口(BeanNameAware、BeanFactoryAware、ApplicationContextA ware),会调用相关的Aware注入BeanName、容器等底层资源
- 若Bean实现了
初始化
- BeanPostProcessor前置处理
- 调用初始化生命周期回调(@PostConstruct、InitializingBean#afterPropertiesSet()、init-method)
- BeanPostProcessor后置处理 可能在这里产生代理对象(JDK、CGLIB动态代理)
销毁
- 调用销毁生命周期回调(PreDestroy(常用) 、destroy() 、destroy-method)
# 7. Spring中的循环引用?
循环依赖发生在两个或两个以上的bean互相持有对方,形成闭环。常见形式包括:
- 直接循环依赖:A 依赖 B,B 依赖 A。
- 间接循环依赖:A 依赖 B,B 依赖 C,C 依赖 A。
- 自我依赖:A 依赖 A(较少见)。
Spring框架允许循环依赖存在,并通过三级缓存解决大部分循环依赖问题:
- 一级缓存:单例池,缓存已完成初始化的bean对象。
- 二级缓存:缓存尚未完成生命周期的早期bean对象。
- 三级缓存:缓存
ObjectFactory
,用于创建bean对象。
构造器注入的循环依赖三级缓存无法解决,需使用注解@Lazy
# 8. 那具体解决流程清楚吗?
解决循环依赖的流程如下:
- 实例化A对象,并创建
ObjectFactory
存入三级缓存。 - A在初始化时需要B对象,开始B的创建逻辑。
- B实例化完成,也创建
ObjectFactory
存入三级缓存。 - B需要注入A,通过三级缓存获取
ObjectFactory
生成A对象,存入二级缓存。 - B通过二级缓存获得A对象后,B创建成功,存入一级缓存。
- A对象初始化时,由于B已创建完成,可以直接注入B,A创建成功存入一级缓存。
- 清除二级缓存中的临时对象A。
# 9. 构造方法出现了循环依赖怎么解决?
由于构造函数是bean生命周期中最先执行的,Spring框架无法解决构造方法的循环依赖问题。可以使用@Lazy
懒加载注解,延迟bean的创建直到实际需要时。
# 12. Spring 的常见注解有哪些?
Spring的常见注解包括:
- 声明Bean的注解:
@Component
、@Service
、@Repository
、@Controller
。 - 依赖注入相关注解:
@Autowired
、@Qualifier
。 - 设置作用域的注解:
@Scope
。 - 配置相关注解:
@Configuration
、@ComponentScan
、@Bean
。 - AOP相关注解:
@Aspect
、@Before
、@After
、@Around
、@Pointcut
。
# @Autowired和@Resource的区别
@Autowired是Spring提供的注解,默认根据类型注入,
@Autowired有一个属性required,默认为true,表示强制要求bean实例的注入,在项目启动时,如果在IOC容器中没有对应类型的Bean就会报错,如果不想要自动注入,也可以设置为false。
但IOC容器中有多个相同类型的Bean,由于@Autowired注解时根据类型注入bean的,所以会报错,可以用@Primary、@Qualifier注解解决,
- @Primary是指定主要的bean,当有多个相同类型的bean时,会优先使用声明了@Primary注解的bean。
- 而使用@Qualifier可以根据bean的名字去装配
@Resource是JDK提供的注解,Spring提供了对这个注解的支持,
@Resource可以通过属性name和type指定注入bean的方式,使用name可以根据bean的名字注入,type可以根据类型注入,如果都不指定,默认是根据名字注入,如果没有匹配成功,再通过类型注入,如果两个都没有匹配到的话,就会报错。
总结:
- @Autowired是spring提供的注解。@Resource是jdk提供的注解,只是spring提供了对这个注解的支持。
- @Autowired默认根据类型注入bean,如果有多个相同类型的bean,可以通过@Primary、@Qualifier注解来解决。而@Resource可以指定通过名字或者类型来注入bean。默认是根据bean的名字注入,如果没有匹配到,再根据类型注入。
# @Component和@Bean的区别
用途:
- @Component适用于标注一个普通类
- @Bean注解是在配置类的方法上声明和配置一个定制化的Bean对象
使用方式:
@Component是一个类级别的注解,Spring通过@ComponentScan注解扫描修饰了@Component注解的类并注册到SpringIOC容器中为Bean
@Bean是方法级别的注解,在带有@Configuration注解的配置类中手动声明和配置Bean
控制权:
- @Component注解修饰的类是由Spring框架来创建和初始化的
- @Bean注解允许开发人员手动控制Bean的创建和配置过程
# @Bean 在 @Component 和 @Configuration 中的区别
@Bean
在@Component
中`@Bean
方法会被当作普通的工厂方法调用,每次调用都会返回一个新的实例。- 不支持
@Bean
方法之间的依赖注入(通过方法调用)。
@Bean
在@Configuration
中@Bean
方法会被 Spring 代理,确保 每次调用返回的是同一个实例(单例)。- 支持
@Bean
方法之间的依赖注入(通过方法调用)。
# 如何给bean指定名称
方法 | 示例代码 | 适用场景 |
---|---|---|
@Component("name") | @Component("myBean") | 自动扫描组件时指定名称 |
@Bean(name = "name") | @Bean(name = "customBean") | 在配置类中手动定义Bean时指定名称 |
@Qualifier("name") | @Qualifier("primaryService") | 解决多Bean冲突时指定限定符 |
XML <bean id="name"> | <bean id="myBean"> | 使用XML配置时指定名称 |
@Named("name") | @Named("customName") | 使用JSR-330标准时指定名称 |
# @Async 为什么不建议用?
默认线程池的弊端
线程无复用:默认使用SimpleAsyncTaskExecutor,每次调用都会创建新线程,而非复用线程池中的线程,可能导致系统资源耗尽(如内存溢出)
缺乏流量控制:默认配置下线程数无上限(Integer.MAX_VALUE),高并发时可能引发性能问题。
异常处理困难
- 异步方法抛出的异常不会直接传递给调用方,需通过AsyncUncaughtExceptionHandler或Future.get()捕获,增加了调试复杂度
事务与上下文问题
事务上下文默认不会传递到异步方法中,可能导致事务失效
自调用(同类方法调用@Asyc方法)因代理机制失效,无法异步执行
性能与稳定性风险
- 线程阻塞:若异步方法中包含阻塞操作(如IO),可能会导致线程死锁
# 10. SpringMVC的执行流程?视图阶段(JSP)
SpringMVC的执行流程包括以下步骤:、
- 用户发送出请求到前端控制器DispatcherServlet(DispatcherServlet是一个调度中心,负责调用其他的组件来完成用户的请求)
- DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
- HandlerMapping找到具体的处理器,生成处理器对像及处理器拦截器(如果有),再一起返回给DispatcherServlet。
- DispatcherServlet调用HandlerAdapter(处理器适配器)
- HandlerAdapter经过适配调用具体的处理器(Handler/Controller),Controller中的方法
- Controller执行完成返回ModelAndView对象
- HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)
- ViewReslover解析后返回具体View(视图)
- DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet响应用户
# SpringMVC的四大核心组件
- DispatcherServlet前端控制器,负责接收前端发来的所有请求,然后调用其他的三个组件处理用户请求
- HandlerMapping处理器映射器,生成并返回handler处理器
- HandlerAdapter处理器适配器,处理handler的参数和返回值
- ViewReslover处理器解析器,将逻辑视图解析为具体视图,
# SpringMVC的执行流程(前后端分离阶段)
- 用户发送出请求到前端控制器DispatcherServlet
- DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
- HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
- DispatcherServlet调用HandlerAdapter(处理器适配器)
- HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
- 由于方法上添加了@ResponseBody
- 处理器会通过HttpMessageConverter将返回结果转换为JSON并响应给用户
# SpringMVC的理解
Spring MVC 是 Spring Framework 生态中的一个 Web 框架,它基于 Servlet API 构建,采用 MVC(Model-View-Controller) 设计模式,旨在简化传统的 Servlet + JSP 开发方式。
Spring MVC 的架构增强与扩展
控制层(Controller)拆分
- 前端控制器(DispatcherServlet):负责请求的统一分发。
- 后端控制器(@Controller):处理具体业务逻辑。
模型层(Model)分层
- 业务层(Service):处理业务逻辑。
- 数据访问层(Repository/Dao):负责数据库交互。
视图层(View)灵活支持
- 支持多种视图技术,如 JSP、Thymeleaf、FreeMarker、Velocity 等。
Spring MVC 的工作流程
- 请求进入 DispatcherServlet(核心控制器)。
- DispatcherServlet 匹配对应的 @Controller 处理请求。
- Controller 处理业务逻辑,返回 ModelAndView(数据 + 视图名)。
- DispatcherServlet 调用 ViewResolver(视图解析器)解析视图。
- 渲染视图,将数据展示到客户端。
# 13. SpringMVC常见的注解有哪些?
SpringMVC的常见注解有:
@RequestMapping
:映射请求路径,用于类上,表示类中所有的方法都是以该地址为父路径。@RequestBody
:接收HTTP请求的JSON数据,将JSON转换为Java对象。@RequestParam
:指定请求路径的查询参数名称。@PathVariable
:从请求路径中获取参数。@ResponseBody
:将Controller方法返回的对象转化为JSON。@RequestHeader
:获取请求头数据。@RestController
=@ResponseBody
+@Controller
等。
# 14. Springboot常见注解有哪些?
Spring Boot的常见注解包括:
@SpringBootApplication
:由@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
组成。@SpringBootConfiguration
是组合了@Configuration
@EnableAutoConfiguration
打开自动配置功能
# 过滤器和拦截器的区别
第一运行的顺序不同,过滤器是SURVELI容器接收到请求之后,但是在SURVILIZE被调用之前运行的,而拦截器呢则是在SURVILIZE被调用之后,但是在响应被发送到客户端之前来运行的,第二配置方式不同,过滤器是在web点XML里面进行配置,而拦截器是在spring的配置文件中去进行配置,或者使用注解的方式进行配置,第3filter呢依赖于SURVILIT容器,而interceptor不依赖于SURVIL的容器,第4filter在过滤器中,只能对request和response进行操作,而interceptor呢可以对request response,handle le mode and view exception进行操作,相当于in interceptor多了,对于spring mvc生态下的组件的一个操作能力
运行的顺序不同:
- 过滤器先执行,然后才执行拦截器
- 过滤器(Filter)是在Servlet容器接收到请求之后,但在Servlet被调用之前执行。 拦截器(Interceptor)则是在Servlet被调用之后,但在响应被发送到客户端之前执行。
依赖关系不同:
- 过滤器依赖于Servlet容器。
- 拦截器不依赖于Servlet容器,它是Spring框架的一部分。
使用方式:
- 过滤器依赖servlet提供的fliter 接口,多个过滤器会根据配置的顺序来决定执行顺序
- 拦截器依赖Spring MVC提供的handlerInterceptor接口,多个拦截器根据Bean的配置顺序决定执行顺序,可以通过order来改变顺序
用途:
过滤器通常用来执行一些必要,编码处理、跨域处理
拦截器通常用来执行一些跟业务相关的,如身份认证与授权、日志记录
拦截器天然集成Spring,可以直接把Bean自动装配进来,方便执行业务处理
# 依赖注入时推荐使用构造函数注入
依赖注入的三种方式
- 使用
@Autowired
注解:将@Autowired
注解直接标记在字段上,Spring会自动注入依赖。 - Setter注入:使用
@Autowired
注解标记Setter方法,Spring会自动调用Setter方法注入依赖。 - 构造函数注入:如果有构造函数,Spring会自动调用构造函数并注入所需的依赖。
为什么推荐使用构造函数注入?
不可变性(Immutability)
- 构造函数注入允许将依赖字段声明为
final
,确保依赖在对象创建后不可变。
- 构造函数注入允许将依赖字段声明为
确保依赖项不为null
构造函数注入强制要求在创建对象时提供所有依赖,如果依赖项不存在,编译器会报错
避免循环依赖
构造函数注入可以帮助发现循环依赖问题,因为Spring在启动时会检查构造函数注入的循环依赖。可以通过
@Lazy
解决符合单一职责原则
构造函数注入强制要求类的依赖在构造函数中明确声明,有助于遵循单一职责原则(SRP)。
使用构造函数时,当一个类只有一个构造函数时,Spring 会自动选择它进行注入,无需额外注解。如果有多个构造函数,需要使用 @Autowired
或 @Primary
明确指定。
如果需要添加注入的依赖,还需要修改构造函数,这时可以使用lombok工具的RequiredArgsConstructor,会自动注入被final修饰的字段
# Spring通知有哪些类型?
# 解释基于XML Schema方式的切面实现
# 解释基于注解的切面实现
# Spring AOP
# JDK和CGLIB动态代理的区别 (opens new window)
- JDK动态代理只提供接口的代理,不支持类的代理。而CGLIB不用实现接口。当被代理的类实现了接口会默认使用jdk代理
- JDK动态代理是通过字节码的方式生成的动态代理类,而CGLIB是用ASM的开源字节码生成库生成的代理类(还有其他的相关的类)
- JDK动态代理是实现了目标类的接口,而CGLIB是将目标类作为父类进行继承,并重写父类的所有方法
- 调用时,JDK动态代理是通过反射调用的目标方法,而CGLIB是通过直接子类调用父类的方式调用目标方法
其他
- CGLIB是通过继承做的动态代理,所有如果类被标记为final,那么它是无法使用CGLIB做动态代理的。
- CGLIB除了生成目标子类代理类,还有一个FastClass(路由类),可以(但spring为了和jdk统一,默认没有使用)让本类方法调用进行增强(通过路由类回调代理类来实现方法的增强),而不会像jdk代理那样调用本类方法,增强会失效
- JDK和CGLIB的性能:JDK动态代理生成类速度快调用慢,CGLIB生成类速度慢但后续调用快,在老版本CGLIB的速度是JDK速度的10倍左右,但是实际上JDK的速度在版本升级的时候每次都提高很多性能,而CGLIB仍止步不前。在对JDK动态代理与CGib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CG引ib要好20%左右。
- 实现了接口的bean不能用实现类的class对象来获取bean,只能通过其接口的class对象来获取bean(因为实现类不在IOC容器中,被代理拦截了)或者实现类名字(需要用接口接收)
- IUserService bean = ioc.getBean(IUserService.class):
- IUserService bean = (IUserService) ioc.getBean("userServiceImpl");
# Spring事务
# 为什么有些公司禁止使用@Transactional声明式事务?
使用声明式事务,如果一个方法中存在较多耗时的操作,会引发长事务问题。长事务会导致锁的竞争,影响性能,也会占用数据库连接,导致数据库连接池被耗尽,影响程序正常执行。
@Transaction事务是通过spring的代理机制实现的,如果调用同类方法会导致事务失效。
@Transaction声明事务将事务控制逻辑放在注解中,如果项目复杂度增加,事务控制可能会变得复杂, 导致代码可读性和维护性下降,
// 反例:事务属性与业务逻辑混杂 @Transactional( isolation = ISOLATION_SERIALIZABLE, timeout = 30, rollbackFor = {BizException.class, DBException.class} )
@Transaction事务是方法层面的,事务范围是整个方法,而使用编程式事务可以更加灵活的控制事务范围,减少事务的锁定时间
# Spring事务的实现方式和实现原理
# Spring的事务传播行为
Spring的事务传播行为定义了Spring多个事务方法相互调用时事务的传播规则
Spring事务有七种传播行为。
REQUIRED(默认)
如果当前存在事务,则加入该事务;否则新建一个事务。
适用场景:通用业务逻辑
REQUIRED_NEW
无论当前是否存在事务,都新建独立事务,原事务被挂起。
适用场景:需隔离的子任务
NESTED
- 若当前存在事务,则在嵌套事务(子事务)中执行,可独立回滚;否则新建事务。
- 适用场景:部分操作可回滚
SUPPORTS
- 如果当前存在事务,则加入该事务,若不存在则以非事务方式执行。
- 适用场景:兼容无事务环境
NOT_SUPPORTED
- 以非事务方式执行,若存在事务则挂起。
- 适用场景:非关键操作
MANDATORY
- 如果当前存在事务,则加入该事务;否则抛出异常。
- 适用场景:强制事务执行
NEVER
- 强制非事务执行,若存在事务则抛出异常。
- 适用场景:禁止事务调用的方法
# 在一个包含多个服务调用的业务流程中,如果其中一个服务方法执行失败应立即回滚,不影响其他服务的正常执行,你认为应如何设置事务传播行为?
使用REQUIRED_NEW或者NESTED REQUIRED_NEW可以为每一个方法创建一个新的独立事务,不会相互影响,而NESTED是嵌套一个子事务,子事务会独立回滚,但是如果外层事务回滚,子事务也会回滚
# REQUIRES _NEW的实际应用
记录日志
# 如果系统中出现了事务嵌套过深导致的性能问题你认为可能的原因是什么?如何优化?
每个嵌套事务会占用独立的数据库连接,导致资源占用增加,或者事务持有的锁时间过长。优化:减少不必要的事务嵌套,将事务合并或者调整事务传播行为,优先使用REQUIRED代替REQUIRES_NEW,避免创建新的事务
# spring的事务隔离?
# Spring框架的事务管理有哪些优点?
# conditional注解的作用
@Conditional
注解的作用是为Bean的加载提供条件判断,只有满足指定条件时,Spring才会将该Bean注册到IOC容器中。
而我们可以实现Condition
接口,并重写matches()
方法,编写自定义的条件判断逻辑。
所以conditional注解,增加了bean的装配的一个灵活性,
Spring Boot在@Conditional
的基础上,提供了更语义化的条件注解,避免手动编写Condition
实现类:
@ConditionalOnClass
:当类路径中存在指定类时生效。@ConditionalOnBean
:当容器中存在指定Bean时生效。@ConditionalOnProperty
:当配置文件中存在指定属性且匹配值时生效。@ConditionalOnMissingBean
:当容器中不存在指定Bean时生效。
# spring里面的事物和分布式事务是如何区分的
spring里没有提供事务,它只是提供了对数据库事务的一个管理的封装,我们可以通过声明式事务的配置啊,使得开发人员可以从一些复杂的事务处理,里面去脱离出来,我们不需要再去关心连接的获取啊,连接的关闭啊,事物的提交,事务的回滚,这样一些操作,我们可以更加聚焦在业务的开发层面,所以呢spring里面的事物啊,本质上是数据库层面的一个事物,而这种事务管理啊,主要是针对于单个数据库里面的,多个数据表的操作,他去满足一个事物的ACID特性,而分布事务呢是解决多个数据库事务操作的,一个数据一致性问题,传统的关系数据库呢不支持跨库的事务操作,所以需要引入分布式事务的解决方案,而spring里面并没有提供分布式事务的场景知识,所以spring里面的事物和分布式事务,在使用上并没有直接的关联关系,但是呢我们可以使用一些主流的分布式事务,解决框架,比如说像seata集成到spring生态里面去,解决分布式事务的一个问题
Spring 本身并不提供事务能力,而是对数据库事务的管理逻辑进行了封装。通过声明式事务(如 @Transactional
)的配置,开发者可以避免手动处理连接的获取/关闭、事务提交/回滚等底层操作,从而更专注于业务逻辑开发。
- Spring 事务的本质是基于单个数据库的 ACID 特性(原子性、一致性、隔离性、持久性),用于管理同一数据库内多个表的操作。
- 分布式事务用于协调多个独立数据库/服务的事务操作(例如跨库、跨微服务),确保全局数据一致性。
Spring 原生未提供分布式事务实现,但支持集成第三方框架(如 Seata、Atomikos)。例如,通过 Seata 的 GlobalTransactional
注解,可以在 Spring 生态中实现分布式事务的协调(如 AT 模式、TCC 模式)。
# Spring 中定义两个相同 ID 的 Bean 会报错吗?
在同一个XML配置文件里面,如果存在两个相同 ID 的 Bean,Spring 容器在启动时会直接报错(BeanDefinitionStoreException
),因为 ID 是 Bean 的唯一标识符,
不同 XML 文件:
- 如果相同 ID 的 Bean 定义在不同的 XML 文件中,Spring 默认会覆盖先加载的 Bean(后加载的生效)。
- 可通过
allowBeanDefinitionOverriding=false
强制禁止覆盖,此时会报错。
这时如果使用@Autowire注解,去根据类型进行实例注入,在启动时候会提示,找不到那个未注册的实例 如果我们使用@Resource这个注解,去根据名字来实现依赖注入,而在spring IOC容器里,根据名字只会得到注册的那个实例对象,于是spring把这个实例赋值给那个未注册的实例时,会就提示类型不匹配错误,错误是在spring IOC容器里的bean初始化后,依赖注入阶段发生的
# spring中的bean的作用域有哪些
常规的bean生命周期呢只有两种
- Singleton(单例)整个 Spring 容器中只会存在一个 Bean 实例,所有请求共享同一个对象。默认作用域,适用于无状态的 Bean(如 Service、DAO)。
- Prototype(原型)每次从 IOC 容器获取该 Bean 时,都会创建一个新的实例。适用于需要保持独立状态的 Bean(如 DTO、线程不安全对象)。
在基于spring框架下的web应用里,增加了一个会话维度,来控制bean的生命周期,
Request(请求作用域)
- 每次 HTTP 请求都会创建一个新的 Bean 实例,请求结束后销毁。
- 适用于存储请求级别的数据(如用户表单数据)。
Session(会话作用域)
- 同一个 HTTP Session 共享同一个 Bean 实例,不同 Session 使用不同的实例。
- 适用于存储用户会话数据(如登录信息)。
Application / Global Session(全局会话作用域,仅 Portlet 应用)
- 全局 Session 作用域,所有用户共享同一个 Bean 实例(已较少使用)
# spring中的BeanFactory 和FactoryBean 的区别
Spring的核心功能是IOC容器,IOC容器本质上是一个Bean的容器或者说是一个Bean的工厂。它能够根据XML配置或注解声明,完成Bean的加载和初始化,并生产所需的bean
BeanFactory
- BeanFactory是Spring框架中所有Bean容器的顶级接口
- 它为Spring容器定义了一套完整的规范
- 提供了getBean()等基础方法,用于从容器中获取指定Bean实例
- 实现了依赖注入(DI)功能,解决Bean之间的依赖关系
而FactoryBean
- FactoryBean是一个特殊的工厂Bean,它是一个接口,主要用于动态生成特定类型的Bean实例,也就是用户自定义Bean的创建
- 有一个重要的方法getObject(),用于实现自定义的Bean构建过程
- 常用于创建复杂对象或需要特殊初始化的Bean
- springCloud里的OpenFeign组件的客户端代理,就是使用FactoryBean来实现的