Spring IoC、AOP 与 Bean 全面详解
代理模式(Proxy Pattern)是一种结构型设计模式,其核心思想是:为某个对象提供一个代理对象,由代理对象控制对原始对象的访问,从而在不修改原始类的前提下,增强其功能。把代理对象当作经纪人,把原始对象当明星,明星有完成演出的功能,经纪人负责在明星演出的前后做各种增强准备。客户端 ──► 代理对象(Proxy)──► 目标对象(Target)│增强逻辑(前置/后置/异常处理)IoC(Inver
本文深入讲解 Spring 框架的核心机制:IoC 容器、AOP 面向切面编程、Spring Bean 生命周期,并重点总结 AOP 代理失效场景与代理原理。
1. 什么是代理(Proxy)
1.1 代理模式概念
代理模式(Proxy Pattern)是一种结构型设计模式,其核心思想是:为某个对象提供一个代理对象,由代理对象控制对原始对象的访问,从而在不修改原始类的前提下,增强其功能。
把代理对象当作经纪人,把原始对象当明星,明星有完成演出的功能,经纪人负责在明星演出的前后做各种增强准备。
客户端 ──► 代理对象(Proxy)──► 目标对象(Target)
│
增强逻辑(前置/后置/异常处理)
1.2 静态代理
静态代理由开发者手动编写代理类,代理类与目标类实现同一接口。
// 目标接口
public interface UserService {
void save(String name);
}
// 目标实现
public class UserServiceImpl implements UserService {
@Override
public void save(String name) {
System.out.println("保存用户: " + name);
}
}
// 静态代理类
public class UserServiceProxy implements UserService {
private final UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void save(String name) {
System.out.println("方法执行前");
target.save(name);
System.out.println("方法执行后");
}
}
缺点:每个类都需要手写代理,代码冗余,扩展性差。
1.3 动态代理
动态代理在运行时自动生成代理类,Spring AOP 使用了两种动态代理:
JDK 动态代理(基于接口)
- 要求目标类实现接口
- 通过
java.lang.reflect.Proxy和InvocationHandler实现 - 生成的代理类与目标类实现同一接口
public class JdkProxyDemo {
public static void main(String[] args) {
// 1. 明星本人:创建真实的目标对象(具体的业务实现)
UserService target = new UserServiceImpl();
// 2. 造星机器:使用 Proxy 动态创建代理对象(即经纪人)
// Proxy.newProxyInstance 返回的是 Object要强转
UserService proxy = (UserService) Proxy.newProxyInstance(
// 参数 A:类加载器。告诉 JVM 用哪个加载器把这个新生成的“代理类”加载进内存
target.getClass().getClassLoader(),
// 参数 B:接口列表。明星要做什么,经纪人就得表现出懂什么业务(save方法)
target.getClass().getInterfaces(),
// 参数 C:经纪人的大脑 (InvocationHandler)。
// 当代理对象的方法被调用时,所有的请求都会转发到这个 Lambda 表达式中
(proxyObj, method, methodArgs) -> {
// 前置:明星上台前的动作(如:检查权限、打印日志、开启事务)
System.out.println("[JDK代理] 前置增强:收尾款、调音响");
// 核心:反射调用。
// method:当前被调用的方法(save)
// target:真正干活的明星本人
// methodArgs:调用时传的参数("张三")
// 这行代码的意思是:调用【target】对象的【method】方法,传入【methodArgs】参数
Object result = method.invoke(target, methodArgs);
// 后置:明星下台后的动作(如:释放连接、记录耗时、提交事务)
System.out.println("[JDK代理] 后置增强:送明星回酒店");
// 返回明星演出的结果(如果有返回值的话)
return result;
}
);
// 3. 【正式演出】:客户通过代理对象发起调用
// 此时,程序并不会直接跳到 UserServiceImpl,而是先进入上面的 Lambda 逻辑
proxy.save("张三");
}
}
CGLIB 动态代理(基于继承)
- 不需要目标类实现接口
- 通过继承目标类,重写方法来实现代理
- Spring Boot 2.x 起默认使用 CGLIB
public class CglibProxyDemo {
public static void main(String[] args) {
// 1. 造星工厂:Enhancer 是 CGLIB 的核心类,用于生成代理对象
Enhancer enhancer = new Enhancer();
// 2. 确定血缘:设置父类。
// CGLIB 是通过“继承”目标类来工作的。
// 这里的代理对象会变成 UserServiceImpl 的一个“儿子”。
enhancer.setSuperclass(UserServiceImpl.class);
// 3. 设置拦截器:定义经纪人的逻辑。
// MethodInterceptor 接口相当于 JDK 中的 InvocationHandler
enhancer.setCallback((MethodInterceptor) (obj, method, methodArgs, proxy) -> {
// 前置:明星上台前干的事
System.out.println("[CGLIB代理] 前置增强:我是子类代理,我先接活");
// 核心:调用父类的方法。
// 注意:这里用的是 proxy.invokeSuper,而不是 method.invoke。
// 它的意思是:去执行我“爸爸”(UserServiceImpl)里的那个真实方法。
Object result = proxy.invokeSuper(obj, methodArgs);
// 后置:明星下台后干的事
System.out.println("[CGLIB代理] 后置增强:演出结束,清理现场");
return result;
});
// 4. 创建代理对象:在内存中生成子类并实例化
// 因为它是 UserServiceImpl 的子类,所以可以直接强转成 UserServiceImpl
UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
// 5. 执行:调用代理对象的方法
proxy.save("李四");
}
}
1.4 JDK 动态代理 vs CGLIB 对比
| 特性 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 代理方式 | 基于接口 | 基于继承(子类) |
| 要求 | 目标类必须实现接口 | 目标类不能是 final |
| 性能 | 反射调用,略慢 | 字节码操作,略快 |
| Spring 默认 | Spring 4.x 及以前 | Spring Boot 2.x 起默认 |
代理 final 方法 |
不支持 | 不支持 |
2. Spring IoC 详解
2.1 什么是 IoC
IoC(Inversion of Control,控制反转) 是一种设计思想,核心是将对象的创建权和依赖管理权从开发者手中转交给 Spring 容器。
- 传统方式:对象自己
new依赖对象,主动控制 - IoC 方式:对象只声明依赖,由容器负责注入,被动接受
DI(Dependency Injection,依赖注入) 是 IoC 的具体实现方式。
传统:A 类 ──new──► B 类 (A 控制 B 的创建)
IoC :容器创建 A 和 B,将 B 注入 A (容器控制一切)
2.2 IoC 容器体系
BeanFactory(根接口)
└── ApplicationContext(扩展接口)
├── ClassPathXmlApplicationContext (从类路径加载 XML)
├── FileSystemXmlApplicationContext (从文件系统加载 XML)
├── AnnotationConfigApplicationContext(注解配置)
└── WebApplicationContext (Web 环境)
| 对比项 | BeanFactory | ApplicationContext |
|---|---|---|
| Bean 实例化时机 | 第一次使用时(懒加载) | 容器启动时(预实例化) |
| 功能 | 基础 IoC 功能 | IoC + AOP + 事件 + 国际化等 |
| 适合场景 | 资源受限环境 | 绝大多数场景 |
2.3 依赖注入三种方式
构造器注入(推荐)
@Service
public class OrderService {
private final UserRepository userRepository;
private final PaymentService paymentService;
public OrderService(UserRepository userRepository,
PaymentService paymentService) {
this.userRepository = userRepository;
this.paymentService = paymentService;
}
}
优点:依赖不可变(final)、对象完整性保证、便于单元测试
Setter 注入
@Service
public class OrderService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
适用:可选依赖、需要支持后期重新注入的场景
字段注入(不推荐)
@Service
public class OrderService {
@Autowired
private UserRepository userRepository; // 不推荐:隐藏依赖、不易测试
}
缺点:依赖关系隐蔽、无法注入 final 字段、不利于单元测试
2.4 IoC 容器启动流程
1. 读取配置(XML / 注解 / JavaConfig)
↓
2. 解析 BeanDefinition(Bean 的元数据)
↓
3. 注册 BeanDefinition 到 BeanDefinitionRegistry
↓
4. BeanFactoryPostProcessor 扩展点(修改 BeanDefinition)
↓
5. 实例化非懒加载的单例 Bean
↓
6. 依赖注入(填充属性)
↓
7. BeanPostProcessor 扩展点(初始化前后增强)
↓
8. 容器就绪,对外提供服务
2.5 @Autowired 注入原理
@Autowired 由 AutowiredAnnotationBeanPostProcessor 处理:
- 按类型(byType) 查找匹配的 Bean
- 找到多个时,按名称(byName) 精确匹配
- 仍有歧义时,结合
@Qualifier指定或@Primary优先
// 有多个实现时,使用 @Qualifier 指定
@Autowired
@Qualifier("mysqlUserRepository")
private UserRepository userRepository;
// 或用 @Primary 标注优先使用的实现
@Primary
@Repository
public class MysqlUserRepository implements UserRepository { ... }
3. Spring Bean 详解
3.1 Bean 的定义
Spring Bean 是由 Spring IoC 容器管理的对象。容器负责 Bean 的创建、装配、初始化和销毁全过程。
3.2 Bean 的作用域(Scope)
| 作用域 | 说明 | 适用场景 |
|---|---|---|
singleton(默认) |
整个容器中只有一个实例 | 无状态服务类 |
prototype |
每次请求创建新实例 | 有状态对象 |
request |
每个 HTTP 请求创建一个实例 | Web 请求相关数据 |
session |
每个 HTTP 会话创建一个实例 | 用户会话数据 |
application |
整个 Web 应用一个实例 | 全局共享数据 |
@Component
@Scope("prototype")
public class ShoppingCart {
private List<Item> items = new ArrayList<>();
// ...
}
3.3 Bean 的生命周期

生命周期代码示例
@Component
public class MyBean implements InitializingBean, DisposableBean,
BeanNameAware, ApplicationContextAware {
private String beanName;
// 1. 构造器 - 实例化
public MyBean() {
System.out.println("1. 实例化:构造函数执行");
}
// 2. Aware 回调
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("3. BeanNameAware:beanName = " + name);
}
@Override
public void setApplicationContext(ApplicationContext ctx) {
System.out.println("3. ApplicationContextAware 回调");
}
// 3. @PostConstruct 初始化
@PostConstruct
public void postConstruct() {
System.out.println("5. @PostConstruct 初始化方法 启动自检");
}
// 4. InitializingBean 初始化
@Override
public void afterPropertiesSet() {
System.out.println("5. InitializingBean.afterPropertiesSet()");
}
// 5. @PreDestroy 销毁
@PreDestroy
public void preDestroy() {
System.out.println("8. @PreDestroy 销毁前");
}
// 6. DisposableBean 销毁
@Override
public void destroy() {
System.out.println("8. DisposableBean.destroy()");
}
}
3.4 Bean 的注册方式
// 方式1:注解(最常用)
@Component / @Service / @Repository / @Controller
// 方式2:@Bean 方法
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
}
// 方式3:XML 配置(传统)
// <bean id="userService" class="com.example.UserServiceImpl"/>
// 方式4:编程式注册
GenericBeanDefinition beanDef = new GenericBeanDefinition();
beanDef.setBeanClass(UserServiceImpl.class);
registry.registerBeanDefinition("userService", beanDef);
3.5 循环依赖问题
Spring 通过三级缓存解决单例 Bean 的循环依赖:
一级缓存(singletonObjects) :存放完整初始化的 Bean
二级缓存(earlySingletonObjects):存放提前暴露的早期 Bean(未完成初始化)
三级缓存(singletonFactories) :存放 ObjectFactory(用于生成早期引用)
解决流程(A 依赖 B,B 依赖 A):
1. 创建 A → 将 A 的 ObjectFactory 放入三级缓存
2. A 注入属性,发现需要 B
3. 创建 B → 将 B 的 ObjectFactory 放入三级缓存
4. B 注入属性,发现需要 A → 从三级缓存获取 A 的早期引用
5. B 初始化完成,放入一级缓存
6. A 拿到 B,完成初始化,放入一级缓存
⚠️ 构造器循环依赖无法解决(因为无法提前暴露未完成的对象),Spring 会抛出
BeanCurrentlyInCreationException。
4. Spring AOP 详解
4.1 什么是 AOP
AOP(Aspect-Oriented Programming,面向切面编程) 是对 OOP 的补充,用于处理横切关注点(Cross-Cutting Concerns),如日志、事务、安全、缓存等。
OrderService UserService PaymentService
│ │ │
日志 ────────┼────────────────┼───────────────┼────── 横切关注点
事务 ────────┼────────────────┼───────────────┼────── 横切关注点
鉴权 ────────┼────────────────┼───────────────┼────── 横切关注点
4.2 AOP 核心概念
| 概念 | 英文 | 说明 |
|---|---|---|
| 切面 | Aspect | 封装横切逻辑的模块(切点 + 通知) |
| 切点 | Pointcut | 定义在哪些方法上织入逻辑(匹配规则) |
| 通知 | Advice | 具体的增强逻辑(前置、后置、环绕等) |
| 连接点 | JoinPoint | 可被拦截的程序执行点(方法调用) |
| 织入 | Weaving | 将切面逻辑插入目标对象的过程 |
| 目标对象 | Target | 被代理的原始对象 |
| 代理对象 | Proxy | 包含增强逻辑的代理对象 |
4.3 通知类型
@Aspect
@Component
public class LogAspect {
// 切点表达式(可复用)
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
// 前置通知:在目标方法执行前
@Before("serviceLayer()")
public void before(JoinPoint jp) {
System.out.println("前置通知:" + jp.getSignature().getName());
}
// 后置通知:目标方法执行后(无论是否异常)
@After("serviceLayer()")
public void after(JoinPoint jp) {
System.out.println("后置通知(Finally)");
}
// 返回通知:目标方法正常返回后
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void afterReturning(Object result) {
System.out.println("返回通知,返回值:" + result);
}
// 异常通知:目标方法抛出异常后
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void afterThrowing(Exception ex) {
System.out.println("异常通知:" + ex.getMessage());
}
// 环绕通知:最强大,包围目标方法执行
@Around("serviceLayer()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知 - 前");
long start = System.currentTimeMillis();
try {
Object result = pjp.proceed(); // 执行目标方法
System.out.println("环绕通知 - 后,耗时:"
+ (System.currentTimeMillis() - start) + "ms");
return result;
} catch (Exception e) {
System.out.println("环绕通知 - 异常");
throw e;
}
}
}
执行顺序(正常情况):
环绕前 → 前置通知 → 目标方法 → 返回通知 → 后置通知 → 环绕后
执行顺序(异常情况):
环绕前 → 前置通知 → 目标方法抛异常 → 异常通知 → 后置通知 → 环绕异常处理
4.4 切点表达式语法
// execution 表达式语法
execution([修饰符] 返回类型 [类路径].方法名(参数) [异常])
// 示例
execution(* com.example.service.*.*(..)) // service 包下所有类所有方法
execution(public String com.example.UserService.*(..)) // UserService 的所有 public String 方法
execution(* *..UserService.save(..)) // 任意包下 UserService 的 save 方法
// @annotation 表达式:拦截带有特定注解的方法
@Pointcut("@annotation(com.example.annotation.Log)")
public void logMethods() {}
// within 表达式:拦截特定类内的所有方法
@Pointcut("within(com.example.service.*)")
public void inServiceLayer() {}
// args 表达式:拦截参数类型匹配的方法
@Pointcut("args(java.lang.String, ..)")
public void stringFirstArg() {}
4.5 Spring AOP 的实现原理
AOP 代理由 AbstractAutoProxyCreator(BeanPostProcessor的子类)在 Bean 初始化后创建:
Bean 初始化完成
↓
postProcessAfterInitialization()
↓
判断是否需要代理(是否有匹配的 Advisor)
↓
┌──────────────────────────────┐
│ 目标类实现了接口? │
│ 是 → JDK 动态代理(默认) │
│ 否 → CGLIB 代理 │
└──────────────────────────────┘
↓
返回代理对象(替换原 Bean)
5. AOP 代理失效场景总结
这是面试和实际开发中的高频问题,理解代理失效的根本原因是关键。
核心原因
AOP 代理失效的根本原因:方法调用绕过了代理对象,直接调用了目标对象的方法。
5.1 类内部方法自调用(最常见)
@Service
public class OrderService {
@Transactional
public void createOrder(OrderDTO dto) {
// ❌ 失效!this 指向的是目标对象,不是代理对象
this.validateAndSave(dto);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void validateAndSave(OrderDTO dto) {
// 事务不会开启新事务,因为代理被绕过
}
}
原因:this.validateAndSave() 调用的是原始对象的方法,没有经过 Spring 代理。
解决方案 1:注入自身代理
@Service
public class OrderService {
@Autowired
private OrderService self; // 注入代理对象
@Transactional
public void createOrder(OrderDTO dto) {
self.validateAndSave(dto); // ✅ 通过代理调用
}
}
解决方案 2:从 ApplicationContext 获取代理
@Service
public class OrderService implements ApplicationContextAware {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext ctx) {
this.context = ctx;
}
@Transactional
public void createOrder(OrderDTO dto) {
OrderService proxy = context.getBean(OrderService.class);
proxy.validateAndSave(dto); // ✅ 通过代理调用
}
}
解决方案 3:使用 AopContext.currentProxy()(推荐)
// 需要开启 exposeProxy
@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class App { ... }
@Service
public class OrderService {
@Transactional
public void createOrder(OrderDTO dto) {
((OrderService) AopContext.currentProxy()).validateAndSave(dto); // ✅
}
}
5.2 final 方法
@Service
public class UserService {
@Transactional
public final void updateUser(User user) { // ❌ CGLIB 无法重写 final 方法
// 事务注解不生效
}
}
原因:CGLIB 通过继承并重写方法来拦截调用,final 方法无法被子类重写,因此代理失效。
解决方案:去掉 final 关键字。
5.3 private 方法
@Service
public class UserService {
@Transactional
private void doSomething() { // ❌ 代理失效
// 事务不生效
}
}
原因:
- JDK 代理基于接口,接口中不存在私有方法
- CGLIB 基于继承,子类无法访问父类
private方法
解决方案:将方法改为 public 或 protected。
5.4 static 方法
@Service
public class CacheService {
@Cacheable("users")
public static User getUser(Long id) { // ❌ 代理失效
return userRepository.findById(id);
}
}
原因:静态方法属于类,而非对象实例,代理作用于对象实例,因此无法拦截静态方法。
解决方案:将静态方法改为实例方法。
5.5 目标对象未被 Spring 管理
// ❌ 手动 new 出来的对象,不是 Spring Bean,没有代理
UserService userService = new UserServiceImpl();
userService.save(user); // AOP 完全不生效
解决方案:通过 Spring 容器获取 Bean,而不是手动 new。
代理失效场景速查表
| # | 场景 | 根本原因 | 解决方案 |
|---|---|---|---|
| 1 | 类内部 this 调用 |
绕过代理对象 | AopContext.currentProxy() / 注入自身 |
| 2 | final 方法 |
CGLIB 无法继承重写 | 去掉 final |
| 3 | private 方法 |
代理无法访问私有方法 | 改为 public/protected |
| 4 | static 方法 |
静态方法属于类而非实例 | 改为实例方法 |
| 5 | 非 Spring 管理对象 | 没有经过容器创建,无代理 | 通过 Spring 容器获取 Bean |
总结
IoC → 解决对象创建和依赖管理问题(控制权交给容器)
Bean → IoC 容器管理的对象,有完整的生命周期
AOP → 解决横切关注点问题(基于代理实现)
代理 → AOP 的底层实现(JDK / CGLIB 动态代理)
理解 AOP 代理失效场景的核心要点:
Spring AOP 是基于代理的 AOP 实现,只有通过 Spring 容器获取的代理对象发起的方法调用,才能被 AOP 拦截。任何绕过代理对象的调用方式都会导致 AOP 失效。
更多推荐



所有评论(0)