1. Spring面试高频题解析:IoC核心原理与实现
Spring框架的IoC(控制反转)容器是其最核心的特性之一。传统编程中我们需要手动创建对象(new操作),而Spring IoC容器则负责对象的创建和依赖管理。让我们深入分析其实现原理。
1.1 IoC容器的三级缓存机制
Spring通过三级缓存解决循环依赖问题:
- singletonObjects:存放完全初始化好的单例Bean
- earlySingletonObjects:存放早期暴露的Bean(已实例化但未完成依赖注入)
- singletonFactories:存放Bean工厂对象
// 简化版三级缓存实现 public class DefaultSingletonBeanRegistry { private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); public Object getSingleton(String beanName) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } return singletonObject; } }1.2 自定义简易IoC容器实现
理解IoC原理最好的方式就是自己实现一个简易版本:
// 定义核心注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Component {} @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Autowired {} // 简易IoC容器 public class SimpleIoC { private Map<Class<?>, Object> beans = new HashMap<>(); public void registerBean(Class<?> clazz) { try { Object instance = clazz.getDeclaredConstructor().newInstance(); beans.put(clazz, instance); } catch (Exception e) { throw new RuntimeException("创建Bean失败: " + clazz.getName(), e); } } public <T> T getBean(Class<T> clazz) { return (T) beans.get(clazz); } public void inject() { for (Object bean : beans.values()) { Field[] fields = bean.getClass().getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(Autowired.class)) { field.setAccessible(true); Object dependency = getBean(field.getType()); field.set(bean, dependency); } } } } }1.3 BeanFactory与ApplicationContext区别
| 特性 | BeanFactory | ApplicationContext |
|---|---|---|
| 加载方式 | 懒加载 | 预加载 |
| 功能范围 | 基础IoC功能 | 完整企业级功能(AOP、事件等) |
| 性能特点 | 启动快,运行期慢 | 启动慢,运行期快 |
| 使用场景 | 资源受限环境 | 大多数应用场景 |
2. AOP深度解析与实现原理
2.1 AOP核心概念图解
切面(Aspect) ├── 切入点(Pointcut) - 定义在何处执行 └── 通知(Advice) - 定义何时执行什么 ├── @Before ├── @After ├── @AfterReturning ├── @AfterThrowing └── @Around2.2 动态代理实现方式对比
2.2.1 JDK动态代理示例
public class JdkProxyDemo { public static void main(String[] args) { UserService target = new UserServiceImpl(); UserService proxy = (UserService) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxyObj, method, args1) -> { System.out.println("[JDK代理]方法前增强"); Object result = method.invoke(target, args1); System.out.println("[JDK代理]方法后增强"); return result; } ); proxy.saveUser("test"); } }2.2.2 CGLIB动态代理示例
public class CglibProxyDemo { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserController.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("[CGLIB]方法前增强"); Object result = proxy.invokeSuper(obj, args); System.out.println("[CGLIB]方法后增强"); return result; } }); UserController proxy = (UserController) enhancer.create(); proxy.getUser(1L); } }2.3 AOP织入时机对比表
| 织入方式 | 实现机制 | 性能 | 灵活性 | Spring支持 |
|---|---|---|---|---|
| 编译期织入 | 修改字节码 | 最高 | 最低 | 有限支持 |
| 类加载期织入 | 通过ClassLoader修改 | 高 | 中 | 需要配置 |
| 运行时织入 | 动态代理 | 中 | 最高 | 默认支持 |
3. 高频面试题深度解析
3.1 Bean生命周期关键阶段
- 实例化(Instantiation)
- 属性填充(Population)
- 初始化(Initialization)
- @PostConstruct
- InitializingBean
- init-method
- 使用中(In Use)
- 销毁(Destruction)
- @PreDestroy
- DisposableBean
- destroy-method
3.2 循环依赖解决方案
Spring通过三级缓存解决setter注入的循环依赖:
- A实例化 -> 放入三级缓存
- A属性注入时需要B -> B开始实例化
- B实例化 -> 放入三级缓存
- B属性注入时需要A -> 从三级缓存获取A的早期引用
- B初始化完成 -> A完成属性注入
- A初始化完成
注意:构造器注入的循环依赖无法解决,会直接抛出BeanCurrentlyInCreationException
4. 实战避坑指南
4.1 常见问题排查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Bean注入失败 | 未扫描到包 | 检查@ComponentScan配置 |
| 事务不生效 | 代理失效 | 检查方法是否为public |
| AOP增强未执行 | 切入点表达式不匹配 | 调试Pointcut匹配逻辑 |
| 循环依赖报错 | 构造器注入循环 | 改为setter注入 |
4.2 性能优化建议
- 合理使用@Lazy延迟初始化
- 避免过度使用AOP(每个代理调用会有约20%性能损耗)
- 对于频繁调用的方法,考虑使用AspectJ编译期织入
- 谨慎使用@Around,避免不必要的proceed()调用
5. 源码分析技巧
5.1 关键源码位置
IoC核心流程:
- AbstractApplicationContext#refresh()
- DefaultListableBeanFactory#preInstantiateSingletons()
AOP代理创建:
- AbstractAutoProxyCreator#postProcessAfterInitialization()
- DefaultAopProxyFactory#createAopProxy()
5.2 调试技巧
- 在BeanDefinitionRegistryPostProcessor中打断点观察Bean定义
- 使用条件断点过滤特定Bean的创建过程
- 关注AbstractAutowireCapableBeanFactory#doCreateBean()方法
在面试中,当被问到Spring原理时,建议从实际场景出发:"在我们项目中遇到XX问题时,通过分析Spring源码发现..."这样的回答方式更能体现你的实战能力。记住,面试官更关注你如何运用原理解决实际问题,而非单纯背诵实现细节。