使用“自动代理 (Auto-proxy)”功能
到目前为止,我们已经讨论了通过使用 ProxyFactoryBean 或类似的工厂 Bean 来显式创建 AOP 代理。
Spring 还允许我们使用“自动代理” Bean 定义,它可以自动代理选定的 Bean 定义。这是建立在 Spring 的 Bean 后置处理器 (Bean Post Processor) 基础设施之上的,它允许在容器加载时修改任何 Bean 定义。
在这种模型中,你在 XML Bean 定义文件中设置一些特殊的 Bean 定义,以配置自动代理基础设施。这让你可以声明符合自动代理条件的候选目标。你无需使用 ProxyFactoryBean。
实现这一点有两种主要方式:
- 使用在当前上下文中引用特定 Bean 的自动代理创建器。
- 由源码级元数据属性(属性驱动)驱动的自动代理创建(通常指注解驱动)。
自动代理创建器
本节介绍 org.springframework.aop.framework.autoproxy 包提供的自动代理创建器。
BeanNameAutoProxyCreator
BeanNameAutoProxyCreator 类是一个 BeanPostProcessor,它会自动为名称与字面值或通配符匹配的 Bean 创建 AOP 代理。以下示例演示了如何配置:
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="jdk*,onlyJdk"/>
<property name="interceptorNames">
<list>
<value>myInterceptor</value>
</list>
</property>
</bean>与 ProxyFactoryBean 一样,这里使用了 interceptorNames 属性而不是拦截器列表,以便为原型(Prototype)类型的 Advisor 提供正确的行为。命名的“拦截器”可以是 Advisor 或任何通知类型。
使用 BeanNameAutoProxyCreator 的主要目的是以最少的配置量,一致地将相同的配置应用于多个对象。它是为多个对象应用声明式事务的流行选择。
在上述示例中,名称匹配的 Bean 定义(如 jdkMyBean 和 onlyJdk)只是带有目标类的普通 Bean 定义。AOP 代理会由 BeanNameAutoProxyCreator 自动创建。相同的通知将应用于所有匹配的 Bean。请注意,如果使用的是 Advisor(而不是上例中的拦截器),则切入点可能会以不同的方式应用于不同的 Bean。
DefaultAdvisorAutoProxyCreator
一个更通用且极其强大的自动代理创建器是 DefaultAdvisorAutoProxyCreator。它会自动地应用当前上下文中所有符合条件的 Advisor,而无需在自动代理创建器的 Bean 定义中包含特定的 Bean 名称。它提供了与 BeanNameAutoProxyCreator 相同的配置一致性和避免重复的优点。
使用此机制涉及:
- 指定一个
DefaultAdvisorAutoProxyCreatorBean 定义。 - 在同一上下文中指定任意数量的 Advisor。请注意,这些必须是 Advisor,而不能只是拦截器或通知。这是必要的,因为必须有一个切入点来评估每个通知对候选 Bean 定义的适用性。
DefaultAdvisorAutoProxyCreator 会自动评估每个 Advisor 中包含的切入点,以查看它应该将哪些通知应用于每个业务对象。
这意味着任意数量的 Advisor 都可以自动应用于每个业务对象。如果没有任何 Advisor 中的切入点匹配业务对象中的任何方法,则该对象不会被代理。随着新业务对象的 Bean 定义被添加,如果需要,它们将被自动代理。
自动代理的一个普遍优势是,它使调用者或依赖者不可能获得未经过通知处理(un-advised)的对象。在这个 ApplicationContext 上调用 getBean("businessObject1") 将返回一个 AOP 代理,而不是目标业务对象。
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
<property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>
<bean id="customAdvisor" class="com.mycompany.MyAdvisor"/>
<bean id="businessObject1" class="com.mycompany.BusinessObject1">
<!-- 属性省略 -->
</bean>
<bean id="businessObject2" class="com.mycompany.BusinessObject2"/>如果你想一致地将相同的通知应用于许多业务对象,DefaultAdvisorAutoProxyCreator 非常有用。一旦基础设施定义到位,你就可以添加新的业务对象,而无需包含特定的代理配置。你还可以轻松地添加额外的切面(例如跟踪或性能监控切面),且配置改动极小。
补充教学
1. 核心原理:BeanPostProcessor 到底做了什么?
自动代理能成功的魔法核心在于 BeanPostProcessor 接口。
- 介入初始化:当 Spring 容器实例化一个 Bean 之后,在执行
init-method前后,会调用后置处理器。 - 偷梁换柱:自动代理创建器会扫描所有的
Advisor,如果发现当前 Bean 匹配某个 Advisor 的切入点,它就会在内存中调用ProxyFactory创建一个代理对象,并返回这个代理对象而不是原始对象。 - 结果:容器中最终存储的是那个被掉包后的代理。
2. 为什么 DefaultAdvisorAutoProxyCreator 是“终极武器”?
- 真正的解耦:目标 Bean 甚至根本不知道 AOP 的存在,拦截器那边也不需要知道自己要拦谁(由切入点表达式决定)。
- 易出错点:如果你忘了把通知包装成
Advisor(即没有切入点),DefaultAdvisorAutoProxyCreator将无法识别它。记住:Advice + Pointcut = Advisor。
3. 现代 Spring Boot 的等价物
虽然文章中讨论的是 XML 配置,但在现代开发中:
@EnableAspectJAutoProxy:这个注解背后注册的就是AnnotationAwareAspectJAutoProxyCreator,它是DefaultAdvisorAutoProxyCreator的子类。- 它不仅支持传统的
Advisor接口,还支持识别标注了@Aspect注解的 POJO 类。
4. 常见坑点:循环依赖
在使用自动代理时,如果不小心让一个 Advisor 依赖了它自己要拦截的 Bean,或者拦截逻辑涉及到了还没初始化完的 Bean,可能会触发 Spring 的循环依赖报错。
- 诊断方法:查看启动日志中是否有关于 “BeanPostProcessor checker” 的警告或
BeanCurrentlyInCreationException。 - 解决建议:尽量保持 Advisor 和拦截器的逻辑轻量,不依赖复杂的业务 Bean。