面试题有关Spring的几个知识点

Spring

Spring 是一种轻量级开发框架,旨在提高开发人员的开发效率以及系统的可维护性。

IOC(控制反转)

控制反转(IoC, Inversion of Control)是一种设计原则,指的是将对象的创建和管理责任从程序代码转移到框架或容器中,允许框架控制程序的流程和对象的生命周期,从而实现更高的模块化和可维护性。在Spring中,IoC主要通过依赖注入(DI, Dependency Injection)实现,使得对象之间的依赖关系可以在运行时动态地进行管理,从而减少耦合度。

优点:
实现组件之间的解耦

提高程序的灵活性和可维护性

缺点:

对程序员来说创建对象的步骤变复杂了,不直观

因为使用反射来创建对象,所以在效率上会有些损耗。但相对于程序的灵活性和可维护性来 说,这点损耗是微不足道的。

缺少IDE重构的支持,如果修改了类名,还需到XML文件中手动修改。

DI(依赖注入)

通常,依赖注入可以通过三种方式完成,即: 构造函数注入 setter 注入 接口注入

 spring 提供了哪些配置方式

Spring 提供了多种配置方式,以便开发者根据需求和偏好选择合适的方式。主要的配置方式包括:

1. XML 配置:使用 XML 文件定义 Spring 的各种 bean 和其依赖关系,是最传统的配置方式。

2. Java 注解配置:通过使用注解(如 `@Component`, `@Service`, `@Repository`, `@Controller`, `@Autowired`, `@Configuration` 等)来标识和配置 beans,减少了XML的使用,提升了可读性。

3. Java 代码配置:使用 Java 类和方法来定义和配置 beans,结合 `@Configuration` 注解,可通过 `@Bean` 注解指定 bean 的创建,这种方式与纯 Java 的编程风格更为一致。


XML 配置:

优点: 方便 直观 代码少(没有配置文件的书写那么复杂)

缺点:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的


Java 注解配置:

优点: 配置和代码是分离的 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。

缺点:编写麻烦,效率低,大型项目过于复杂。

解决循环依赖(三级缓存)

在Spring中,循环依赖是指两个或多个bean相互依赖,导致在实例化时出现问题,即A需要B,B又需要A。在此情况下,若使用传统的单例模式进行依赖注入,可能会导致StackOverflowErrorNullPointerException。为了解决这个问题,Spring引入了三级缓存机制。

三级缓存机制
  1. 单例池(singletonObjects):存储已完全初始化的 bean 实例。

  2. 早期单例池(earlySingletonObjects):存储那些已被创建但尚未完全初始化的 bean 实例。这个池用于存放在依赖注入过程中的 bean。

  3. 所需依赖的 bean 定义(singletonFactories):存储生成早期单例的工厂实例。这是一个 Supplier 接口的实现,可以在需要时创建 bean。

具体实现步骤
  • 当 bean A 需要 bean B 时,Spring 会将 bean A 放入 早期单例池 中。
  • 然后,Spring 对 bean B 进行初始化,假设 B 又依赖于 A,此时可以从 早期单例池 中获取 A 的引用(代理)。
  • 当 bean B 初始化完成后,Spring 再返回到 A 的初始化流程并继续进行注入。

 注意的是,Spring 只支持通过构造函数注入的方式处理循环依赖,而如果是通过 setter 方法或字段注入,则可能会出现依赖注入不完全的问题。

一级缓存(singletonObjects):存储已经完全初始化好的单例Bean实例。当一个Bean被成功创建并注入所有依赖后,它会被存放在这里。

二级缓存(earlySingletonObjects):存储正在创建中的单例Bean实例。当一个Bean的实例化过程开始,但还没有完成属性填充和初始化时,它会被存放在这里。这个缓存允许其他Bean引用尚未完全初始化的Bean,从而解决循环依赖问题。

三级缓存(singletonFactories):存储单例Bean工厂对象,用于创建Bean实例。当一个Bean被其他Bean引用,并且需要提前曝光时,Spring会将一个能够创建该Bean实例的工厂对象存放在这里。这个工厂对象可以确保在需要时能够创建Bean的实例,同时支持代理对象的创建,如AOP代理。

 

Spring Bean

 Spring Bean的生命周期

 创建过程
1. 首先实例化Bean,并设置Bean的属性。
 2. 根据其实现的Aware接口(主要是 BeanFactoryAware接口,BeanFactoryAware, ApplicationContextAware)设置依赖信息。
 3. 接下来调用BeanPostProcess的postProcessBeforeInitialization方法,完成initial前的自定义逻 辑;
4. afterPropertiesSet方法做一些属性被设定后的自定义的事情;
5. 调用Bean自身定义的init方法,去做一些初始化相关的工作;
6. 然后再调用postProcessAfterInitialization去做一些bean初始化之后的自定义工作。 这四个方法的 调用有点类似AOP。
7. 此时,Bean初始化完成,可以使用这个Bean了。
销毁过程:
1. 如果实现了DisposableBean的destroy方法,则调用它。

 2. 如果实现了自定义的销毁方法,则 调用之。


Spring Bean的作用域
 

singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。

prototype : 每次请求都会创建一个新的 bean 实例。

request: 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。

session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。

global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经 没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容 器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话

@Autowired的使用

 

AOP

AOP的基本概念

  1. 切面(Aspect):关注横切关注点的模块化,例如日志记录、事务管理等。
  2. 连接点(Join Point):程序执行中一个明确的点,可以在该点添加切面逻辑,例如方法调用、构造函数执行等。
  3. 通知(Advice):切面在特定连接点执行的代码。可以是前置通知、后置通知、环绕通知等。前置通知 → 2. 环绕通知(进入方法体前) → 3. 目标方法 → 4. 环绕通知(进入方法体后) →返回通知(如果目标方法正常返回)或异常通知(如果目标方法抛出异常) → 6. 后置通知
  4. 切入点(Pointcut):定义了哪些连接点应该被通知的表达式。
  5. 目标(Target):被切面所增强的对象。
  6. 代理(Proxy):AOP通常是通过生成代理对象来实现的,代理会控制对目标对象的调用。

 

AOP的实现方式
 

1. 代理模式

  • JDK动态代理

    • 适用于接口实现。
    • 利用Java的反射机制,通过实现InvocationHandler接口动态生成代理类。
    • 只能对接口进行代理,不能对具体类进行代理。
  • CGLIB(Code Generation Library)代理

    • 适用于没有接口的类或需要代理具体类的情况。
    • 通过字节码增强的方式,创建目标类的子类来实现代理。
    • 相比于JDK动态代理,CGLIB会产生更多的字节码,但能够代理类的方法。

PS:Maven是一个用于Java项目的项目管理和构建自动化工具,主要通过依赖管理和标准化构建流程来提升开发效率。
 

事务

Spring 管理事务的方式有几种

1. 编程式事务,在代码中硬编码。(难维护,不推荐使用)

2. 声明式事务,在配置文件中配置(推荐使用) 声明式事务又分为两种:

1. 基于XML的声明式事务

2. 基于注解的声明式事务

Spring 事务中的隔离级别

Spring 支持以下四种隔离级别:

  1. READ_UNCOMMITTED(读未提交)
    允许读取其他事务未提交的更改。这种级别可能导致脏读、不可重复读和幻读。

  2. READ_COMMITTED(读已提交)
    只允许读取已提交的事务更改。这可以防止脏读,但可能会导致不可重复读和幻读。

  3. REPEATABLE_READ(可重复读)
    确保在同一事务中多次读取同一数据时返回相同的值,防止了脏读和不可重复读,但可能会导致幻读。

  4. SERIALIZABLE(串行化)
    是最高的隔离级别,确保事务串行执行,避免脏读、不可重复读和幻读。性能较低,通常不推荐在高并发场景中使用。

如何设置隔离级别

在 Spring 中,可以通过 @Transactional 注解设置隔离级别,例如:

import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

@Transactional(isolation = Isolation.READ_COMMITTED)
public void someTransactionalMethod() {
    // 业务逻辑
}

 

Spring 事务失效

1. 异常未捕获  
   如果在事务中发生未捕获的运行时异常,事务会自动回滚。但如果是受检异常(如 `IOException`),则需要显式回滚。

2. 配置错误  
   如果未正确配置事务管理器或未启用事务注解,事务可能无法生效。

3. 传播行为
   使用 `@Transactional` 时,如果传播行为设置为 `Propagation.NEVER` 或 `Propagation.NOT_SUPPORTED`,事务不会被创建或参与。

4. 自我调用 
   如果在同一个类内部调用带有 `@Transactional` 的方法,事务不会生效。Spring 的 AOP 代理机制要求通过代理类进行调用。

5. 事务超时  
   如果事务超时,Spring 会自动回滚事务。可以通过 `@Transactional(timeout = seconds)` 设置超时时间。

6. 未使用 Spring 管理的对象  
   如果在非 Spring 管理的对象中使用事务,事务不会生效。

7. 数据源问题 
   如果数据源配置不正确或连接失败,事务也可能无法正常工作。

8. 外部事务管理 
   如果同一数据库连接被其他框架或代码管理(例如,使用 JDBC 直接操作),可能导致事务失效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值