Spring核心原理与面试指南

这是一个关于 Spring 框架核心原理和常见面试问题的全面总结。无论是准备面试还是深入理解 Spring,这份指南都会对你大有裨益。


Part 1: Spring 核心原理深度解析

Spring 的核心可以归结为两大基石:IoC (控制反转) 和 AOP (面向切面编程)。几乎所有其他特性都是建立在它们之上的。

一、IoC (控制反转) / DI (依赖注入)

1. 核心思想:

  • 控制反转 (IoC): 将对象的创建、组装、管理的控制权从应用程序代码反转到外部容器(Spring IoC Container)。简单说,以前是new Object(),现在是由 Spring 容器给你对象。

  • 依赖注入 (DI): 是 IoC 的一种实现方式。对象之间的依赖关系由容器在运行时注入(通过构造函数、Setter方法等),而不是由对象自己创建或查找。

2. 核心组件:

  • BeanDefinition: Spring 容器内部用于描述一个 Bean 的元数据对象,它定义了如何创建这个 Bean(类名、作用域、是否懒加载、依赖关系等)。

  • BeanFactory: IoC 容器的最底层接口,提供了基础的 DI 功能。(功能简单,一般用于资源受限的环境)

  • ApplicationContext: BeanFactory 的子接口,是更高级的容器,提供了更多企业级功能,如:

    • 国际化的消息访问(MessageSource)

    • 资源访问(如URL和文件)

    • 事件发布机制(ApplicationEventPublisher)

    • 与 Web 应用的集成(WebApplicationContext)

    • 它是我们最常用的容器。(例如:ClassPathXmlApplicationContextAnnotationConfigApplicationContext

3. 容器工作流程 (Bean 的生命周期):
这是面试的超高频问题!一个 Bean 从无到有要经历很多步骤。

  1. 实例化 (Instantiate): 容器通过反射调用构造方法创建 Bean 的实例。

  2. 属性填充 (Populate properties): 容器注入 Bean 所依赖的其他 Bean(依赖注入)。

  3. 设置 BeanName/Aware: 如果 Bean 实现了 BeanNameAware 等 Aware 接口,容器会回调相关方法,让 Bean 感知到自身在容器中的 ID 和资源。

  4. BeanPostProcessor (前置处理): 调用所有 BeanPostProcessor 的 postProcessBeforeInitialization 方法。

  5. 初始化 (Initialization):

    • 如果 Bean 实现了 InitializingBean 接口,执行 afterPropertiesSet() 方法。

    • 如果配置了自定义的 init-method 方法,执行该方法。

  6. BeanPostProcessor (后置处理): 调用所有 BeanPostProcessor 的 postProcessAfterInitialization 方法。AOP 代理就是在此阶段生成的!

  7. 使用 (Ready): Bean 已经完全创建,存放在容器中,可以被应用程序使用。

  8. 销毁 (Destruction):

    • 容器关闭时,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。

    • 如果配置了自定义的 destroy-method 方法,执行该方法。

二、AOP (面向切面编程)

1. 核心思想:
将那些与核心业务逻辑无关但又遍布在代码各处的功能(称为横切关注点,如日志、事务、安全等)抽取出来,形成独立的切面(Aspect),然后在需要的地方织入(Weaving) 这些切面。从而实现关注点的分离,提高代码的复用性和可维护性。

2. 核心概念:

  • Aspect (切面): 横切关注点的模块化,它是一个类,包含 Advice 和 Pointcut

  • Joinpoint (连接点): 程序执行过程中一个明确的点,如方法调用、异常抛出。在 Spring AOP 中,连接点总是代表方法的执行

  • Advice (通知): 切面在特定连接点上执行的动作。主要类型有:

    • @Before: 方法执行前

    • @AfterReturning: 方法成功返回后

    • @AfterThrowing: 方法抛出异常后

    • @After (finally): 方法执行后(无论成功与否)

    • @Around最强大的通知,环绕连接点,可以控制是否执行方法。

  • Pointcut (切点): 一个表达式,用于匹配哪些连接点需要被切入。Advice 和一个 Pointcut 表达式关联。

  • Weaving (织入): 将切面应用到目标对象并创建新的代理对象的过程。Spring AOP 在运行时通过动态代理完成织入。

3. 实现原理 (动态代理):
Spring AOP 默认使用动态代理技术。根据目标类是否实现接口,选择不同的代理策略:

  • JDK 动态代理: 如果目标类实现了接口,Spring 会使用 JDK 的 java.lang.reflect.Proxy 来创建代理对象。

  • CGLIB 代理: 如果目标类没有实现任何接口,Spring 会使用 CGLIB 库生成目标类的子类作为代理对象。(可以通过配置强制使用 CGLIB)


Part 2: 常见 Spring 面试问题与解答思路

一、IoC/DI 相关

1. 什么是 Spring IoC 和 DI?它们有什么关系?

  • IoC 是一种设计思想,将控制权交给容器。

  • DI 是实现 IoC 的主要技术手段。

  • 关系: DI 是实现 IoC 的方式,IoC 是 DI 的目标。

2. Spring 中 Bean 的作用域(Scope)有哪些?

  • singleton (默认): 每个 IoC 容器中只有一个 Bean 实例。

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

  • request: 每次 HTTP 请求创建一个实例(仅 Web 环境)。

  • session: 每个 HTTP Session 创建一个实例(仅 Web 环境)。

  • application: 每个 ServletContext 生命周期内一个实例(仅 Web 环境)。

  • websocket: 每个 WebSocket 会话内一个实例(仅 Web 环境)。

3. Spring 如何解决循环依赖?
循环依赖:A 依赖 B,B 也依赖 A。
Spring 通过三级缓存来解决单例模式下的Setter注入/字段注入的循环依赖。

  • 一级缓存 (singletonObjects): 存放已经完全初始化好的单例 Bean。(成品)

  • 二级缓存 (earlySingletonObjects): 存放提前暴露的、未完成初始化的 Bean 的引用(早期引用),用于解决循环依赖。(半成品)

  • 三级缓存 (singletonFactories): 存放生成 Bean 早期引用的 ObjectFactory。(工厂)

解决过程(A 和 B 循环依赖):

  1. 创建 A,实例化 A,将 A 的 ObjectFactory 放入三级缓存

  2. 为 A 注入属性时发现依赖 B,于是去创建 B。

  3. 创建 B,实例化 B,将 B 的 ObjectFactory 放入三级缓存

  4. 为 B 注入属性时发现依赖 A,于是从三级缓存中找到 A 的 ObjectFactory,获取 A 的早期引用(并将此引用放入二级缓存,同时从三级缓存删除)。将早期引用 A 注入给 B。

  5. B 完成属性填充、初始化,成为一个完整的 Bean,放入一级缓存。同时清理二、三级缓存。

  6. A 从一级缓存中拿到完整的 B,注入成功,A 也完成后续步骤,放入一级缓存。

注意:构造函数循环依赖无法解决! 因为 Java 要求在构造对象时必须先成功构造其依赖,而对方也处于构造阶段,导致死锁。

二、AOP 相关

1. Spring AOP 和 AspectJ 有什么区别?

特性Spring AOPAspectJ
实现方式基于动态代理(运行时织入)基于字节码操作(编译时/后织入)
功能仅支持方法级别的连接点支持所有连接点(方法、构造器、字段等)
性能运行时稍有开销编译时织入,运行时无代理,性能更高
依赖轻量,仅需 Spring Core需要引入 AspectJ 编译器/织入器
适用场景解决大多数企业应用的横切关注点(事务、日志等)功能更强大,适用于需要更复杂切面编程的场景

2. 介绍一下 Spring 的事务管理?

  • 编程式事务: 在代码中手动使用 TransactionTemplate 或 PlatformTransactionManager 管理事务。(灵活性高,但代码侵入性强)

  • 声明式事务(推荐): 通过 @Transactional 注解或 XML 配置,由 Spring AOP 在背后自动管理事务。

    • 原理: 声明式事务基于 AOP 实现。@Transactional 注解的方法会被事务通知 (TransactionInterceptor) 环绕,在方法执行前开启事务,执行后提交或回滚事务。

    • 传播行为 (Propagation): 定义了一个事务方法被另一个事务方法调用时,事务应该如何传播。常用 REQUIRED(默认,如果当前没有事务,就新建一个;如果已存在,则加入)。

    • 隔离级别 (Isolation): 定义事务与其他并发事务的隔离程度(如 READ_COMMITTED)。

三、其他综合问题

1. Spring 中常用注解有哪些?

  • 声明 Bean: @Component@Service@Controller@Repository@Configuration

  • 注入依赖: @Autowired (按类型), @Resource (按名称,JSR-250), @Inject (JSR-330), @Value

  • 配置相关: @Configuration@Bean@ComponentScan@PropertySource@Profile

  • AOP/事务: @Aspect@Transactional

  • MVC: @RequestMapping@GetMapping@PostMapping@RequestBody@ResponseBody

2. Spring 中的设计模式?

  • 工厂模式: BeanFactory

  • 单例模式: Bean 的默认作用域

  • 代理模式: Spring AOP 的实现

  • 模板方法模式: JdbcTemplateRestTemplate 等,定义了操作骨架,具体步骤由子类实现。

  • 观察者模式: 事件监听机制 (ApplicationEvent 和 ApplicationListener)

  • 适配器模式: Spring MVC 中的 HandlerAdapter

3. Spring 如何整合 MyBatis?
核心是通过 SqlSessionFactoryBean 和 Mapper 扫描。

  1. 配置 DataSource

  2. 配置 SqlSessionFactoryBean,注入 DataSource 和 Mapper XML 文件的位置。

  3. 配置 MapperScannerConfigurer,指定要扫描的 Mapper 接口所在的包。Spring 会为这些接口自动生成代理实现类,并注入到需要的 Service 中。


面试技巧与建议

  1. 不要死记硬背: 理解原理背后的为什么。比如,为什么要有三级缓存?两级行不行?理解每一步的设计意图。

  2. 结合代码: 在回答时,如果能结合简单的代码示例(比如用 @Autowired 注入,或者定义一个 @Aspect),会显得你经验丰富。

  3. 主动深入: 当被问到基础问题时,可以主动向深处引申。例如,被问到 @Autowired,可以接着说它的注入原理、@Qualifier 的作用、与 @Resource 的区别等。

  4. 诚实守信: 如果遇到不会的问题,坦诚承认,并说明自己的学习思路,比胡乱猜测要好得多。

希望这份总结能帮助你更好地理解和准备 Spring 相关的面试!祝你成功!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值