Spring源码深度解析:把框架核心讲透,让面试官刮目相看
前言
🎯 写在前面:Spring是Java后端开发的"利刃",几乎所有Java程序员每天都在用。但你真的了解Spring的底层原理吗?
在Java后端面试中,Spring永远是逃不开的话题。而Spring的核心就是IOC容器和AOP切面编程。
很多小伙伴都遇到过这些问题:
"Spring是怎么把Bean创建出来的?" "依赖注入是怎么实现的?" "AOP的代理是怎么生成的?JDK动态代理和CGLIB有什么区别?" "Bean的生命周期到底有哪些步骤?" "循环依赖是怎么解决的?"
今天这篇文章,我将从源码层面深入剖析Spring IOC/AOP的原理,用图文并茂的方式把这些"八股文"讲透,让你面试再也不慌!
![Spring IOC/AOP源码解析封面]
一、Spring到底是个什么东西?
1.1 重新认识Spring
在正式讲解源码之前,我们先来理解Spring到底是什么。
┌─────────────────────────────────────────────────────────────────────┐
│ Spring框架架构 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Spring Framework │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ IOC │ │ AOP │ │ 事务 │ │ │
│ │ │ 控制反转 │ │ 面向切面 │ │ 管理 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │ │ │ │
│ │ └───────────────┴───────────────┘ │ │
│ │ │ │ │
│ │ ┌────────────────┼────────────────┐ │ │
│ │ │ │ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ JDBC │ │ Web MVC │ │ 测试 │ │ │
│ │ │ 模板 │ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ Spring Boot │
│ Spring Cloud │
│ Spring Data │
│ │
└─────────────────────────────────────────────────────────────────────┘
1.2 IOC到底是什么?
IOC = Inversion of Control = 控制反转
这是Spring的核心思想。听起来高大上,其实很简单:
传统写法(主动):
public class UserService {
// 主动创建依赖
private UserDao userDao = new UserDaoImpl();
public void save(User user) {
// 自己管理依赖
userDao.save(user);
}
}
IOC写法(被动):
public class UserService {
// 被动接收依赖,由Spring注入
private UserDao userDao;
// Spring通过构造方法或setter注入
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public void save(User user) {
// 不用关心userDao怎么来的
userDao.save(user);
}
}
IOC的好处:
┌─────────────────────────────────────────────────────────────────────┐
│ IOC的优势 │
│ │
│ 传统方式: IOC方式: │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ UserService │ │ UserService │ │
│ │ 直接依赖 │ │ 间接依赖 │ │
│ │ UserDaoImpl │ │ Spring容器 │ │
│ └─────────────────┘ └────────┬────────┘ │
│ │ │ │
│ │ 耦合度高, │ 耦合度低 │
│ │ 难以测试 │ 易于测试 │
│ │ 难以替换 │ 易于替换 │
│ │ 难以扩展 │ 易于扩展 │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 强依赖实现类 │ │ 依赖接口/容器 │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
1.3 AOP又是什么?
AOP = Aspect Oriented Programming = 面向切面编程
AOP的思想是:在不修改原有代码的情况下,增强功能。
传统方式(侵入性):
public class UserService {
public void save(User user) {
// ❌ 重复代码:日志记录
log.info("开始保存用户: " + user.getName());
long startTime = System.currentTimeMillis(); // ❌ 重复代码:计时
try {
// 核心业务逻辑
doSave(user);
// ❌ 重复代码:日志记录
log.info("保存用户成功: " + user.getName());
} catch (Exception e) {
// ❌ 重复代码:日志记录
log.error("保存用户失败: " + user.getName(), e);
throw e;
}
// ❌ 重复代码:计时
long endTime = System.currentTimeMillis();
log.info("保存用户耗时: " + (endTime - startTime) + "ms");
}
public void update(User user) {
// 又要复制一遍上述代码... ❌
}
public void delete(Long id) {
// 又要复制一遍上述代码... ❌
}
}
AOP方式(非侵入):
// 切面类:定义增强逻辑
@Aspect
@Component
public class LoggingAspect {
@Around("execution(* com.example.service.UserService.*(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
// 增强逻辑只写一次
log.info("开始执行: " + point.getSignature());
long startTime = System.currentTimeMillis();
Object result = point.proceed();
long endTime = System.currentTimeMillis();
log.info("执行完成,耗时: " + (endTime - startTime) + "ms");
return result;
}
}
// 业务类:保持纯净
@Service
public class UserService {
public void save(User user) {
// 只需要写核心业务逻辑
doSave(user);
}
public void update(User user) {
doUpdate(user);
}
public void delete(Long id) {
doDelete(id);
}
}
二、IOC容器启动流程:源码深度解析
2.1 整体架构图
┌─────────────────────────────────────────────────────────────────────┐
│ Spring IOC容器架构 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ApplicationContext │ │
│ │ (应用上下文) │ │
│ └──────────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼────────────────────────────────┐ │
│ │ DefaultListableBeanFactory │ │
│ │ (核心工厂类) │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ BeanDefinition │ │ SingletonRegistry│ │ BeanPostProcessor│ │
│ │ │ 注册表 │ │ 单例缓存 │ │ 后置处理器 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.2 启动入口:refresh()方法
Spring容器的启动,核心在于AbstractApplicationContext.refresh()方法:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1️⃣ 准备工作:设置启动时间、状态
prepareRefresh();
// 2️⃣ 获取BeanFactory(创建或获取容器)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3️⃣ 准备BeanFactory(设置类加载器、表达式解析器等)
prepareBeanFactory(beanFactory);
try {
// 4️⃣ 允许BeanFactory后置处理(子类可以覆盖)
postProcessBeanFactory(beanFactory);
// 5️⃣ 执行BeanFactory后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 6️⃣ 注册Bean后置处理器
registerBeanPostProcessors(beanFactory);
// 7️⃣ 初始化消息源
initMessageSource();
// 8️⃣ 初始化事件广播器
initApplicationEventMulticaster();
// 9️⃣ 子类初始化(模板方法)
onRefresh();
// 🔟 注册监听器
registerListeners();
// 1️⃣1️⃣ 实例化所有单例Bean
finishBeanFactoryInitialization(beanFactory);
// 1️⃣2️⃣ 完成刷新:发布事件
finishRefresh();
} catch (BeansException ex) {
destroyBeans();
cancelRefresh(ex);
throw ex;
}
}
}
}
流程图:
┌─────────────────────────────────────────────────────────────────────┐
│ refresh() 流程图 │
│ │
│ prepareRefresh() │
│ │ │
│ ▼ │
│ obtainFreshBeanFactory() │
│ │ │
│ ▼ │
│ prepareBeanFactory() │
│ │ │
│ ▼ │
│ postProcessBeanFactory() │
│ │ │
│ ▼ │
│ invokeBeanFactoryPostProcessors() ◄── BeanDefinitionRegistryPostProcessor│
│ │ │
│ ▼ │
│ registerBeanPostProcessors() ◄── BeanPostProcessor │
│ │ │
│ ▼ │
│ initMessageSource() │
│ │ │
│ ▼ │
│ initApplicationEventMulticaster() │
│ │ │
│ ▼ │
│ onRefresh() ◄── 子类扩展点 │
│ │ │
│ ▼ │
│ registerListeners() │
│ │ │
│ ▼ │
│ finishBeanFactoryInitialization() ◄── 实例化所有单例Bean │
│ │ │
│ ▼ │
│ finishRefresh() ◄── 发布ContextRefreshedEvent │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.3 BeanDefinition加载:资源定位
第一步:obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 刷新BeanFactory
refreshBeanFactory();
// 返回BeanFactory
return getBeanFactory();
}
BeanDefinition从哪里来?
// Spring Boot会自动扫描
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// 等价于XML配置
<context:component-scan base-package="com.example"/>
ComponentScan扫描过程:
┌─────────────────────────────────────────────────────────────────────┐
│ Bean扫描流程 │
│ │
│ 1️⃣ 指定扫描包 │
│ @ComponentScan("com.example.service") │
│ │ │
│ ▼ │
│ 2️⃣ 扫描class文件 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │UserDao │ │UserSvc │ │UserCtrl │ │
│ │.class │ │.class │ │.class │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │
│ ▼ │
│ 3️⃣ 解析注解 │
│ @Component → BeanDefinition │
│ @Service → BeanDefinition │
│ @Repository→BeanDefinition │
│ │ │
│ ▼ │
│ 4️⃣ 注册到BeanDefinitionRegistry │
│ ┌──────────────────────────────────────────┐ │
│ │ BeanDefinitionMap │ │
│ │ "userDao" → BeanDefinition │ │
│ │ "userService" → BeanDefinition │ │
│ │ "userController" → BeanDefinition │ │
│ └──────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.4 BeanDefinition结构解析
/**
* BeanDefinition - Bean的定义信息
*
* 类似于Class和Object的关系:
* Class是对类的描述
* BeanDefinition是对Bean的描述
*/
public class BeanDefinitionDemo {
public static void main(String[] args) {
// 假设有这样的类
// @Component
// public class UserService {
// @Autowired
// private UserDao userDao;
//
// @Value("${app.name}")
// private String appName;
// }
// 生成的BeanDefinition包含:
BeanDefinition bd = new RootBeanDefinition(UserService.class);
// 1. beanClassName:Bean的全限定类名
bd.setBeanClassName("com.example.service.UserService");
// 2. scope:作用域
bd.setScope(BeanDefinition.SCOPE_SINGLETON); // 单例
// 3. lazyInit:是否懒加载
bd.setLazyInit(false); // 默认false,立即加载
// 4. autowireMode:自动装配模式
bd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
// 5. propertyValues:属性值(@Value注入的值)
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("appName", "my-app");
bd.setPropertyValues(pvs);
// 6. constructorArgumentValues:构造方法参数(@Autowired注入的值)
ConstructorArgumentValues cav = new ConstructorArgumentValues();
// 假设UserService有构造方法参数
bd.setConstructorArgumentValues(cav);
}
}
2.5 Bean实例化:createBean()
这是IOC容器最核心的方法——Bean是如何被创建出来的?
/**
* AbstractAutowireCapableBeanFactory.createBean()
*
* Bean实例化核心流程
*/
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 1️⃣ 解析beanClass(可能是一个Class,也可能是一个字符串)
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
try {
// 2️⃣ 合并父子BeanDefinition
mbd = getMergedLocalBeanDefinition(beanName);
// 3️⃣ 检查抽象标记(抽象Bean不能实例化)
if (mbd.isAbstract()) {
throw new BeanIsAbstractException(beanName);
}
// 4️⃣ 处理LookupMethod替换(@Lookup注解)
if (mbd.hasMethodOverrides()) {
// 创建代理类,处理lookup-method和replace-method
return resolveBeforeInstantiation(beanName, mbd);
}
// 5️⃣ 真正创建Bean实例
Object beanInstance = doCreateBean(beanName, mbd, args);
return beanInstance;
} catch (BeanCreationException ex) {
throw ex;
}
}
/**
* 真正创建Bean的方法
*/
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 1️⃣ 创建BeanWrapper(包装器)
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 单例模式下,从缓存获取之前创建的FactoryBean
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 2️⃣ 实例化Bean(使用构造方法或工厂方法)
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanClass = instanceWrapper.getWrappedClass();
// 3️⃣ 提前暴露Bean(用于解决循环依赖)
boolean earlySingletonExposure = mbd.isSingleton() &&
mbd.allowCircularReferences() &&
isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
// 添加到三级缓存:singletonFactories
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 4️⃣ 属性填充(依赖注入)
populateBean(beanName, mbd, instanceWrapper);
// 5️⃣ 初始化Bean(执行Aware接口、init-method等)
bean = initializeBean(beanName, bean, mbd);
// 6️⃣ 处理循环依赖
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 循环依赖处理...
}
}
// 7️⃣ 注册DisposableBean(销毁回调)
registerDisposableBeanIfNecessary(beanName, bean, mbd);
return bean;
}
Bean创建流程图:
┌─────────────────────────────────────────────────────────────────────┐
│ doCreateBean 流程图 │
│ │
│ createBeanInstance() ◄── 实例化Bean │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 构造方法选择策略 │ │
│ │ │ │
│ │ 1. 有@Autowired构造方法? │ │
│ │ → 使用该构造方法 │ │
│ │ │ │
│ │ 2. 只有一个构造方法? │ │
│ │ → 使用该构造方法 │ │
│ │ │ │
│ │ 3. 有多个构造方法? │ │
│ │ → 使用无参构造方法(有就报错) │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ populateBean() ◄── 属性填充(依赖注入) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 依赖注入方式 │ │
│ │ │ │
│ │ @Autowired │ │
│ │ ├── @Autowired on Field │ │
│ │ │ → 反射注入字段值 │ │
│ │ │ │ │
│ │ ├── @Autowired on Setter │ │
│ │ │ → 调用setter方法注入 │ │
│ │ │ │ │
│ │ └── @Autowired on Constructor │ │
│ │ → 构造方法注入 │ │
│ │ │ │
│ │ @Resource (javax.annotation) │ │
│ │ → byName 优先 │ │
│ │ │ │
│ │ @Value │ │
│ │ → 注入普通值/SpEL │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ initializeBean() ◄── 初始化Bean │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 初始化流程 │ │
│ │ │ │
│ │ 1. Aware接口处理 │ │
│ │ BeanNameAware → setBeanName() │ │
│ │ BeanFactoryAware → setBeanFactory│ │
│ │ ApplicationContextAware → │ │
│ │ setApplicationContext() │ │
│ │ │ │
│ │ 2. BeanPostProcessor.postProcessBeforeInitialization │ │
│ │ │ │
│ │ 3. InitializingBean.afterPropertiesSet() │ │
│ │ 或 @PostConstruct │ │
│ │ │ │
│ │ 4. BeanPostProcessor.postProcessAfterInitialization │ │
│ │ │ │
│ │ 5. Bean已准备就绪 │ │
│ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
三、Bean生命周期:完整流程图解
3.1 生命周期全图
┌─────────────────────────────────────────────────────────────────────────────┐
│ Bean 完整生命周期 │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 1. BeanDefinition阶段 │ │
│ │ │ │
│ │ 扫描class → 解析注解 → 生成BeanDefinition → 注册到BeanFactory │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 2. Bean实例化阶段 │ │
│ │ │ │
│ │ BeanWrapper创建 → 构造方法选择 → 实例化Bean │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 3. 属性填充阶段 │ │
│ │ │ │
│ │ @Autowired注入 → @Value注入 → @Resource注入 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 4. 初始化阶段 │ │
│ │ │ │
│ │ BeanNameAware → BeanFactoryAware → ApplicationContextAware │ │
│ │ ↓ │ │
│ │ postProcessBeforeInitialization │ │
│ │ ↓ │ │
│ │ @PostConstruct / afterPropertiesSet() │ │
│ │ ↓ │ │
│ │ postProcessAfterInitialization │ │
│ │ ↓ │ │
│ │ 初始化完成,Bean可用 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 5. 销毁阶段 │ │
│ │ │ │
│ │ @PreDestroy / destroy() / DisposableBean.destroy() │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
3.2 生命周期代码演示
/**
* 完整演示Bean生命周期的各个阶段
*/
@Component
@Scope("singleton")
public class UserService implements InitializingBean, DisposableBean,
BeanNameAware, BeanFactoryAware {
@Autowired
private UserDao userDao;
@Value("${app.name:default}")
private String appName;
// ==================== 1. 构造方法 ====================
public UserService() {
System.out.println("1️⃣ UserService 构造方法执行");
}
// ==================== 2. 属性注入 ====================
@Autowired
public void setUserDao(UserDao userDao) {
System.out.println("2️⃣ UserDao 依赖注入: " + userDao);
this.userDao = userDao;
}
@PostConstruct
public void init() {
System.out.println("4️⃣ @PostConstruct 初始化方法执行");
}
@PreDestroy
public void cleanup() {
System.out.println("7️⃣ @PreDestroy 销毁前方法执行");
}
// ==================== 3. Aware接口 ====================
@Override
public void setBeanName(String name) {
System.out.println("3️⃣ BeanNameAware.setBeanName: " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("3️⃣ BeanFactoryAware.setBeanFactory: " + beanFactory);
}
// ==================== 4. InitializingBean ====================
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("5️⃣ InitializingBean.afterPropertiesSet");
}
// ==================== 5. DisposableBean ====================
@Override
public void destroy() throws Exception {
System.out.println("6️⃣ DisposableBean.destroy");
}
// ==================== 业务方法 ====================
public void save(User user) {
userDao.save(user);
}
}
/**
* 演示BeanPostProcessor
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("🔄 postProcessBeforeInitialization: " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("🔄 postProcessAfterInitialization: " + beanName);
return bean;
}
}
输出顺序:
1️⃣ UserService 构造方法执行
3️⃣ BeanNameAware.setBeanName: userService
3️⃣ BeanFactoryAware.setBeanFactory: org.springframework...
🔄 postProcessBeforeInitialization: userService
2️⃣ UserDao 依赖注入: com.example.dao.UserDao@12345678
4️⃣ @PostConstruct 初始化方法执行
5️⃣ InitializingBean.afterPropertiesSet
🔄 postProcessAfterInitialization: userService
...
容器关闭:
6️⃣ DisposableBean.destroy
7️⃣ @PreDestroy 销毁前方法执行
四、依赖注入:三种方式深度解析
4.1 构造方法注入 vs Setter注入 vs 字段注入
/**
* 三种依赖注入方式对比
*/
@Service
public class UserService {
private UserDao userDao;
private String appName;
// ==================== 1. 构造方法注入(推荐)====================
@Autowired
public UserService(UserDao userDao, @Value("${app.name}") String appName) {
// 优点:
// 1. 强制依赖,编译时就检查
// 2. 可以设置为final
// 3. 便于单元测试(直接new)
// 4. 循环依赖检测
this.userDao = userDao;
this.appName = appName;
}
// ==================== 2. Setter注入(可选依赖)====================
private CacheService cacheService;
@Autowired(required = false) // required = false表示可选
public void setCacheService(CacheService cacheService) {
// 优点:
// 1. 可选依赖
// 2. 可以在运行时替换(测试场景)
this.cacheService = cacheService;
}
// ==================== 3. 字段注入(不推荐)====================
@Autowired
private LogService logService; // ❌ 不推荐
// 为什么不推荐字段注入?
// 1. 无法设置为final
// 2. 难以单元测试(需要反射或Spring Test)
// 3. 违反单一职责原则
// 4. 无法注入编译时常量
}
4.2 @Autowired vs @Resource 区别
@Service
public class UserService {
// @Autowired vs @Resource 区别
// ================================
// 1. @Autowired
// - 来源:Spring
// - 默认按byType注入
// - 可以配合@Qualifier按name注入
// - required属性控制是否必需
@Autowired
@Qualifier("userDaoImpl") // 指定Bean名称
private UserDao userDao1;
// 2. @Resource
// - 来源:JSR-250(Java标准)
// - 默认按byName注入
// - name属性指定Bean名称
// - 没有required属性
@Resource(name = "userDaoImpl")
private UserDao userDao2;
// 3. @Inject
// - 来源:JSR-330
// - 类似@Autowired
// - 需要导入javax.inject包
@Inject
private UserDao userDao3;
}
4.3 循环依赖:三级缓存揭秘
什么是循环依赖?
// 循环依赖示例
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB; // 依赖B
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA; // 又依赖A!循环了!
}
Spring如何解决循环依赖?
Spring使用三级缓存来解决这个问题:
┌─────────────────────────────────────────────────────────────────────┐
│ 三级缓存解决循环依赖 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 一级缓存:singletonObjects │ │
│ │ - 完全初始化好的Bean │ │
│ │ - 可以直接使用 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ▲ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 二级缓存:earlySingletonObjects │ │
│ │ - 提前曝光的Bean(未完全初始化) │ │
│ │ - 正在创建中 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ▲ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 三级缓存:singletonFactories │ │
│ │ - Bean工厂(用于创建早期Bean的引用) │ │
│ │ - ObjectFactory<?> │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
解决过程图解:
┌─────────────────────────────────────────────────────────────────────┐
│ 循环依赖解决过程 │
│ │
│ 假设创建ServiceA,ServiceA依赖ServiceB,ServiceB又依赖ServiceA │
│ │
│ 步骤1:开始创建ServiceA │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 创建ServiceA │ │
│ │ 1️⃣ 调用构造方法 │ │
│ │ 2️⃣ 创建原始对象A(未填充属性) │ │
│ │ 3️⃣ 放入三级缓存singletonFactories │ │
│ │ {"serviceA": ObjectFactory<ServiceA>} │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 步骤2:填充ServiceA的属性,发现依赖ServiceB │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 填充@Autowired ServiceB │ │
│ │ → 尝试获取ServiceB │ │
│ │ → ServiceB还没创建!需要创建ServiceB │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 步骤3:开始创建ServiceB │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 创建ServiceB │ │
│ │ 1️⃣ 调用构造方法 │ │
│ │ 2️⃣ 创建原始对象B(未填充属性) │ │
│ │ 3️⃣ 放入三级缓存singletonFactories │ │
│ │ {"serviceB": ObjectFactory<ServiceB>} │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 步骤4:填充ServiceB的属性,发现依赖ServiceA │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 填充@Autowired ServiceA │ │
│ │ → 尝试获取ServiceA │ │
│ │ → 从三级缓存获取ServiceA的ObjectFactory │ │
│ │ → 调用getObject()获取早期引用 │ │
│ │ → 放入二级缓存earlySingletonObjects │ │
│ │ → 从三级缓存移除 │ │
│ │ → ServiceB填充完成 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 步骤5:ServiceB创建完成 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ServiceB填充完成 │ │
│ │ → 放入一级缓存singletonObjects │ │
│ │ → 从二级/三级缓存移除 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 步骤6:回到ServiceA,填充ServiceB │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 从一级缓存获取ServiceB │ │
│ │ → ServiceA填充完成 │ │
│ │ → 放入一级缓存singletonObjects │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 完成!循环依赖解决 │
│ │
└─────────────────────────────────────────────────────────────────────┘
为什么需要三级缓存?
// 二级缓存不够吗?
// 答案:不够!因为需要支持AOP代理
// 假设ServiceA有AOP增强
@Service
public class ServiceA {
@Transactional
public void doSomething() { }
}
// 创建过程需要增强:
// 1. ServiceA原始对象创建
// 2. 需要知道是否需要创建代理
// 3. 如果需要,创建代理对象
// 三级缓存的设计:
// - 一级缓存:完全初始化好的Bean(可能已经是代理)
// - 二级缓存:早期暴露的Bean(可能是代理)
// - 三级缓存:Bean工厂(延迟创建代理)
// 好处:
// 1. 延迟代理对象的创建(性能优化)
// 2. 允许在Bean创建过程中动态决定是否创建代理
// 3. 完美解决循环依赖 + AOP问题
五、AOP切面编程:源码深度解析
5.1 AOP核心概念
┌─────────────────────────────────────────────────────────────────────┐
│ AOP核心概念 │
│ │
│ ┌─────────┐ │
│ │ 切面 │ Aspect = 切入点 + 通知 │
│ │ Aspect │ 定义:在哪个时机(通知)做什么(增强逻辑) │
│ └─────────┘ │
│ │ │
│ │ 包含 │
│ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 切入点 │ │ 通知 │ │ 目标 │ │ 代理 │ │
│ │Pointcut│ │ Advice │ │ Target │ │ Proxy │ │
│ │ 哪里切 │ │ 何时通知│ │ 业务类 │ │ 增强后 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 通知类型: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ @Before - 前置通知:方法执行前 │ │
│ │ @After - 后置通知:方法执行后(无论是否异常) │ │
│ │ @AfterReturning - 返回通知:方法正常返回后 │ │
│ │ @AfterThrowing - 异常通知:方法抛出异常后 │ │
│ │ @Around - 环绕通知:方法执行前后(可以控制是否执行) │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
5.2 实战:自定义日志切面
/**
* 日志切面:记录方法调用信息
*/
@Aspect
@Component
@Slf4j
public class LoggingAspect {
/**
* 定义切入点:所有Controller层的public方法
*/
@Pointcut("execution(public * com.example.controller..*.*(..))")
public void controllerPointcut() {}
/**
* 定义切入点:所有Service层的save/update/delete方法
*/
@Pointcut("execution(* com.example.service..*.save*(..)) || " +
"execution(* com.example.service..*.update*(..)) || " +
"execution(* com.example.service..*.delete*(..))")
public void modifyOperationPointcut() {}
/**
* 前置通知:记录方法开始
*/
@Before("controllerPointcut()")
public void doBefore(JoinPoint joinPoint) {
// 获取方法签名
Signature signature = joinPoint.getSignature();
// 获取类名.方法名
String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
// 获取参数
Object[] args = joinPoint.getArgs();
log.info("➡️ 方法开始: {},参数: {}", methodName, Arrays.toString(args));
}
/**
* 返回通知:记录方法返回值
*/
@AfterReturning(pointcut = "controllerPointcut()", returning = "result")
public void doAfterReturning(JoinPoint joinPoint, Object result) {
Signature signature = joinPoint.getSignature();
log.info("✅ 方法返回: {}.{},结果: {}",
signature.getDeclaringTypeName(),
signature.getName(),
result);
}
/**
* 异常通知:记录方法异常
*/
@AfterThrowing(pointcut = "controllerPointcut()", throwing = "exception")
public void doAfterThrowing(JoinPoint joinPoint, Exception exception) {
Signature signature = joinPoint.getSignature();
log.error("❌ 方法异常: {}.{},异常: {}",
signature.getDeclaringTypeName(),
signature.getName(),
exception.getMessage(),
exception);
}
/**
* 环绕通知:性能监控
*/
@Around("modifyOperationPointcut()")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
long startTime = System.currentTimeMillis();
// 执行目标方法
Object result = point.proceed();
long endTime = System.currentTimeMillis();
String methodName = point.getSignature().getName();
log.info("⏱️ 业务操作: {},耗时: {}ms", methodName, endTime - startTime);
return result;
}
}
5.3 事务切面:@Transactional原理
/**
* @Transactional 原理
*
* Spring事务基于AOP实现,核心类:TransactionInterceptor
*/
@Service
public class TransactionalService {
@Transactional(rollbackFor = Exception.class)
public void transfer(String from, String to, BigDecimal amount) {
// 这些操作在同一个事务中
accountDao.decrease(from, amount); // 扣款
accountDao.increase(to, amount); // 存款
// 如果发生异常,整个事务回滚
}
}
/**
* 自定义事务管理器切面(简化版)
*/
@Aspect
@Component
public class TransactionAspect {
@Autowired
private PlatformTransactionManager transactionManager;
@Around("@annotation(Transactional)")
public Object around(ProceedingJoinPoint point) throws Throwable {
// 1️⃣ 获取事务定义
TransactionDefinition definition = new DefaultTransactionDefinition();
// 2️⃣ 获取或创建事务
TransactionStatus status = transactionManager.getTransaction(definition);
try {
// 3️⃣ 执行目标方法
Object result = point.proceed();
// 4️⃣ 提交事务
transactionManager.commit(status);
return result;
} catch (Exception e) {
// 5️⃣ 回滚事务
transactionManager.rollback(status);
throw e;
}
}
}
六、动态代理:JDK vs CGLIB
6.1 为什么需要代理?
┌─────────────────────────────────────────────────────────────────────┐
│ 代理模式原理 │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 客户端 │ │ 目标对象 │ │
│ │ (Client) │ │ (Target) │ │
│ └────────┬────────┘ └─────────────────┘ │
│ │ │
│ │ 调用 │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 代理对象 │ │
│ │ (Proxy) │ │
│ │ │ │
│ │ 1. 前置增强 │ │
│ │ 2. 调用目标方法 │ │
│ │ 3. 后置增强 │ │
│ │ 4. 返回结果 │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 目标对象 │ │
│ │ (Target) │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
6.2 JDK动态代理:基于接口
原理:
/**
* JDK动态代理演示
*
* 核心类:java.lang.reflect.Proxy
* 要求:目标类必须实现接口
*/
public class JdkProxyDemo {
public static void main(String[] args) {
// 1️⃣ 定义目标对象(必须实现接口)
UserService target = new UserServiceImpl();
// 2️⃣ 创建InvocationHandler
InvocationHandler handler = (proxy, method, methodArgs) -> {
// 前置增强
System.out.println(">>> 前置逻辑开始");
long startTime = System.currentTimeMillis();
// 3️⃣ 调用目标方法
Object result = method.invoke(target, methodArgs);
// 后置增强
long endTime = System.currentTimeMillis();
System.out.println("<<< 后置逻辑结束,耗时:" + (endTime - startTime) + "ms");
return result;
};
// 4️⃣ 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 实现的接口
handler // 处理逻辑
);
// 5️⃣ 使用代理对象(与使用原对象完全相同)
proxy.save(new User());
proxy.update(new User());
}
}
/**
* 接口定义
*/
interface UserService {
void save(User user);
void update(User user);
}
/**
* 实现类
*/
class UserServiceImpl implements UserService {
@Override
public void save(User user) {
System.out.println("UserServiceImpl.save 执行");
}
@Override
public void update(User user) {
System.out.println("UserServiceImpl.update 执行");
}
}
生成的代理类结构:
// 生成的代理类(伪代码)
public class $Proxy0 implements UserService {
private InvocationHandler h;
public $Proxy0(InvocationHandler h) {
this.h = h;
}
@Override
public void save(User user) {
// 调用InvocationHandler
h.invoke(this, saveMethod, new Object[]{user});
}
@Override
public void update(User user) {
h.invoke(this, updateMethod, new Object[]{user});
}
}
6.3 CGLIB代理:基于继承
原理:
/**
* CGLIB动态代理演示
*
* 核心类:net.sf.cglib.proxy.Enhancer
* 要求:目标类不能是final
* 原理:继承目标类,重写方法
*/
public class CglibProxyDemo {
public static void main(String[] args) {
// 1️⃣ 创建Enhancer
Enhancer enhancer = new Enhancer();
// 2️⃣ 设置父类(目标类)
enhancer.setSuperclass(UserService.class);
// 3️⃣ 设置回调
enhancer.setCallbacks(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
// 前置增强
System.out.println(">>> CGLIB 前置逻辑");
long startTime = System.currentTimeMillis();
// 调用父类方法(目标方法)
// 方式1:proxy.invokeSuper() - 推荐
Object result = proxy.invokeSuper(obj, args);
// 方式2:proxy.invoke() - 慎用,可能导致死循环
// Object result = proxy.invoke(target, args);
// 后置增强
long endTime = System.currentTimeMillis();
System.out.println("<<< CGLIB 后置逻辑,耗时:" + (endTime - startTime));
return result;
}
});
// 4️⃣ 创建代理对象
UserService proxy = (UserService) enhancer.create();
// 5️⃣ 使用代理对象
proxy.save(new User());
proxy.update(new User());
}
}
生成的代理类结构:
// 生成的代理类(伪代码)
public class UserServiceImpl$$EnhancerByCGLIB$$12345678
extends UserServiceImpl {
private MethodInterceptor callback;
// 构造函数
public UserServiceImpl$$EnhancerByCGLIB$$12345678(MethodInterceptor callback) {
this.callback = callback;
}
@Override
public void save(User user) {
// 调用拦截器
MethodProxy proxy = ...;
callback.intercept(this, saveMethod, new Object[]{user}, proxy);
}
@Override
public void update(User user) {
MethodProxy proxy = ...;
callback.intercept(this, updateMethod, new Object[]{user}, proxy);
}
}
6.4 两者对比
┌─────────────────────────────────────────────────────────────────────┐
│ JDK代理 vs CGLIB代理 │
│ │
│ ┌────────────────────┬────────────────────┐ │
│ │ JDK代理 │ CGLIB代理 │ │
│ ├────────────────────┼────────────────────┤ │
│ │ 依赖 │ JDK自带 │ net.sf.cglib │
│ ├────────────────────┼────────────────────┤ │
│ │ 实现原理 │ 接口实现 │ 继承重写 │
│ ├────────────────────┼────────────────────┤ │
│ │ 要求 │ 必须有接口 │ 类不能是final │
│ ├────────────────────┼────────────────────┤ │
│ │ 生成速度 │ 快 │ 慢 │
│ ├────────────────────┼────────────────────┤ │
│ │ 运行速度 │ 稍慢 │ 快(直接调用) │
│ │ │ (反射调用) │ │
│ ├────────────────────┼────────────────────┤ │
│ │ 内存效率 │ 高 │ 低(创建子类) │
│ ├────────────────────┼────────────────────┤ │
│ │ 适用场景 │ 有接口的服务 │ 无接口/类/ final类 │
│ └────────────────────┴────────────────────┘ │
│ │
│ Spring默认策略: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 目标类有接口? │ │
│ │ │ │ │
│ │ ┌────┴────┐ │ │
│ │ │ Yes │ No │ │
│ │ ▼ ▼ │ │
│ │ ┌──────┐ ┌──────┐ │ │
│ │ │ JDK │ │CGLIB │ │ │
│ │ │ 代理 │ │ 代理 │ │ │
│ │ └──────┘ └──────┘ │ │
│ │ │ │
│ │ 配置项:spring.aop.proxy-target-class=false → 强制使用JDK │ │
│ │ spring.aop.proxy-target-class=true → 强制使用CGLIB│ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
6.5 Spring AOP代理创建流程
/**
* AbstractAutowireCapableBeanFactory.createProxy() 简化版
*/
public class ProxyCreationFlow {
// 入口:创建AOP代理
protected Object createProxy(String beanName, RootBeanDefinition beanDefinition,
Object bean, Set<String> targets) {
// 1️⃣ 创建ProxyFactory
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(beanDefinition);
// 2️⃣ 判断使用JdkDynamicProxy还是CglibAopProxy
// 取决于proxyTargetClass属性和目标类是否有接口
// 如果目标类有接口 && !proxyTargetClass → 使用JDK代理
// 否则 → 使用CGLIB代理
if (shouldProxyTargetClass(beanDefinition, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
// 3️⃣ 添加切面
// @AspectJ标注的类会被解析成Advisor
proxyFactory.addAdvisors(beanFactory.getAdvicesAndAdvisorsForBean(
beanClass, beanName, null));
// 4️⃣ 创建代理
return proxyFactory.getProxy(getBeanClassLoader());
}
// 判断逻辑
private boolean shouldProxyTargetClass(RootBeanDefinition beanDefinition,
String beanName) {
// 1. 如果proxy-target-class=true,强制使用CGLIB
if (beanDefinition.getAttribute("proxyTargetClass") == Boolean.TRUE) {
return true;
}
// 2. 如果目标类有接口,使用JDK代理
if (hasInterface(beanDefinition.getBeanClass())) {
return false;
}
// 3. 否则使用CGLIB
return true;
}
}
七、Spring事务:传播行为深度解析
7.1 七种传播行为
/**
* Spring事务传播行为
*
* TransactionDefinition接口定义了7种传播行为
*/
public enum Propagation {
/**
* REQUIRED(默认)
* 如果当前有事务,加入该事务
* 如果当前没有事务,创建新事务
*/
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 场景:A和B在同一个事务
methodB(); // B加入A的事务
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
// 与A在同一个事务
}
/**
* REQUIRES_NEW
* 总是创建新事务
* 如果当前有事务,挂起当前事务
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodC() {
// 场景:C在独立事务中执行
// 即使A的事务失败,C的事务不受影响
}
/**
* SUPPORTS
* 如果当前有事务,加入该事务
* 如果当前没有事务,以非事务执行
*/
/**
* NOT_SUPPORTED
* 如果当前有事务,挂起当前事务
* 以非事务执行
*/
/**
* MANDATORY
* 必须在事务中执行
* 如果当前没有事务,抛异常
*/
/**
* NEVER
* 必须在非事务中执行
* 如果当前有事务,抛异常
*/
/**
* NESTED
* 如果当前有事务,在嵌套事务中执行
* 使用Savepoint机制(JDBC Savepoint)
*/
}
7.2 传播行为对比图
┌─────────────────────────────────────────────────────────────────────┐
│ 事务传播行为对比 │
│ │
│ 调用方(A) 被调用方(B) 结果 │
│ ───────── ───────────── ────── │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ @Transactional│ │ REQUIRED │ ┌─────────────────┐ │
│ │ 有事务 │────►│ (默认) │────►│ A和B在同一个事务 │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 无事务 │────►│ REQUIRED │────►│ B创建新事务 │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ @Transactional│ │REQUIRES_NEW │────►│ B在独立新事务执行 │ │
│ │ 有事务 │────►│ │ │ A事务被挂起 │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ @Transactional│ │ NESTED │────►│ B在嵌套事务执行 │ │
│ │ 有事务 │────►│ (Savepoint)│ │ A回滚,B部分回滚 │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
八、实战:自定义Spring注解
8.1 自定义缓存注解
/**
* 自定义缓存注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCacheable {
// 缓存key
String key();
// 过期时间(秒)
int expire() default 300;
}
/**
* 缓存切面实现
*/
@Aspect
@Component
@Slf4j
public class MyCacheAspect {
private Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
@Around("@annotation(myCacheable)")
public Object around(ProceedingJoinPoint point, MyCacheable myCacheable)
throws Throwable {
String cacheKey = buildCacheKey(myCacheable.key(), point.getArgs());
// 1️⃣ 尝试从缓存获取
CacheEntry entry = cache.get(cacheKey);
if (entry != null && !entry.isExpired()) {
log.info("🔵 命中缓存: {}", cacheKey);
return entry.getValue();
}
// 2️⃣ 缓存不存在或已过期,执行目标方法
log.info("🔴 缓存未命中: {}", cacheKey);
Object result = point.proceed();
// 3️⃣ 写入缓存
cache.put(cacheKey, new CacheEntry(result, myCacheable.expire()));
return result;
}
private String buildCacheKey(String keyTemplate, Object[] args) {
// 简单实现:替换{0}, {1}等占位符
String result = keyTemplate;
for (int i = 0; i < args.length; i++) {
result = result.replace("{" + i + "}",
args[i] != null ? args[i].toString() : "null");
}
return result;
}
// 缓存条目
private static class CacheEntry {
private final Object value;
private final long expireTime;
public CacheEntry(Object value, int expireSeconds) {
this.value = value;
this.expireTime = System.currentTimeMillis() + expireSeconds * 1000L;
}
public Object getValue() { return value; }
public boolean isExpired() {
return System.currentTimeMillis() > expireTime;
}
}
}
/**
* 使用自定义缓存注解
*/
@Service
public class UserService {
@MyCacheable(key = "user:{0}", expire = 600)
public User getUserById(Long id) {
// 从数据库查询
return userDao.findById(id);
}
@MyCacheable(key = "user:list:{0}:{1}", expire = 300)
public List<User> getUsers(int page, int size) {
return userDao.findAll(page, size);
}
}
8.2 自定义限流注解
/**
* 限流注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
// 限流key
String key() default "";
// 时间窗口(秒)
int window() default 1;
// 最大请求数
int maxRequests() default 100;
}
/**
* 限流切面(基于令牌桶算法)
*/
@Aspect
@Component
@Slf4j
public class RateLimitAspect {
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
@Around("@annotation(rateLimit)")
public Object around(ProceedingJoinPoint point, RateLimit rateLimit)
throws Throwable {
String key = getKey(rateLimit.key(), point);
// 1️⃣ 获取或创建限流器
RateLimiter limiter = limiters.computeIfAbsent(key,
k -> new RateLimiter(rateLimit.maxRequests(), rateLimit.window()));
// 2️⃣ 尝试获取令牌
if (!limiter.tryAcquire()) {
log.warn("⚠️ 请求被限流: {}", key);
throw new RuntimeException("请求太频繁,请稍后重试");
}
// 3️⃣ 执行目标方法
return point.proceed();
}
private String getKey(String keyTemplate, ProceedingJoinPoint point) {
if (StringUtils.isNotBlank(keyTemplate)) {
return keyTemplate;
}
// 默认key:类名.方法名
return point.getSignature().getDeclaringTypeName() + "." +
point.getSignature().getName();
}
/**
* 简单限流器(固定窗口算法)
*/
private static class RateLimiter {
private final int maxRequests;
private final long windowMillis;
private final AtomicInteger count = new AtomicInteger(0);
private volatile long windowStart = System.currentTimeMillis();
public RateLimiter(int maxRequests, int windowSeconds) {
this.maxRequests = maxRequests;
this.windowMillis = windowSeconds * 1000L;
}
public boolean tryAcquire() {
long now = System.currentTimeMillis();
// 超过时间窗口,重置
if (now - windowStart > windowMillis) {
windowStart = now;
count.set(0);
}
// 尝试递增计数
return count.incrementAndGet() <= maxRequests;
}
}
}
/**
* 使用限流注解
*/
@RestController
public class UserController {
@RateLimit(key = "user:get", maxRequests = 100, window = 1)
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
@RateLimit(key = "user:login", maxRequests = 10, window = 60)
@PostMapping("/login")
public String login(@RequestBody LoginRequest request) {
return userService.login(request);
}
}
九、面试重点总结
9.1 IOC相关面试题
Q1: Spring容器启动流程是什么?
1. prepareRefresh() - 准备刷新
2. obtainFreshBeanFactory() - 获取BeanFactory
3. prepareBeanFactory() - 准备BeanFactory
4. postProcessBeanFactory() - 后置处理BeanFactory
5. invokeBeanFactoryPostProcessors() - 执行BeanFactory后置处理器
6. registerBeanPostProcessors() - 注册Bean后置处理器
7. initMessageSource() - 初始化消息源
8. initApplicationEventMulticaster() - 初始化事件广播器
9. onRefresh() - 子类扩展
10. registerListeners() - 注册监听器
11. finishBeanFactoryInitialization() - 初始化所有单例Bean
12. finishRefresh() - 完成刷新
Q2: Bean的生命周期有哪些?
1. 实例化(构造方法)
2. 属性填充(依赖注入)
3. Aware接口注入
- BeanNameAware
- BeanFactoryAware
- ApplicationContextAware
4. BeanPostProcessor.postProcessBeforeInitialization()
5. @PostConstruct
6. InitializingBean.afterPropertiesSet()
7. BeanPostProcessor.postProcessAfterInitialization()
8. Bean就绪
9. @PreDestroy
10. DisposableBean.destroy()
Q3: Spring如何解决循环依赖?
使用三级缓存:
- 一级缓存:完全初始化好的Bean
- 二级缓存:提前曝光的Bean(未完全初始化)
- 三级缓存:Bean工厂
流程:
1. 创建A,放入三级缓存
2. 填充A的属性,发现依赖B
3. 创建B,放入三级缓存
4. 填充B的属性,发现依赖A
5. 从三级缓存获取A的工厂,创建早期A
6. B创建完成,放入一级缓存
7. A填充B成功,创建完成
Q4: @Autowired和@Resource的区别?
| 特性 | @Autowired | @Resource |
|------------|-------------------|----------------------|
| 来源 | Spring | JSR-250 (Java标准) |
| 默认方式 | byType | byName |
| 配合注解 | @Qualifier | name属性 |
| required | 支持 | 不支持 |
9.2 AOP相关面试题
Q5: AOP的代理机制是什么?
Spring AOP使用代理模式,有两种实现:
1. JDK动态代理(默认)
- 需要目标类实现接口
- 代理类实现相同接口
- 调用InvocationHandler
2. CGLIB代理
- 继承目标类
- 重写方法
- 使用MethodInterceptor
选择策略:
- 目标类有接口 → JDK代理
- 目标类无接口 → CGLIB代理
- proxyTargetClass=true → 强制CGLIB
Q6: AOP通知的执行顺序?
正常流程:
@Before → 目标方法 → @AfterReturning → @After
异常流程:
@Before → 目标方法 → @AfterThrowing → @After
环绕通知:
@Around(前置) → 目标方法 → @Around(后置)
Q7: Spring事务的传播行为有哪些?
REQUIRED(默认):有事务加入,无事务创建
REQUIRES_NEW:始终创建新事务
SUPPORTS:有事务加入,无事务非事务执行
NOT_SUPPORTED:挂起事务,非事务执行
MANDATORY:必须在事务中,否则抛异常
NEVER:必须在非事务中,否则抛异常
NESTED:嵌套事务(Savepoint)
9.3 知识图谱
┌──────────────────────────────────────────────────────────────────────────┐
│ Spring IOC/AOP 知识图谱 │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ IOC容器 │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ refresh │ │ BeanDef │ │ 依赖注入 │ │ 循环依赖 │ │ │
│ │ │ 启动流程 │ │ 注册解析 │ │ 三种方式 │ │ 三级缓存 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ Bean生命周期 │ │
│ │ │ │
│ │ 实例化 → 属性填充 → Aware注入 → 初始化 → 销毁 │ │
│ │ │ │
│ │ 关键扩展点:BeanPostProcessor、InitializingBean、DisposableBean │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ AOP切面编程 │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ 切入点 │ │ 通知类型 │ │ JDK代理 │ │ CGLIB代理 │ │ │
│ │ │ @Pointcut│ │ 5种通知 │ │ 基于接口 │ │ 基于继承 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ 事务管理 │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ @Transactional│ │传播行为 │ │ 事务实现 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
结语
这篇文章我们从Spring IOC/AOP的源码层面进行了深度剖析,涵盖了:
核心要点回顾:
- IOC容器启动:
refresh()方法是入口,核心流程包括BeanDefinition加载、Bean实例化、属性填充、初始化等 - Bean生命周期:从构造方法到销毁回调,共10个关键步骤
- 依赖注入:构造方法注入(推荐)、Setter注入、字段注入三种方式
- 循环依赖:三级缓存完美解决,核心是提前暴露Bean
- AOP原理:JDK代理 vs CGLIB代理,Spring根据情况自动选择
- 事务传播:7种传播行为解决嵌套调用问题
- 自定义注解:结合AOP实现缓存、限流等高级功能
Spring的设计之美在于解耦和扩展。希望这篇文章能帮助大家理解框架的底层原理,写出更优雅的代码!
本文首发于掘金,同步更新于CSDN
更多优质内容,欢迎关注我的博客