Appearance
Spring AOP 中的 ProxyFactoryBean:创建 AOP 代理的工厂利器 🏭
引言:为什么需要 ProxyFactoryBean?
在 Spring AOP 的世界里,我们经常需要为业务对象创建代理来实现横切关注点(如日志、事务、安全等)。想象一下,如果没有一个统一的工厂来管理这些代理的创建,我们就需要手动编写大量重复的代理创建代码。这就像没有汽车工厂,每个人都要自己组装汽车一样低效!
NOTE
ProxyFactoryBean 是 Spring AOP 框架中用于创建 AOP 代理的核心工厂 Bean,它提供了一种声明式的方式来配置和创建代理对象。
核心概念解析 🎯
ProxyFactoryBean 的本质
ProxyFactoryBean
是 Spring 的一个特殊的 FactoryBean
实现,它引入了一层间接性:
IMPORTANT
当你定义一个名为 foo
的 ProxyFactoryBean 时,引用 foo
的对象看到的不是 ProxyFactoryBean 实例本身,而是由其 getObject()
方法创建的 AOP 代理对象。
核心属性配置 ⚙️
继承自 ProxyConfig 的关键属性
kotlin
@Configuration
class AopConfig {
@Bean
fun proxyFactoryBean(): ProxyFactoryBean {
return ProxyFactoryBean().apply {
// 设置目标对象
setTarget(personTarget())
// 配置代理行为
isProxyTargetClass = false // 使用接口代理
isOptimize = false // 不启用激进优化
isFrozen = false // 允许配置修改
isExposeProxy = true // 暴露当前代理到ThreadLocal
// 设置要代理的接口
setProxyInterfaces(arrayOf("com.example.Person"))
// 设置拦截器链
setInterceptorNames(arrayOf("myAdvisor", "debugInterceptor"))
}
}
}
xml
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 目标对象 -->
<property name="target" ref="personTarget"/>
<!-- 代理配置 -->
<property name="proxyTargetClass" value="false"/>
<property name="optimize" value="false"/>
<property name="frozen" value="false"/>
<property name="exposeProxy" value="true"/>
<!-- 接口配置 -->
<property name="proxyInterfaces" value="com.example.Person"/>
<!-- 拦截器链 -->
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
<value>debugInterceptor</value>
</list>
</property>
</bean>
属性详解
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
proxyTargetClass | Boolean | false | 是否代理目标类而非接口 |
optimize | Boolean | false | 是否启用 CGLIB 优化 |
frozen | Boolean | false | 代理配置是否冻结 |
exposeProxy | Boolean | false | 是否将代理暴露到 ThreadLocal |
proxyInterfaces | String[] | null | 要代理的接口名数组 |
interceptorNames | String[] | null | 拦截器/通知器名称数组 |
TIP
exposeProxy
属性特别有用,当目标对象需要获取当前代理时,可以通过 AopContext.currentProxy()
方法获取。
代理类型选择机制 🔄
Spring 会根据不同的配置自动选择使用 JDK 动态代理还是 CGLIB 代理:
实战示例 💡
1. 接口代理示例
kotlin
// 业务接口
interface PersonService {
fun getName(): String
fun setName(name: String)
fun getAge(): Int
}
// 业务实现
@Component("personTarget")
class PersonServiceImpl : PersonService {
private var name: String = ""
private var age: Int = 0
override fun getName(): String {
println("获取姓名: $name")
return name
}
override fun setName(name: String) {
println("设置姓名: $name")
this.name = name
}
override fun getAge(): Int = age
}
kotlin
@Component("myAdvisor")
class LoggingAdvisor : PointcutAdvisor {
private val pointcut = object : StaticMethodMatcherPointcut() {
override fun matches(method: Method, targetClass: Class<*>): Boolean {
return method.name.startsWith("get")
}
}
private val advice = MethodInterceptor { invocation ->
val startTime = System.currentTimeMillis()
println("🚀 方法调用开始: ${invocation.method.name}")
try {
val result = invocation.proceed()
val endTime = System.currentTimeMillis()
println("✅ 方法调用成功: ${invocation.method.name}, 耗时: ${endTime - startTime}ms")
result
} catch (e: Exception) {
println("❌ 方法调用失败: ${invocation.method.name}, 异常: ${e.message}")
throw e
}
}
override fun getPointcut(): Pointcut = pointcut
override fun getAdvice(): Advice = advice
override fun isPerInstance(): Boolean = true
}
kotlin
@Configuration
class ProxyConfig {
@Bean
fun personProxy(): ProxyFactoryBean {
return ProxyFactoryBean().apply {
setTarget(personTarget())
setProxyInterfaces(arrayOf("com.example.PersonService"))
setInterceptorNames(arrayOf("myAdvisor", "debugInterceptor"))
}
}
@Bean
fun personTarget(): PersonService {
return PersonServiceImpl().apply {
setName("张三")
}
}
@Bean
fun debugInterceptor(): DebugInterceptor {
return DebugInterceptor()
}
}
2. 使用代理
kotlin
@Service
class BusinessService {
@Autowired
@Qualifier("personProxy")
private lateinit var personService: PersonService
fun doSomething() {
// 这里调用的是代理对象,会触发通知
val name = personService.getName()
println("业务逻辑处理: $name")
// 检查代理类型
if (personService is Advised) {
println("这是一个被通知的代理对象")
println("代理类型: ${personService.javaClass.simpleName}")
}
}
}
WARNING
注意:通过 ProxyFactoryBean 创建的代理对象可以转换为 Advised
接口,从而可以在运行时动态修改通知配置(除非设置了 frozen=true
)。
3. 类代理示例(CGLIB)
kotlin
// 没有接口的类
@Component("personClassTarget")
class PersonClass {
var name: String = ""
var age: Int = 0
open fun getName(): String {
println("获取姓名: $name")
return name
}
open fun setName(name: String) {
println("设置姓名: $name")
this.name = name
}
final fun getAge(): Int = age
// final 方法无法被 CGLIB 代理
}
@Configuration
class CglibProxyConfig {
@Bean
fun personClassProxy(): ProxyFactoryBean {
return ProxyFactoryBean().apply {
setTarget(personClassTarget())
isProxyTargetClass = true
// 强制使用 CGLIB 代理
setInterceptorNames(arrayOf("myAdvisor"))
}
}
}
CAUTION
使用 CGLIB 代理时需要注意:
final
类无法被代理final
方法无法被通知private
方法无法被通知- 构造函数会被调用两次(目标类和代理类各一次)
全局通知器配置 🌐
ProxyFactoryBean 支持使用通配符来应用全局通知器:
kotlin
@Configuration
class GlobalAdvisorConfig {
@Bean
fun serviceProxy(): ProxyFactoryBean {
return ProxyFactoryBean().apply {
setTarget(myService())
setInterceptorNames(arrayOf("global*"))
// 会匹配所有以 "global" 开头的通知器
}
}
@Bean("global_debug")
fun globalDebugInterceptor(): DebugInterceptor {
return DebugInterceptor()
}
@Bean("global_performance")
fun globalPerformanceInterceptor(): PerformanceMonitorInterceptor {
return PerformanceMonitorInterceptor()
}
@Bean("global_security")
fun globalSecurityInterceptor(): MethodInterceptor {
return MethodInterceptor { invocation ->
println("🔒 安全检查: ${invocation.method.name}")
// 执行安全检查逻辑
invocation.proceed()
}
}
}
高级特性 🚀
1. 匿名内部Bean配置
kotlin
@Bean
fun personProxyWithInnerBean(): ProxyFactoryBean {
return ProxyFactoryBean().apply {
// 使用匿名内部bean作为目标对象
setTarget(PersonServiceImpl().apply {
setName("李四")
})
setProxyInterfaces(arrayOf("com.example.PersonService"))
setInterceptorNames(arrayOf("myAdvisor"))
}
}
匿名内部Bean的优势
- 只有一个 PersonService 类型的对象存在于容器中
- 避免了获取未被通知的原始对象的可能性
- 配置更加自包含
2. 原型作用域支持
kotlin
@Bean
@Scope("prototype")
fun prototypeProxy(): ProxyFactoryBean {
return ProxyFactoryBean().apply {
setTarget(PersonServiceImpl())
setProxyInterfaces(arrayOf("com.example.PersonService"))
setInterceptorNames(arrayOf("statefulAdvisor"))
isSingleton = false
// 每次获取都创建新的代理实例
}
}
3. 代理暴露到ThreadLocal
kotlin
@Component
class SelfInvokingService {
fun outerMethod() {
println("外部方法调用")
// 获取当前代理并调用内部方法
val proxy = AopContext.currentProxy() as SelfInvokingService
proxy.innerMethod() // 这样调用会触发AOP通知
}
fun innerMethod() {
println("内部方法调用")
}
}
@Bean
fun selfInvokingProxy(): ProxyFactoryBean {
return ProxyFactoryBean().apply {
setTarget(SelfInvokingService())
isProxyTargetClass = true
isExposeProxy = true
setInterceptorNames(arrayOf("loggingAdvisor"))
}
}
性能考量 ⚡
JDK 动态代理 vs CGLIB 代理
特性 | JDK 动态代理 | CGLIB 代理 |
---|---|---|
基础要求 | 目标类必须实现接口 | 无接口要求 |
性能 | 略快 | 略慢(但差异很小) |
内存占用 | 较少 | 较多 |
创建速度 | 快 | 慢(需要生成字节码) |
方法调用 | 通过反射 | 直接调用 |
NOTE
在现代 JVM 上,JDK 动态代理和 CGLIB 代理的性能差异已经非常小,选择时应该更多考虑设计因素而非性能因素。
最佳实践 📋
1. 优先使用接口
kotlin
// ✅ 推荐:基于接口的设计
interface UserService {
fun createUser(user: User): User
fun findUser(id: Long): User?
}
@Service
class UserServiceImpl : UserService {
override fun createUser(user: User): User {
// 实现逻辑
return user
}
override fun findUser(id: Long): User? {
// 查询逻辑
return null
}
}
2. 合理配置拦截器顺序
kotlin
@Bean
fun orderedProxy(): ProxyFactoryBean {
return ProxyFactoryBean().apply {
setTarget(businessService())
setInterceptorNames(arrayOf(
"securityAdvisor", // 1. 安全检查
"transactionAdvisor", // 2. 事务管理
"loggingAdvisor", // 3. 日志记录
"performanceAdvisor" // 4. 性能监控
))
}
}
3. 避免过度使用frozen配置
kotlin
@Bean
fun flexibleProxy(): ProxyFactoryBean {
return ProxyFactoryBean().apply {
setTarget(dynamicService())
isFrozen = false
// 保持灵活性,允许运行时修改配置
setInterceptorNames(arrayOf("baseAdvisor"))
}
}
总结 📝
ProxyFactoryBean 是 Spring AOP 中创建代理对象的核心工厂,它提供了:
- 🏭 统一的代理创建机制:通过配置而非编程方式创建代理
- 🔧 灵活的配置选项:支持多种代理策略和行为定制
- 🎯 智能的代理选择:自动选择最适合的代理技术
- 🌐 强大的通知器管理:支持复杂的通知器链和全局通知器
- 🔄 与 IoC 容器深度集成:充分利用依赖注入的优势
IMPORTANT
虽然 ProxyFactoryBean 功能强大,但在现代 Spring 应用中,更推荐使用 @AspectJ
注解或 <aop:config>
等更简洁的方式来配置 AOP。ProxyFactoryBean 更适合需要精细控制代理创建过程的场景。
通过掌握 ProxyFactoryBean,你将能够更深入地理解 Spring AOP 的工作原理,并在需要时创建高度定制化的 AOP 解决方案! 🎉