Spring面试高频题:从基础到源码,通俗拆解+避坑指南

本文拒绝晦涩难懂的源码堆砌,用"通俗类比+细节拆解+面试真题"的方式,把Spring面试中最常考、最易踩坑的知识点讲透,不管是初级面试还是中级面试,都能直接套用,帮你轻松拿下Spring相关考题。

一、Spring基础认知:先搞懂"是什么、为什么用"

面试第一道Spring题,大概率是"你对Spring的理解是什么?",很多人只会说"Spring是一个轻量级框架",这样的回答太敷衍,根本拿不到高分。核心是要讲清"定位+核心价值+核心特性",用通俗的话让面试官明白你真的懂。

1.1 核心定位(通俗类比)

Spring的核心定位是:一个轻量级的IoC容器 + AOP框架,本质是"帮我们管理对象、简化开发"。

通俗类比:以前我们写代码,需要自己new对象、管理对象之间的依赖(比如Service里需要Dao,就要自己new DaoImpl()),就像"自己做饭"------买菜、洗菜、做饭、洗碗全要自己来;而Spring就像"一个全能管家",你告诉它需要什么对象,它会帮你创建对象、管理对象之间的依赖,你只需要专注于"业务逻辑"(就像只负责吃,不用管做饭和洗碗)。

1.2 为什么要用Spring?(面试高频)

核心是"解耦+简化开发",具体有4个核心优势,面试时要结合实际开发场景说,不要只背理论:

  1. 解耦:通过IoC容器管理对象,消除对象之间的硬依赖(不用再自己new对象),比如Service和Dao的依赖,由Spring注入,后续替换Dao实现类,不用修改Service代码。

  2. 简化开发:Spring提供了大量的工具类和封装(比如事务管理、异常处理),不用重复写冗余代码,比如以前手动管理事务需要try-catch,Spring一句注解就能搞定。

  3. 高可扩展性:支持AOP、自定义Bean、插件扩展(比如集成MyBatis、Redis),能轻松适配不同的业务场景。

  4. 生态完善:Spring家族有Spring Boot(快速开发)、Spring Cloud(微服务)、Spring Security(安全)等,一套生态能覆盖后端开发的所有场景,降低技术选型成本。

1.3 核心概念拆解(必记,面试基础)

Spring的核心概念不多,但每个都要吃透,尤其是"IoC、DI、AOP、Bean",面试必问,用通俗的话拆解如下:

  • IoC(控制反转):核心是"反转对象的创建权"------以前对象由开发者自己创建(new),现在由Spring容器创建并管理,开发者只需要"被动接收"对象。比如以前你需要new UserService(),现在Spring帮你创建好,你直接用@Autowired注入就能用。

  • DI(依赖注入):IoC的具体实现方式,指Spring容器将依赖的对象"注入"到需要的对象中。比如UserService需要UserDao,Spring会把UserDao对象注入到UserService中,不用UserService自己创建。(补充:IoC是思想,DI是实现,面试时要区分开,避免混淆)

  • Bean:Spring容器管理的对象,就是"被Spring管家照顾的对象",比如UserService、UserDao,只要交给Spring管理,就是Bean。

  • AOP(面向切面编程):核心是"在不修改原有代码的前提下,增强业务逻辑",比如日志记录、事务管理、权限校验,这些通用功能可以做成"切面",织入到业务方法中,不用在每个业务方法里重复写。

1.4 Spring、Spring Boot、Spring Cloud的区别(高频易混题)

很多面试会问三者的关系,很多人分不清,用"通俗类比+表格"讲清,一看就懂:

类比:Spring是"地基",Spring Boot是"精装修的房子",Spring Cloud是"小区配套"------地基(Spring)打好了,才能盖房子(Spring Boot),房子盖好后,需要小区的配套设施(Spring Cloud)才能满足日常需求。

|--------------|----------------------------------------|--------------------------|
| 框架 | 核心作用 | 通俗理解 |
| Spring | 提供IoC、AOP核心能力,是基础框架,负责管理对象和依赖 | 地基,所有Spring家族的基础 |
| Spring Boot | 基于Spring,简化配置(自动配置),快速搭建项目,无需手动整合依赖 | 精装修房子,拎包入住,不用自己装修(配置) |
| Spring Cloud | 基于Spring Boot,提供微服务相关组件(注册中心、网关、配置中心等) | 小区配套,比如快递柜、超市,满足微服务的协同需求 |

总结:Spring是基础,Spring Boot简化Spring开发,Spring Cloud基于Spring Boot实现微服务,三者不是替代关系,而是互补关系。

二、Spring核心原理:面试深挖考点,吃透不慌

基础概念问完,面试官一定会深挖核心原理,这部分是"拉开差距"的关键,重点掌握3个核心:IoC容器原理、Bean生命周期、AOP原理,每个都要讲清"原理+细节+面试考点"。

2.1 IoC容器原理(面试高频深挖)

核心问题:Spring IoC容器是怎么创建和管理Bean的?流程是什么?

通俗理解:IoC容器就像"Spring的对象仓库",它的核心工作是"扫描Bean→创建Bean→注入依赖→管理Bean",整个流程分为3步,细节要记牢(面试时要能说出完整流程):

核心流程(3步走):
  1. 扫描Bean(定位):Spring启动时,会扫描指定包下(比如@ComponentScan注解指定的包)所有带有@Component、@Service、@Controller、@Repository注解的类,将这些类标记为"需要被管理的Bean",并记录类的信息(比如类名、依赖关系)。

  2. 创建Bean(初始化):Spring根据扫描到的类信息,通过"反射"创建Bean对象(默认是单例,后续会讲单例和多例),创建过程中会执行Bean的构造方法、初始化方法。

  3. 注入依赖(装配):Bean创建完成后,Spring会检查Bean是否有依赖(比如@Autowired注解的属性),如果有,就从容器中找到对应的Bean,注入到当前Bean中,完成依赖装配。

关键细节(面试避坑点):
  • IoC容器的核心接口:BeanFactoryApplicationContext,两者的区别是面试高频题,记清楚:

    • BeanFactory:最基础的IoC容器,采用"懒加载"(只有获取Bean时才创建Bean),功能简单,适合简单场景。

    • ApplicationContext:BeanFactory的子接口,采用"预加载"(Spring启动时就创建所有单例Bean),提供更多功能(比如国际化、事件监听、资源加载),实际开发中几乎都用ApplicationContext(比如ClassPathXmlApplicationContext、AnnotationConfigApplicationContext)。

  • Bean的创建方式:除了注解扫描,还有3种方式,面试可能会问:

    • XML配置:<bean id="userService" class="com.example.UserService"/>(传统方式,现在很少用,但面试可能考)。

    • @Bean注解:在配置类中用@Bean注解方法,方法返回值就是Bean(比如整合第三方框架时常用)。

    • 工厂方法:通过实现FactoryBean接口,自定义Bean的创建逻辑(进阶考点,了解即可)。

2.2 Bean的生命周期(面试重中之重)

核心问题:Spring Bean从创建到销毁,整个生命周期有哪些步骤?(很多人记不全,用"通俗步骤+细节"帮你记住)

通俗理解:Bean的生命周期就像"一个人的一生"------从"出生"(创建)→"成长"(初始化)→"工作"(被使用)→"死亡"(销毁),整个流程分为7步,每一步都有面试考点:

Bean生命周期完整步骤(带细节):
  1. 实例化Bean(出生):Spring通过反射调用Bean的无参构造方法,创建Bean对象(此时Bean只是一个空对象,属性还未赋值)。 注意:如果Bean没有无参构造方法,会报错(InstantiationException),面试常考"Bean实例化失败的原因",这是一个坑。

  2. 设置Bean属性(赋值):Spring将容器中对应的依赖,注入到Bean的属性中(比如@Autowired注入的UserDao,就是在这一步赋值的)。

  3. 调用BeanNameAware的setBeanName方法:如果Bean实现了BeanNameAware接口,Spring会将Bean的id(名称)传入该方法,让Bean知道自己在容器中的名称。

  4. 调用BeanFactoryAware的setBeanFactory方法:如果Bean实现了BeanFactoryAware接口,Spring会将IoC容器本身传入该方法,让Bean能直接操作容器(实际开发中很少用,但面试会问)。

  5. 调用InitializingBean的afterPropertiesSet方法:如果Bean实现了InitializingBean接口,Spring会在Bean属性赋值完成后,调用该方法,用于执行Bean的初始化逻辑(比如初始化数据库连接)。 补充:也可以用@PostConstruct注解替代该接口,更简洁(实际开发常用)。

  6. Bean就绪(工作):此时Bean已经创建完成、属性赋值完成、初始化完成,能够被开发者使用(比如通过@Autowired注入后调用方法)。

  7. Bean销毁(死亡):当Spring容器关闭时,会调用Bean的销毁方法,释放资源(比如关闭数据库连接)。 补充:可以通过实现DisposableBean接口的destroy方法,或用@PreDestroy注解,定义销毁逻辑。

关键细节(面试避坑点):
  • 初始化方法的执行顺序:@PostConstruct注解 > InitializingBean接口的afterPropertiesSet方法 > XML配置中的init-method属性(如果同时存在,按这个顺序执行)。

  • 销毁方法的执行顺序:@PreDestroy注解 > DisposableBean接口的destroy方法 > XML配置中的destroy-method属性。

  • 单例Bean和多例Bean的生命周期区别:单例Bean的生命周期和容器一致(容器启动创建,容器关闭销毁);多例Bean每次被获取时创建,使用完后由JVM垃圾回收(Spring不管理多例Bean的销毁)。

2.3 AOP原理(面试高频,必吃透)

核心问题:Spring AOP是怎么实现的?动态代理有两种方式,区别是什么?

通俗理解:AOP就像"给业务方法加一层'滤镜'"------不用修改业务方法的代码,就能在方法执行前、执行中、执行后添加通用逻辑(比如日志、事务),而这层"滤镜"的实现核心,就是动态代理

AOP核心原理:动态代理(两种方式)

Spring AOP的底层实现有两种动态代理方式,Spring会自动选择,面试时要能说出两种方式的区别、适用场景,还要知道"为什么Spring会这么选":

|-----------|-------------------------------------------------|--------------------|---------------------------------------|
| 动态代理方式 | 实现原理 | 适用场景 | 优点/缺点 |
| JDK动态代理 | 基于Java反射机制,通过Proxy类创建目标对象的接口代理(只能代理实现了接口的类) | 目标类实现了接口 | 优点:原生支持,创建速度快;缺点:只能代理接口,无法代理类方法 |
| CGLIB动态代理 | 基于字节码技术,生成目标类的子类(不用实现接口,直接代理类) | 目标类没有实现接口,或需要代理类方法 | 优点:无需接口,执行速度快;缺点:不能代理final类/方法,创建速度较慢 |

AOP核心流程(通俗拆解):
  1. 定义切面(Aspect):比如日志切面、事务切面,里面包含通知(增强逻辑)和切入点(哪些方法需要被增强)。

  2. Spring扫描切面,解析切入点表达式,找到需要被增强的目标方法。

  3. Spring根据目标类是否实现接口,选择对应的动态代理方式(JDK/CGLIB),创建目标对象的代理对象。

  4. 当调用目标方法时,实际调用的是代理对象的方法,代理对象会先执行切面的通知逻辑(比如前置通知),再执行目标方法,最后执行后置通知,完成增强。

关键细节(面试避坑点):
  • AOP的5种通知类型(记牢,面试常考):

    • 前置通知(@Before):目标方法执行前执行(比如日志记录方法开始时间)。

    • 后置通知(@After):目标方法执行后执行(无论方法是否异常,都会执行,比如记录方法结束时间)。

    • 返回通知(@AfterReturning):目标方法正常返回后执行(比如记录方法返回值)。

    • 异常通知(@AfterThrowing):目标方法抛出异常后执行(比如记录异常信息)。

    • 环绕通知(@Around):包裹目标方法,可在方法执行前、执行中、执行后自定义逻辑(最灵活,比如事务管理)。

  • Spring Boot 2.x后,默认使用CGLIB动态代理(即使目标类实现了接口),可以通过配置spring.aop.proxy-target-class=false,强制使用JDK动态代理。

  • AOP的坑:final方法、private方法无法被增强(因为CGLIB无法生成final类的子类,JDK动态代理也无法代理private方法),面试常考"为什么我的AOP不起作用",这是常见坑。

三、Spring面试高频题:真题+通俗解析+避坑

这部分是核心,整理了Spring面试中最常考的10道题,每道题都包含"真题+通俗解析+避坑点",直接背就能用,避免面试时答非所问。

3.1 真题1:Spring IoC和DI的区别是什么?(初级必问)

解析(通俗版):IoC是"思想",DI是"实现";IoC说的是"反转对象创建权",让Spring管理对象;DI说的是"反转依赖注入",让Spring把依赖的对象注入到需要的地方。

比如:你需要UserService,Spring帮你创建UserService(这是IoC);UserService需要UserDao,Spring把UserDao注入到UserService(这是DI)。

避坑点:不要说"IoC和DI是一回事",也不要混淆两者的关系,记住"IoC是思想,DI是实现"即可。

3.2 真题2:Spring Bean的作用域有哪些?(初级必问)

解析(重点记前2个,后3个了解即可):Bean的作用域指的是"Bean在IoC容器中的创建方式和生命周期",共5种:

  1. singleton(单例,默认):Spring容器中只有一个Bean实例,所有请求都共享这个实例(比如Service、Dao,实际开发中最常用)。 避坑点:单例Bean是线程不安全的!如果Bean中有成员变量,多线程并发修改会出问题,解决方案:不用成员变量,或用ThreadLocal、锁机制。

  2. prototype(多例):每次获取Bean时,Spring都会创建一个新的Bean实例(比如Controller,Spring Boot中Controller默认是单例,但若有成员变量,可设置为多例)。 避坑点:多例BeanSpring不管理销毁,需要自己手动释放资源。

  3. request(请求域):每个HTTP请求创建一个新的Bean,仅在当前请求中有效(Web场景专用)。

  4. session(会话域):每个HTTP会话创建一个新的Bean,仅在当前会话中有效(Web场景专用)。

  5. globalSession(全局会话域):仅在Portlet(门户)场景中有效,现在几乎不用。

3.3 真题3:@Autowired和@Resource的区别是什么?(中级必问)

解析(通俗版):两者都是用来注入依赖的,但来源、匹配方式、支持的参数不同,用表格对比最清晰:

|------|-------------------------------|------------------------------------|
| 对比维度 | @Autowired | @Resource |
| 来源 | Spring自带注解 | JDK自带注解(javax.annotation.Resource) |
| 匹配方式 | 先按类型(byType)匹配,再按名称(byName)匹配 | 先按名称(byName)匹配,再按类型(byType)匹配 |
| 支持参数 | 支持required=false(注入失败不报错) | 支持name属性(指定注入Bean的名称) |
| 适用场景 | Spring项目中通用,推荐使用 | 需要跨框架(比如Spring+JDK),或指定Bean名称时使用 |

避坑点:@Autowired默认按类型匹配,如果容器中有多个同类型的Bean,会报错,解决方案:用@Qualifier注解指定Bean名称(比如@Autowired + @Qualifier("userDaoImpl1"))。

3.4 真题4:Spring事务管理的核心原理是什么?(中级必问)

解析(通俗版):Spring事务管理的核心是"AOP+动态代理",把事务逻辑(开启事务、提交事务、回滚事务)做成切面,织入到业务方法中,无需手动写事务代码。

关键细节(必记):

  • 事务的传播机制(面试高频,记清楚7种,重点记前4种):

    • REQUIRED(默认):如果当前有事务,就加入事务;如果没有,就创建一个新事务(比如Service方法调用Dao方法,Dao的事务会加入Service的事务)。

    • SUPPORTS:如果当前有事务,就加入事务;如果没有,就以非事务方式执行(很少用)。

    • MANDATORY:必须在事务中执行,否则报错(很少用)。

    • REQUIRES_NEW:无论当前是否有事务,都创建一个新事务,原有事务暂停(比如转账时,日志记录的事务要独立,即使转账失败,日志也要记录)。

    • NOT_SUPPORTED:以非事务方式执行,如果当前有事务,就暂停事务(很少用)。

    • NEVER:以非事务方式执行,如果当前有事务,就报错(很少用)。

    • NESTED:如果当前有事务,就创建一个嵌套事务(子事务依赖父事务,父事务回滚,子事务也回滚;子事务回滚,父事务不回滚)。

  • 事务的隔离级别(对应数据库的隔离级别,记4种):

    • DEFAULT(默认):继承数据库的隔离级别(MySQL默认是REPEATABLE READ)。

    • READ_UNCOMMITTED:读未提交,会出现脏读、不可重复读、幻读(最低级别,不用)。

    • READ_COMMITTED:读已提交,避免脏读,会出现不可重复读、幻读(Oracle默认)。

    • REPEATABLE READ:可重复读,避免脏读、不可重复读,会出现幻读(MySQL默认)。

    • SERIALIZABLE:串行化,避免所有问题,但性能极差(不用)。

避坑点:Spring事务默认只回滚RuntimeException(运行时异常), checked异常(比如IOException)不会回滚,解决方案:在@Transactional注解中指定rollbackFor={Exception.class},让所有异常都回滚。

3.5 真题5:Spring Boot自动配置的原理是什么?(中级必问)

解析(通俗版):Spring Boot的核心是"自动配置",不用手动写XML配置,就能整合各种框架,核心原理是"@EnableAutoConfiguration注解 + 自动配置类 + 条件注解"。

核心流程(3步):

  1. Spring Boot启动时,@SpringBootApplication注解会包含@EnableAutoConfiguration注解,该注解会开启自动配置。

  2. @EnableAutoConfiguration会扫描Spring Boot内置的自动配置类(位于spring-boot-autoconfigure.jar的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中)。

  3. 每个自动配置类(比如DataSourceAutoConfiguration、MyBatisAutoConfiguration)都会通过条件注解(比如@ConditionalOnClass、@ConditionalOnMissingBean)判断是否需要生效,生效后会自动配置对应的Bean(比如数据源Bean)。

避坑点:自动配置类生效的条件是"引入对应的依赖",比如要让MyBatisAutoConfiguration生效,必须引入mybatis-spring-boot-starter依赖,否则自动配置不生效。

3.6 真题6:Spring Bean的循环依赖怎么解决?(高级必问)

解析(通俗版):循环依赖就是"两个Bean互相依赖",比如A依赖B,B依赖A,Spring默认能解决"单例Bean的循环依赖",核心是"三级缓存"。

三级缓存(记清楚,面试必问):

  • 一级缓存(singletonObjects):存储已经创建完成、初始化完成的单例Bean(最终供开发者使用)。

  • 二级缓存(earlySingletonObjects):存储已经实例化、但未初始化的单例Bean(用于解决循环依赖,提前暴露Bean)。

  • 三级缓存(singletonFactories):存储Bean的工厂方法,用于创建Bean的早期实例(如果Bean有AOP增强,会在这里生成代理对象)。

循环依赖解决流程(A依赖B,B依赖A):

  1. Spring创建A,实例化A(无参构造),将A的工厂方法放入三级缓存,此时A未初始化、未赋值。

  2. A需要注入B,Spring去创建B,实例化B,将B的工厂方法放入三级缓存,此时B未初始化、未赋值。

  3. B需要注入A,Spring从三级缓存中获取A的工厂方法,创建A的早期实例(未初始化),放入二级缓存,删除三级缓存中的A。

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

  5. A注入B,完成初始化,放入一级缓存,删除三级缓存中的B。

避坑点:Spring只能解决"单例Bean的循环依赖",多例Bean的循环依赖无法解决(会报错);如果Bean有构造方法注入,也无法解决循环依赖(因为构造方法注入会在实例化时就需要依赖,而三级缓存是在实例化后才暴露)。

3.7 真题7:Spring AOP和AspectJ的区别是什么?(高级必问)

解析(通俗版):两者都是AOP框架,但实现方式、适用场景不同,核心区别是"织入时机不同":

  • Spring AOP :基于动态代理(JDK/CGLIB),运行时织入(Spring启动后,创建代理对象时织入切面),只能增强Spring管理的Bean,功能相对简单,适合大多数业务场景。

  • AspectJ :基于字节码修改,编译时织入(编译Java文件时,就将切面织入到目标类中),可以增强任何类(不限于Spring Bean),功能强大,但配置复杂,适合需要复杂AOP增强的场景。

避坑点:Spring AOP默认不支持AspectJ的切入点表达式(比如within、execution的复杂用法),需要引入aspectjweaver依赖才能支持。

3.8 真题8:@Component和@Bean的区别是什么?(初级必问)

解析(通俗版):两者都是用来创建Bean的,但作用范围、使用场景不同:

  • @Component :作用于类上,是"注解驱动"的Bean创建方式,Spring会扫描该类,自动创建Bean(默认Bean名称是类名首字母小写),适合自己写的类。

  • @Bean :作用于方法上,是"手动配置"的Bean创建方式,方法的返回值就是Bean,适合整合第三方框架(比如整合Redis、MyBatis,第三方类无法添加@Component注解)。

例子:自己写的UserService,用@Component;整合Redis的RedisTemplate,用@Bean注解方法,返回RedisTemplate对象。

3.9 真题9:Spring Boot的 Starter 是什么?(初级必问)

解析(通俗版):Starter是Spring Boot的"依赖包集合",本质是"简化依赖配置",比如mybatis-spring-boot-starter,里面包含了MyBatis、Spring MyBatis、数据源等相关依赖,不用手动一个个引入,只需引入一个Starter,就能整合MyBatis。

核心作用:减少依赖配置,实现"一键整合",让开发者专注于业务开发,不用关注依赖的版本兼容(Starter会帮我们管理依赖版本,避免版本冲突)。

避坑点:不同的Starter对应不同的框架,比如spring-boot-starter-web是整合Spring MVC的,spring-boot-starter-jdbc是整合JDBC的,不要引入错误的Starter。

3.10 真题10:Spring Bean的初始化和销毁方法有哪些方式?(初级必问)

解析(记3种常用方式,按优先级排序):

  1. 注解方式(最常用):@PostConstruct(初始化方法)、@PreDestroy(销毁方法),作用于方法上,方法名可以自定义。

  2. 接口方式:实现InitializingBean接口(重写afterPropertiesSet方法,初始化)、实现DisposableBean接口(重写destroy方法,销毁)。

  3. XML配置方式(传统方式,现在很少用):在XML中配置init-method和destroy-method属性,指定初始化和销毁方法。

避坑点:初始化方法会在Bean属性赋值完成后执行,销毁方法会在Spring容器关闭时执行;多例Bean的销毁方法不会被Spring调用,需要手动处理。

四、Spring实战避坑:生产环境高频问题及解决方案

很多开发者在本地测试Spring项目时一切正常,但部署到生产环境后,会遇到各种问题,以下是生产环境中最常见的5个坑,附具体解决方案,面试时如果能说出这些,会加分很多。

4.1 坑1:单例Bean线程不安全(最常见)

现象:多线程并发调用单例Bean的方法时,出现数据错乱(比如成员变量被篡改)。

原因:单例Bean在容器中只有一个实例,多线程共享该实例,若Bean中有可变成员变量,会出现线程安全问题。

解决方案:

  1. 避免使用可变成员变量(优先推荐,比如把变量放在方法内部,作为局部变量)。

  2. 使用ThreadLocal存储成员变量(每个线程有独立的变量副本,互不影响)。

  3. 将Bean改为多例(prototype),但会增加Bean创建的开销,谨慎使用。

  4. 使用锁机制(比如synchronized),但会影响性能,适合并发量低的场景。

4.2 坑2:Spring事务不生效(高频坑)

现象:方法上添加了@Transactional注解,但事务没有回滚(比如报错后,数据库数据依然被修改)。

常见原因及解决方案:

  1. 注解标注错误:@Transactional注解标注在非public方法上(Spring事务只对public方法生效),改为public方法。

  2. 异常类型不匹配:Spring默认只回滚RuntimeException,若方法抛出checked异常(比如IOException),需在@Transactional中添加rollbackFor={Exception.class}。

  3. 自调用问题:同一个Bean中,无事务的方法调用有事务的方法,事务不生效(因为自调用不会经过代理对象),解决方案:将有事务的方法抽成独立的Bean,或通过AopContext获取代理对象调用。

  4. 未开启事务管理:Spring Boot需要@EnableTransactionManagement注解开启事务管理(Spring Boot 2.x后,若引入spring-boot-starter-jdbc,会自动开启,无需手动添加)。

4.3 坑3:AOP不起作用(高频坑)

现象:编写了AOP切面,但目标方法执行时,切面逻辑没有执行。

常见原因及解决方案:

  1. 切面类没有添加@Aspect注解,或没有被Spring扫描到(需添加@Component注解,确保切面类在@ComponentScan扫描范围内)。

  2. 切入点表达式错误:没有匹配到目标方法(比如包名、方法名写错),建议用execution表达式精准匹配(比如execution(* com.example.service.*.*(..)))。

  3. 目标方法是final或private方法:CGLIB无法代理final方法,JDK无法代理private方法,修改方法为非final、public方法。

  4. 自调用问题:同一个Bean中,方法自调用,AOP不生效(原因同事务自调用),解决方案:抽成独立Bean或获取代理对象调用。

4.4 坑4:循环依赖导致Bean创建失败

现象:Spring启动时报错"Circular reference"(循环依赖),Bean创建失败。

常见原因及解决方案:

  1. 多例Bean循环依赖:Spring无法解决多例Bean的循环依赖,将多例Bean改为单例Bean,或打破循环依赖(比如通过第三方Bean中转)。

  2. 构造方法注入循环依赖:将构造方法注入改为@Autowired字段注入或setter方法注入。

  3. 复杂循环依赖:通过@Lazy注解延迟加载依赖(比如@Autowired @Lazy private B b;),让Bean在使用时才创建,打破循环依赖。

4.5 坑5:自动配置不生效

现象:引入了Starter依赖,但自动配置的Bean没有被创建(比如引入mybatis-spring-boot-starter,但RedisTemplate无法注入)。

常见原因及解决方案:

  1. 引入的Starter错误:比如要整合MyBatis,却引入了spring-boot-starter-jdbc,替换为正确的Starter。

  2. 自动配置类被排除:比如@SpringBootApplication(exclude = DataSourceAutoConfiguration.class),导致数据源自动配置不生效,删除exclude属性。

  3. 条件注解不满足:比如DataSourceAutoConfiguration需要引入数据源依赖(mysql-connector-java),且配置了spring.datasource相关参数,补充依赖和配置。

五、总结:Spring学习与面试建议

Spring面试的核心,不是背源码,而是"理解原理+掌握细节+结合实战",很多开发者面试翻车,不是因为不懂,而是因为"懂皮毛,不懂细节",结合自身经验,给大家以下学习和面试建议:

  1. 入门阶段:先掌握基础概念(IoC、DI、AOP、Bean),学会用Spring Boot快速搭建项目,熟悉常用注解(@Component、@Autowired、@Transactional),能完成简单的业务开发。

  2. 进阶阶段:吃透核心原理(Bean生命周期、AOP动态代理、IoC容器流程、事务管理),重点记细节和避坑点,能解释面试题的底层逻辑,而不是死记硬背。

  3. 面试准备:重点掌握本文整理的10道高频题,结合实战避坑点,面试时不要只说理论,要结合实际开发场景(比如"我在项目中遇到过事务不生效的问题,原因是XXX,解决方案是XXX"),这样更有说服力。

最后提醒:Spring的知识点看似多,但核心围绕"IoC和AOP"展开,只要抓住这两个核心,再逐步拆解细节、掌握避坑点,就能轻松应对Spring面试。

希望本文能帮你吃透Spring面试知识点,少走弯路,面试顺利拿下offer!如果觉得有收获,欢迎点赞、收藏,也可以在评论区分享你的Spring面试经验~

相关推荐
xlp666hub2 小时前
从零点亮 RK3568 的 LED:设备树,平台总线,现代gpio子系统全解析(附完整代码)
linux·面试
huaweichenai2 小时前
java的时间操作介绍
java·开发语言
毕设源码-朱学姐2 小时前
【开题答辩全过程】以 基于SpringBoot+Vue的百货商品进出货平台为例,包含答辩的问题和答案
java·spring boot·后端
左左右右左右摇晃2 小时前
Java笔记——包装类(自动拆装箱)
java·笔记·python
森林里的程序猿猿2 小时前
Java深入理解并发、线程、与等待通知机制(一)
java
夜空下的星2 小时前
springboot实现Minio大文件分片下载
java·spring boot·后端
Huangxy__3 小时前
接口的的的~
java
Fairy要carry3 小时前
面试08-“生产者-消费者” 模型实现并发 Agent
python·面试
廋到被风吹走3 小时前
【MySql】超时问题分析
java·数据库·mysql