Spring框架是Java后端开发的基石,IoC和AOP是Spring最核心的两大特性。阿里、字节、美团等大厂面试都会深入考察,因为它直接反映你是停留在"会用注解"的水平,还是真正理解框架的设计思想和底层机制。
先讲IoC的核心思想:控制反转,把对象的创建和依赖管理交给Spring容器,而不是程序员手动new。依赖注入(DI)是IoC的具体实现方式。
说明容器原理:BeanFactory是基础容器,ApplicationContext是增强版,支持事件、国际化、AOP等。Bean的配置方式包括XML、注解(@Component、@Service等)和Java配置(@Configuration + @Bean)。
重点讲Bean生命周期:实例化 → 属性填充 → Aware接口回调 → BeanPostProcessor前置处理 → @PostConstruct → InitializingBean → BeanPostProcessor后置处理 → 就绪 → @PreDestroy → DisposableBean。
再讲AOP:面向切面编程,将日志、事务、权限等横切关注点与业务逻辑分离。
说明AOP实现机制:Spring AOP基于动态代理,对接口使用JDK动态代理,对类使用CGLIB生成子类。核心概念包括切面、通知(前置/后置/环绕)、切点、连接点。
Spring IoC即控制反转,核心是将对象的创建、依赖关系的维护交给Spring容器管理。容器启动时读取配置(注解或XML),通过反射创建Bean实例,并按照依赖关系自动注入。注入方式有三种:构造器注入(推荐)、Setter注入、字段注入(@Autowired)。Bean的生命周期比较复杂:首先通过反射实例化,然后填充属性(依赖注入),接着回调各种Aware接口(如BeanNameAware、ApplicationContextAware),再经过BeanPostProcessor的前置处理,执行@PostConstruct初始化方法,最后经过BeanPostProcessor的后置处理后Bean才真正可用。AOP就是在BeanPostProcessor后置处理阶段实现的。Spring AOP底层使用动态代理:如果目标对象实现了接口,默认使用JDK动态代理(基于Proxy和InvocationHandler);否则使用CGLIB(通过生成目标类的子类来实现代理)。以@Transactional为例,Spring在Bean初始化后检查是否匹配事务切点,如果匹配就生成代理对象替换原始Bean。调用方法时,代理先开启事务,执行目标方法,正常则提交,异常则回滚。需要注意self-invocation问题:类内部方法直接调用this.method()不会走代理,因此AOP不生效,解决办法是注入自身代理或使用AopContext.currentProxy()。循环依赖方面,Spring通过三级缓存解决单例Bean的循环依赖:singletonObjects存完整Bean,earlySingletonObjects存早期引用,singletonFactories存ObjectFactory用于创建代理。
一定要能画出Bean生命周期流程图,面试官经常让你现场画。
理解AOP的self-invocation问题是加分点,很多人不知道类内调用AOP会失效。
三级缓存解决循环依赖的原理是高频追问,提前准备好。
面试时如果Bean生命周期顺序记混了,即答侠可以实时提示正确的流程。
当A依赖B、B依赖A时:创建A → 将A的ObjectFactory放入三级缓存 → 填充属性发现需要B → 创建B → B填充属性需要A → 从三级缓存获取A的早期引用 → B创建完成 → A完成属性填充。三级缓存的关键作用是延迟AOP代理的创建。
@Autowired是Spring注解,默认按类型注入,配合@Qualifier按名称。@Resource是JSR-250标准注解,默认按名称注入,找不到再按类型。推荐使用构造器注入。
JDK动态代理基于接口,通过Proxy.newProxyInstance生成代理对象,要求目标类实现接口。CGLIB通过ASM字节码框架生成目标类的子类,不要求实现接口,但无法代理final方法。Spring Boot 2.x默认使用CGLIB。