SpringIOC、AOP、事务和高级特性相关面试题

3 阅读20分钟

一、Spring IoC 容器 高频面试题

1. 什么是Spring IoC?IoC和DI的区别与联系是什么?(必问)

答案:

① 定义:IoC(Inversion of Control,控制反转)是Spring的核心思想,本质是反转Bean的创建、依赖管理和生命周期的控制权——传统开发中,开发者手动new对象、维护依赖;IoC模式下,控制权转移到Spring IoC容器,容器负责创建Bean、注入依赖、管理Bean生命周期,开发者仅需配置Bean,无需关心具体实现。

② 区别与联系:

  • 联系:DI(Dependency Injection,依赖注入)是IoC的具体实现方式,没有DI,IoC思想无法落地;没有IoC,DI也失去存在意义,二者不可分割。
  • 区别:IoC是「设计思想/原则」,强调“控制权反转”;DI是「实现手段」,强调“通过容器将依赖注入到目标Bean”,是IoC思想的具体落地操作。

2. Spring IoC容器的核心接口有哪些?BeanFactory和ApplicationContext的区别是什么?(必问)

答案:

① 核心接口:顶层接口是BeanFactory,核心实现接口是ApplicationContext(BeanFactory的子接口)。

② 核心区别(3点核心,面试重点):

对比维度BeanFactoryApplicationContext
加载方式懒加载(仅调用getBean()时才创建Bean)预加载(容器启动时,自动创建所有单例Bean,除非配置懒加载)
功能范围基础功能(获取Bean、判断Bean存在等),无扩展功能继承BeanFactory所有功能,新增国际化、事件发布、资源加载、AOP集成等增强功能
实际应用Spring内部使用,开发中极少直接使用开发首选,如ClassPathXmlApplicationContext、AnnotationConfigApplicationContext(SpringBoot底层核心)

3. Spring Bean的生命周期是什么?(必问,重点)

答案:

Spring Bean的生命周期贯穿「创建→初始化→使用→销毁」,核心4个阶段,按顺序执行(ApplicationContext容器下):

  1. Bean定义阶段:容器加载Bean配置(XML/注解),解析为BeanDefinition对象,存储到BeanDefinitionRegistry(注册表),记录Bean的类名、作用域、依赖等信息。

  2. Bean实例化阶段:容器根据BeanDefinition,调用Bean的构造器(默认无参构造)创建Bean实例(仅分配内存,未注入依赖)。

  3. Bean初始化阶段(核心):

    1. 注入依赖:容器将依赖的Bean通过构造器/setter/字段注入到当前Bean。
    2. 调用Aware接口方法:依次调用BeanNameAware(注入Bean名称)、BeanFactoryAware(注入容器实例)、ApplicationContextAware(注入ApplicationContext,仅ApplicationContext容器支持)。
    3. BeanPostProcessor前置增强:调用postProcessBeforeInitialization(),对Bean进行初始化前增强(AOP动态代理前置操作)。
    4. 自定义初始化:执行@PostConstruct注解方法(优先)或init-method配置的方法。
    5. BeanPostProcessor后置增强:调用postProcessAfterInitialization(),生成AOP动态代理对象(核心,AOP实现关键)。
  4. Bean销毁阶段:容器关闭时(仅单例Bean,原型Bean由开发者手动管理),执行@PreDestroy注解方法(优先)或destroy-method配置的方法,释放资源。

4. Spring Bean的作用域有哪些?单例和原型的核心区别是什么?(高频)

答案:

① 核心作用域(5种,重点前2种):

  • singleton(单例,默认):容器中仅存在一个Bean实例,所有请求共享该实例,生命周期由容器管理。
  • prototype(原型):每次调用getBean(),容器都会创建一个新的Bean实例,生命周期由开发者手动管理(容器不负责销毁)。
  • request(Web场景):每个HTTP请求创建一个新Bean,请求结束后销毁。
  • session(Web场景):每个HTTP Session创建一个新Bean,Session过期后销毁。
  • globalSession(分布式场景):多服务器共享一个Bean,仅用于Portlet环境(极少用)。

② 单例和原型的核心区别:

  • 实例数量:单例仅1个,原型每次请求新实例。
  • 生命周期:单例由容器管理(创建→销毁),原型容器仅负责创建,销毁由开发者手动处理。
  • 性能:单例性能优(无需频繁创建实例),原型性能略差(频繁创建/销毁)。

5. Spring IoC如何解决循环依赖?三级缓存分别是什么?(高频难点)

答案:

① 循环依赖定义:两个/多个Bean互相依赖(如A依赖B,B依赖A),导致容器无法正常初始化Bean。

② Spring支持情况:仅支持「单例Bean + setter注入/字段注入」,不支持「单例Bean + 构造器注入」「原型Bean(无论哪种注入)」。

③ 三级缓存核心原理(解决循环依赖的关键):

  • 一级缓存(singletonObjects):存储「完全初始化完成」的单例Bean(可用状态)。
  • 二级缓存(earlySingletonObjects):存储「提前暴露」的、未完全初始化的单例Bean(仅实例化,未注入依赖)。
  • 三级缓存(singletonFactories):存储Bean的工厂对象(ObjectFactory),用于生成提前暴露的Bean实例(解决AOP动态代理的循环依赖)。

④ 简单流程:A实例化后放入三级缓存 → A需要注入B,B实例化后放入三级缓存 → B需要注入A,从三级缓存获取A的工厂,生成A的提前暴露实例(放入二级缓存) → B注入A后完成初始化(放入一级缓存) → A注入B后完成初始化(放入一级缓存)。

6. Spring Bean的注入方式有哪些?@Autowired和@Resource的区别是什么?(必问)

答案:

① 核心注入方式(3种):

  • 构造器注入(推荐):通过@Autowired标注构造器,容器调用带参构造器注入依赖,能保证Bean注入的完整性(避免空指针)。
  • setter注入:通过@Autowired标注setter方法,容器调用setter方法注入依赖,适用于可选依赖。
  • 字段注入:通过@Autowired标注类字段,无需构造器和setter,简洁高效,但可读性略差,不推荐用于复杂依赖。

② @Autowired和@Resource的区别(3点核心):

  • 来源不同:@Autowired是Spring自带注解;@Resource是JSR-250规范注解(Java原生)。
  • 注入方式不同:@Autowired默认按「类型(byType)」注入,多个实现类需配合@Qualifier指定Bean名称;@Resource默认按「名称(byName)」注入,名称匹配失败再按类型注入。
  • 支持范围不同:@Autowired可用于构造器、setter、字段;@Resource可用于字段、setter,不支持构造器注入。

二、Spring AOP 高频面试题

1. 什么是Spring AOP?AOP和OOP的区别与联系是什么?(必问)

答案:

① 定义:AOP(Aspect-Oriented Programming,面向切面编程)是Spring核心特性,与OOP互补,核心是「关注点分离」——将日志、事务、权限等通用功能(切面)从业务逻辑中剥离,动态织入到目标方法中,实现通用功能复用和解耦。

② 区别与联系:

  • 联系:二者互补,OOP负责核心业务逻辑的封装(纵向复用),AOP负责通用功能的复用(横向复用),共同构成Spring的核心架构。
  • 区别:OOP以「类」为核心,解决纵向代码重复;AOP以「切面」为核心,解决横向代码重复(如所有方法的日志记录)。

2. Spring AOP的核心概念有哪些?(必问,记准)

答案:

核心7个概念,简洁明了,面试直接答:

  • 切面(Aspect):封装通用功能的类(如日志切面、事务切面),包含切入点和通知。
  • 通知(Advice):切面中的具体逻辑(要做什么),Spring提供5种通知类型。
  • 切入点(Pointcut):定义“在哪些方法上执行通知”,通过表达式指定目标方法(在哪里做)。
  • 连接点(JoinPoint):程序执行中可插入切面的所有点(如方法调用、异常抛出),切入点是连接点的子集。
  • 目标对象(Target):被切面增强的业务逻辑对象。
  • 代理对象(Proxy):Spring AOP动态生成的对象,包含目标对象逻辑和切面通知逻辑,开发者实际调用。
  • 织入(Weaving):将切面通知织入到目标方法的过程(Spring AOP是运行时织入)。

3. Spring AOP的5种通知类型是什么?执行顺序如何?(必问)

答案:

① 5种通知类型(按执行时机排序):

  • 前置通知(@Before):目标方法执行前执行,无法阻止目标方法执行。
  • 环绕通知(@Around):包裹目标方法,执行前后都能增强,可控制目标方法执行(需调用proceed()),功能最强。
  • 返回通知(@AfterReturning):目标方法正常执行完成后执行(无异常)。
  • 异常通知(@AfterThrowing):目标方法抛出异常后执行。
  • 后置通知(@After):目标方法执行后执行(无论是否异常,都会执行),常用于资源释放。

② 执行顺序(单一切面):

@Before → 目标方法 → @AfterReturning/@AfterThrowing → @After; 环绕通知:@Around(前置部分) → 目标方法 → @Around(后置部分),环绕通知会包裹其他所有通知。

③ 多切面(按@Order注解排序,值越小优先级越高):

优先级高的切面:@Before先执行 → 优先级低的切面:@Before执行 → 目标方法 → 优先级低的切面:@AfterReturning/@AfterThrowing → 优先级低的切面:@After → 优先级高的切面:@AfterReturning/@AfterThrowing → 优先级高的切面:@After。

4. Spring AOP的实现原理是什么?JDK动态代理和CGLIB动态代理的区别是什么?(必问难点)

答案:

① 核心实现原理:Spring AOP基于「动态代理」,运行时动态生成代理对象,将切面通知织入到目标方法,不修改目标对象源码,符合开闭原则。

② 两种动态代理方式及区别:

对比维度JDK动态代理(默认)CGLIB动态代理(补充)
实现基础基于Java自带的Proxy类和InvocationHandler接口基于第三方CGLIB库,通过继承目标类实现
适用场景目标对象必须实现至少一个接口目标对象可无接口(也支持有接口)
限制无法代理无接口的类无法代理final类、final方法(无法继承/重写)
性能效率高(JDK原生,无需额外依赖)效率略低(需动态生成子类),Spring已集成CGLIB

③ Spring选择规则:目标对象有接口 → JDK动态代理;目标对象无接口 → CGLIB动态代理;SpringBoot 2.x+ 默认配置proxy-target-class=true,无论是否有接口,优先使用CGLIB。

5. 同一类中,被AOP增强的方法A调用方法B,B的AOP增强为什么会失效?如何解决?(高频实战题)

答案:

① 失效原因:Spring AOP基于动态代理,同一类中方法调用时,调用的是「目标对象自身的方法」,而非「代理对象的方法」,无法触发AOP的增强逻辑。

② 3种解决方案(优先前2种):

  • 自注入:在类中通过@Autowired + @Lazy注解注入自身代理对象,调用时使用代理对象调用方法B(避免循环依赖)。
  • 通过ApplicationContext获取代理对象:注入ApplicationContext,调用getBean()获取自身代理对象,再调用方法B。
  • 拆分类:将方法A和方法B拆分到不同类中,避免同一类中方法调用。

示例(自注入方式):

@Service
public class UserService {
    @Autowired
    @Lazy // 避免循环依赖
    private UserService userService; // 注入自身代理对象

    @Log // AOP增强
    public void methodA() {
        userService.methodB(); // 调用代理对象的methodB,触发AOP
    }

    @Log // AOP增强
    public void methodB() {
        // 业务逻辑
    }
}

6. Spring AOP的切入点表达式有哪些?常用的execution表达式语法是什么?(高频)

答案:

① 核心切入点表达式(3种,重点前2种):

  • execution表达式(最常用):通过方法的修饰符、返回值、包名、类名、方法名、参数匹配目标方法。
  • @annotation表达式:匹配被指定注解标注的方法(如@Log注解标注的方法)。
  • within表达式:匹配指定包/类下的所有方法。

② execution表达式语法:

execution(修饰符 返回值类型 包名.类名.方法名(参数类型) throws 异常类型)

通配符说明:

  • *:匹配任意单个字符(如返回值、包名、方法名、参数)。
  • ..:匹配任意多个字符(如多级包、任意个数/类型的参数)。

常用示例:

  • execution(* com.service..(..)):匹配com.service包下所有类的所有方法。
  • execution(public * com.service.UserService.*(String, ..)):匹配UserService类中所有public方法,第一个参数为String。
  • execution(* com.service...(..)):匹配com.service包及其子包下所有类的所有方法。

三、Spring 事务管理 高频面试题

1. 什么是事务?Spring事务的四大特性是什么?(必问)

答案:

① 事务(Transaction):是数据库操作的最小单元,一组操作要么全部成功,要么全部失败,保证数据的一致性。

② 事务四大特性(ACID,面试必须答全):

  • 原子性(Atomicity):事务中的所有操作要么全部执行成功,要么全部回滚,不可分割。
  • 一致性(Consistency):事务执行前后,数据的完整性约束不被破坏(如转账前A+B=100,转账后仍为100)。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的操作不会影响另一个事务的执行(避免并发问题)。
  • 持久性(Durability):事务执行成功后,数据会永久保存到数据库,即使系统崩溃也不会丢失。

2. Spring事务的隔离级别有哪些?默认隔离级别是什么?(必问)

答案:

① Spring事务隔离级别(基于数据库隔离级别,5种):

  • DEFAULT(默认):继承数据库的默认隔离级别(MySQL默认REPEATABLE READ,Oracle默认READ COMMITTED)。
  • READ_UNCOMMITTED(读未提交):最低隔离级别,允许读取未提交的数据,会出现脏读、不可重复读、幻读。
  • READ_COMMITTED(读已提交):允许读取已提交的数据,避免脏读,会出现不可重复读、幻读(Oracle默认)。
  • REPEATABLE_READ(可重复读):保证同一事务中多次读取同一数据结果一致,避免脏读、不可重复读,会出现幻读(MySQL默认)。
  • SERIALIZABLE(串行化):最高隔离级别,事务串行执行,避免所有并发问题,性能最差(极少用)。

② 核心并发问题(对应隔离级别):

  • 脏读:读取到其他事务未提交的数据(后续可能回滚,数据无效)。
  • 不可重复读:同一事务中,多次读取同一数据,结果不一致(其他事务修改并提交)。
  • 幻读:同一事务中,多次查询同一条件,结果行数不一致(其他事务新增/删除并提交)。

3. Spring事务的传播行为有哪些?常用的传播行为是什么?(必问)

答案:

① 事务传播行为:定义了“当一个事务方法调用另一个事务方法时,如何处理事务”(如是否新建事务、是否加入当前事务),Spring提供7种传播行为,重点记4种常用的:

  • REQUIRED(默认):如果当前有事务,就加入当前事务;如果没有事务,就新建一个事务(最常用,如Service方法调用Dao方法)。
  • REQUIRES_NEW:无论当前是否有事务,都新建一个事务,原事务暂停,新事务执行完成后,原事务继续执行(如日志记录,无论主事务成败,日志都要保存)。
  • SUPPORTS:如果当前有事务,就加入当前事务;如果没有事务,就以非事务方式执行(不常用)。
  • NESTED:如果当前有事务,就在当前事务中嵌套一个子事务;如果没有事务,就新建一个事务(子事务回滚不影响主事务,主事务回滚会带动子事务回滚)。

② 补充:NEVER(不允许有事务,有事务则抛异常)、MANDATORY(必须有事务,没有则抛异常)、NOT_SUPPORTED(以非事务方式执行,有事务则暂停)(极少用)。

4. Spring事务的实现方式有哪些?声明式事务和编程式事务的区别是什么?(必问)

答案:

① 两种实现方式:

  • 编程式事务:通过代码手动控制事务(如TransactionTemplate、PlatformTransactionManager),灵活性高,代码侵入性强。
  • 声明式事务:通过注解(@Transactional)或XML配置实现,无需手动写事务控制代码,代码侵入性低,是开发首选。

② 核心区别:

对比维度编程式事务声明式事务
代码侵入性强(需手动写事务控制代码)弱(仅需标注@Transactional注解)
灵活性高(可手动控制事务的开始、提交、回滚)低(依赖注解/配置,无法灵活控制)
开发效率低(代码繁琐)高(简化开发,无需关注事务控制)
适用场景复杂事务场景(如多步骤事务控制)常规业务场景(如CRUD操作)

5. @Transactional注解的核心属性有哪些?(高频)

答案:

@Transactional是声明式事务的核心注解,常用属性(面试重点记5个):

  • propagation:事务传播行为(默认REQUIRED)。
  • isolation:事务隔离级别(默认DEFAULT)。
  • readOnly:是否为只读事务(默认false),设置为true时,只能执行查询操作,不能执行增删改,提升查询性能。
  • rollbackFor:指定哪些异常触发事务回滚(如rollbackFor = Exception.class,所有异常都回滚),默认只回滚运行时异常(RuntimeException)和Error。
  • noRollbackFor:指定哪些异常不触发事务回滚(如noRollbackFor = BusinessException.class)。
  • timeout:事务超时时间(默认-1,无超时),单位秒,超时未完成则回滚。

6. Spring事务回滚失效的常见原因有哪些?(高频实战题)

答案:

核心6种原因(面试重点答,结合场景):

    1. 注解标注错误:@Transactional标注在非public方法上(Spring事务仅对public方法生效)。
    1. 异常类型不匹配:未指定rollbackFor,抛出的是检查异常(如IOException),默认不回滚。
    1. 异常被手动捕获:方法内try-catch捕获了异常,未重新抛出,Spring无法感知异常,无法回滚。
    1. 事务传播行为配置错误:如配置为SUPPORTS,当前无事务时,以非事务方式执行,无法回滚。
    1. 未开启事务管理:未在配置类上标注@EnableTransactionManagement(SpringBoot自动开启,纯Spring需手动开启)。
    1. 目标对象未被Spring管理:Bean未通过@Component、@Service等注解注册,Spring无法对其进行事务增强。

7. Spring事务的底层实现原理是什么?(难点)

答案:

Spring事务的底层依赖「AOP动态代理」和「事务管理器(PlatformTransactionManager)」,核心流程:

  1. 当方法标注@Transactional时,Spring AOP会为该方法生成动态代理对象。
  2. 代理对象调用目标方法前,通过事务管理器(如DataSourceTransactionManager)开启事务,获取数据库连接,设置事务隔离级别、传播行为等。
  3. 调用目标方法,执行业务逻辑和数据库操作。
  4. 若方法正常执行,事务管理器提交事务;若方法抛出异常(符合回滚条件),事务管理器回滚事务;若异常被捕获未抛出,事务提交。
  5. 事务执行完成后,释放数据库连接。

核心关键点:事务管理器是核心,不同的数据源(如JDBC、JPA)对应不同的事务管理器;AOP负责将事务控制逻辑(开启、提交、回滚)织入到目标方法中。

四、Spring 高级特性 高频面试题

1. Spring的BeanPostProcessor是什么?作用是什么?(高频)

答案:

① 定义:BeanPostProcessor是Spring IoC的核心扩展接口,称为「Bean后置处理器」,用于在Bean初始化前后对Bean进行增强处理,是AOP实现的核心基础。

② 核心作用:在Bean的初始化阶段(初始化前、初始化后)插入自定义逻辑,对Bean进行增强,无需修改Bean的源码。

③ 核心方法(2个):

  • postProcessBeforeInitialization(Object bean, String beanName):Bean初始化前执行(在@PostConstruct、init-method之前),返回增强后的Bean。
  • postProcessAfterInitialization(Object bean, String beanName):Bean初始化后执行(在@PostConstruct、init-method之后),返回增强后的Bean(AOP动态代理对象在此生成)。

注意:该接口的方法会对容器中所有Bean生效,若需针对性增强,可在方法中判断Bean类型。

2. Spring的FactoryBean和BeanFactory的区别是什么?(高频易混)

答案:

二者名称相似,但功能完全不同,核心区别:

对比维度BeanFactoryFactoryBean
本质IoC容器的顶层接口,是「Bean工厂」,负责管理所有Bean的创建、依赖、生命周期是一个「特殊的Bean」,用于创建复杂Bean(如数据源、SqlSessionFactory)
作用管理Bean(获取Bean、判断Bean存在等)创建目标Bean(自身是Bean,核心功能是生成其他Bean)
使用方式直接使用(如ApplicationContext是其实现类)注册到容器中,通过getBean()获取其生成的目标Bean(默认获取目标Bean,加&获取FactoryBean本身)

示例:MyBatis的SqlSessionFactoryBean,注册到Spring容器后,getBean()获取的是SqlSessionFactory(目标Bean),而非SqlSessionFactoryBean本身。

3. Spring的事件驱动模型是什么?核心组成有哪些?(高频)

答案:

① 定义:Spring事件驱动模型基于「观察者模式」,实现组件间的解耦,允许一个组件发送事件,其他组件监听事件并做出响应,无需直接依赖。

② 核心组成(3个,缺一不可):

  • 事件(ApplicationEvent):事件的载体,存储事件相关信息,自定义事件需继承ApplicationEvent。
  • 事件发布者(ApplicationEventPublisher):负责发布事件,通过publishEvent()方法发送事件(Spring容器本身就是事件发布者)。
  • 事件监听器(ApplicationListener):负责监听事件,实现该接口或标注@EventListener注解,当事件发布时,执行对应的处理逻辑。

示例:用户注册成功后,发布UserRegisterEvent事件,监听者监听该事件,执行发送短信、记录日志等操作,与注册逻辑解耦。

4. Spring的类型转换机制是什么?(了解,高频补充)

答案:

① 定义:Spring提供统一的类型转换机制,用于将一种数据类型转换为另一种数据类型(如String→Integer、String→Date),解决配置文件中参数类型与Java类字段类型不匹配的问题。

② 核心组件:

  • Converter<S, T>:最基础的类型转换器,将S类型转换为T类型(如StringToIntegerConverter)。
  • ConverterFactory:用于批量转换(如将String转换为所有Number类型)。
  • Formatter:用于字符串与对象的双向转换(支持格式化,如Date与String的格式化转换),常用于Web场景。

③ 自定义类型转换:实现Converter接口,重写convert()方法,注册到Spring容器(通过@Bean或ConversionServiceFactoryBean)。

5. Spring Boot的自动配置原理和Spring的自动配置有什么区别?(结合Spring Boot,高频)

答案:

① 核心区别:Spring的自动配置是「基础能力」,Spring Boot的自动配置是「在Spring基础上的增强和简化」,本质是对Spring自动配置的封装,让配置更便捷。

② 具体区别:

  • Spring自动配置:需手动开启(如@EnableAutoConfiguration),需手动引入依赖,自动配置类通过spring.factories文件注册,配置灵活但繁琐。
  • Spring Boot自动配置:无需手动开启(@SpringBootApplication包含@EnableAutoConfiguration),通过Starter起步依赖自动引入相关依赖,自动配置类通过spring.factories(1.x/2.x)或AutoConfiguration.imports(3.x)注册,默认配置更完善,无需手动配置,实现“开箱即用”。

总结:Spring Boot的自动配置 = Spring自动配置 + 起步依赖 + 约定优于配置,简化了Spring应用的搭建和配置。

6. Spring的异步处理如何实现?@Async注解的使用注意事项是什么?(高频)

答案:

① 实现方式:Spring通过@Async注解实现异步处理,配合@EnableAsync注解开启异步支持,让方法异步执行(无需等待方法执行完成,提升应用响应速度)。

② 核心步骤:

  1. 在配置类上标注@EnableAsync,开启异步支持。
  2. 在需要异步执行的方法上标注@Async。

③ 使用注意事项(3点核心):

  • @Async标注的方法必须是public方法(Spring异步仅对public方法生效)。
  • 同一类中,异步方法A调用异步方法B,B的异步失效(原因同AOP,调用的是目标对象自身方法)。
  • 异步方法的返回值建议用Future,用于获取异步方法的执行结果;若无需返回值,返回void即可。