Spring AOP代理机制详解:ProxyFactoryBean全解析
ProxyFactoryBean是Spring早期用于手动创建AOP代理的工厂Bean,通过FactoryBean机制返回代理对象而非自身。它支持JDK动态代理和CGLIB两种方式,通过proxyTargetClass等属性控制代理行为。关键配置包括:proxyInterfaces指定接口,interceptorNames定义拦截链,target设置目标对象。当目标类无接口时自动使用CGLIB,有
🌟 总体理解:ProxyFactoryBean 是什么?
ProxyFactoryBean 是 Spring 框架早期用于手动创建 AOP 代理对象 的一个工厂 Bean。
- 它属于 Spring AOP 的底层 API。
- 使用它可以在 Spring IoC 容器中定义一个“代理”,这个代理会包装目标对象(target),并在方法调用前后插入切面逻辑(如日志、事务等)。
- 虽然现在更推荐使用 基于注解或自动代理(如
<aop:config>或@EnableAspectJAutoProxy)的方式,但理解ProxyFactoryBean有助于掌握 Spring AOP 的工作原理。
🔍 分节解析
✅ 6.4.1 基本概念:什么是 FactoryBean?为什么需要 ProxyFactoryBean?
核心要点:
ProxyFactoryBean是一个特殊的 Spring Bean ——FactoryBean。FactoryBean不返回自己本身,而是通过它的getObject()方法返回另一个对象。- 所以当你从容器获取名为
person的 bean 时,实际上拿到的是一个 动态生成的 AOP 代理对象,而不是ProxyFactoryBean实例本身。
💡 类比:就像面包机(
ProxyFactoryBean)不是面包,但它能做出你要吃的面包(代理对象)。
优势:
- 所有组件(目标对象、通知 advice、切入点 pointcut)都可以由 Spring 管理(IoC)。
- 可以实现依赖注入,比如某个 Advice 可以引用其他 service bean,这是很多 AOP 框架做不到的。
✅ 6.4.2 JavaBean 属性:配置代理的关键参数
ProxyFactoryBean 是一个 JavaBean,因此可以通过 XML 配置属性来控制行为:
| 属性名 | 含义 |
|---|---|
proxyTargetClass |
是否对类进行代理(而非接口)。设为 true 则使用 CGLIB 代理。默认 false。 |
optimize |
是否启用 CGLIB 优化(谨慎使用,可能影响稳定性)。仅对 CGLIB 有效。 |
frozen |
代理创建后是否“冻结”。若为 true,之后不能再修改代理的 advice 链。性能略好,安全性更高。 |
exposeProxy |
是否把当前代理暴露到线程局部变量(ThreadLocal),这样在目标方法内部可以用 AopContext.currentProxy() 获取代理本身。适用于自调用场景。 |
proxyInterfaces |
明确指定要代理哪些接口(字符串数组),例如 "com.example.Person"。如果不设置,则尝试自动检测。 |
interceptorNames |
拦截器/通知的名字列表(必须是字符串 bean 名称),顺序很重要!先添加的先执行。 |
singleton |
是否单例。如果是 false,每次 getBean 都会新建一个代理实例,适合状态性 advice(stateful advice)。 |
⚠️ 注意:
interceptorNames不能写成<ref>引用,因为要支持 prototype 类型的 advice。
✅ 6.4.3 JDK 动态代理 vs CGLIB 代理:Spring 如何选择?
这是非常重要的知识点!
Spring 根据你的配置决定使用哪种代理机制:
| 条件 | 使用哪种代理? |
|---|---|
| 目标类没有实现任何接口 | ✅ 自动使用 CGLIB 代理(子类方式) |
目标类实现了接口,并且设置了 proxyTargetClass=true |
✅ 使用 CGLIB 代理(即使有接口也忽略) |
设置了 proxyInterfaces 数组 |
✅ 使用 JDK 动态代理(实现这些接口) |
目标类实现了接口,但未设置 proxyInterfaces 和 proxyTargetClass |
✅ 自动使用 JDK 动态代理,代理所有接口 |
🧠 小贴士:
- JDK 动态代理:基于接口,生成实现相同接口的代理类。
- CGLIB 代理:基于继承,生成目标类的子类(所以不能代理 final 类或 final 方法)。
- Spring 3.2+ 已内置 CGLIB(repackaged into
spring-core),无需额外引入 jar 包。
✅ 6.4.4 示例:代理接口(JDK 动态代理)
<bean id="personTarget" class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<property name="age" value="51"/>
</bean>
<bean id="myAdvisor" class="com.mycompany.MyAdvisor"/>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<!-- 创建代理 -->
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mycompany.Person"/>
<property name="target" ref="personTarget"/>
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
<value>debugInterceptor</value>
</list>
</property>
</bean>
解释:
person这个 bean 实际上是一个代理对象。- 外部代码可以像使用普通
Person对象一样使用它:Person person = (Person) ctx.getBean("person"); person.getName(); // 调用会被拦截 - 如果其他 bean 依赖
person,也能正常注入,完全透明。
匿名内部 Bean 技巧(推荐)
你可以把目标对象内联定义,避免暴露原始对象:
<property name="target">
<bean class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<property name="age" value="51"/>
</bean>
</property>
好处:
- 防止别人直接访问未增强的目标对象。
- 更清晰的封装。
✅ 6.4.5 代理类(CGLIB 代理)
如果你的目标类 没有实现任何接口,或者你想强制代理整个类(包括非接口方法),就需要开启 CGLIB。
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 关键:启用类代理 -->
<property name="proxyTargetClass" value="true"/>
<property name="target">
<bean class="com.mycompany.Person"> <!-- 没有实现接口 -->
<property name="name" value="Tony"/>
</bean>
</property>
<property name="interceptorNames">
<list>
<value>debugInterceptor</value>
</list>
</property>
</bean>
CGLIB 的特点:
- 通过生成子类来实现代理(运行时字节码增强)。
- 可以代理类的所有非
final方法。 - 不能代理
final方法或final类。 - 性能与 JDK 动态代理相差不大,一般不用作为选型依据。
✅ 6.4.6 “全局”通知(Global Advisors)
利用通配符 * 可以批量注册多个 advisor:
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="service"/>
<property name="interceptorNames">
<list>
<value>global*</value> <!-- 匹配所有以 global 开头的 bean -->
</list>
</property>
</bean>
<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>
作用:
- 所有名字以
global_开头的 advisor 都会被加入拦截链。 - 很适合统一的日志、监控、性能统计等功能。
🧩 总结图:Spring 如何选择代理类型?
┌─────────────┐
│ 目标类存在吗?│
└──────┬──────┘
│
┌───────────────┴────────────────┐
▼ ▼
实现了至少一个接口? 没有实现接口?
│ │
┌─────────┴──────────┐ ┌──────────┴──────────┐
▼ ▼ ▼ ▼
proxyTargetClass=true? proxyInterfaces 设置? 必须使用 CGLIB JDK 代理所有接口
│ │
▼ ▼
使用 CGLIB 使用 JDK 动态代理
✅ 最佳实践建议(虽然现在很少手写)
尽管现代开发中多用自动代理(@AspectJ + @EnableAspectJAutoProxy),但理解 ProxyFactoryBean 仍然有价值:
| 场景 | 推荐做法 |
|---|---|
| 想要细粒度控制某个特定 bean 的代理 | 可考虑 ProxyFactoryBean |
| 统一对多个 bean 应用切面 | 使用 BeanNameAutoProxyCreator 或 <aop:config> |
| 使用注解驱动 AOP | 推荐 @Aspect + @Around/@Before 等 |
| 需要代理类而非接口 | 确保 proxyTargetClass=true 或启用 <aop:aspectj-autoproxy proxy-target-class="true"/> |
📌 补充说明(针对当前时代)
你现在看到的内容属于 Spring 2.x ~ 3.x 时代的经典 AOP 编程模型。
如今更常见的写法是:
✅ 注解方式(主流)
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
}
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.mycompany.*.*(..))")
public void log() {
System.out.println("Method is called...");
}
}
XML 方式(包括 ProxyFactoryBean)主要用于老项目维护。
✅ 结语
你提供的文本完整展示了 Spring AOP 在低层是如何工作的:
ProxyFactoryBean就像是一个“装配线”,你告诉它:
- 我要代理谁(target)
- 要实现哪些接口(proxyInterfaces)
- 要应用哪些增强逻辑(interceptorNames)
- 是否代理类本身(proxyTargetClass)
➜ 它就会给你造出一个带拦截功能的代理对象!
虽然现在我们更多用“自动装配”的方式来做 AOP,但了解 ProxyFactoryBean 能让你真正明白 Spring AOP 的本质:基于代理的运行时织入(weaving)机制。
如果你想进一步学习,我可以继续讲解:
Advised接口的作用DebugInterceptor,PerformanceMonitorInterceptor实际做什么- 与 AspectJ 的区别
BeanNameAutoProxyCreator的用法
欢迎继续提问 😊
更多推荐


所有评论(0)