🌟 总体理解: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 动态代理(实现这些接口)
目标类实现了接口,但未设置 proxyInterfacesproxyTargetClass ✅ 自动使用 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 的用法

欢迎继续提问 😊

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐