编程式创建 @AspectJ 代理 (Programmatic Creation of @AspectJ Proxies)
除了通过使用 <aop:config> 或 <aop:aspectj-autoproxy> 在配置中声明切面外,还可以通过编程方式创建为目标对象提供通知的代理。关于 Spring AOP API 的详细细节,请参阅下一章。在这里,我们重点介绍使用 @AspectJ 切面自动创建代理的能力。
你可以使用 org.springframework.aop.aspectj.annotation.AspectJProxyFactory 类为一个或多个 @AspectJ 切面通知的目标对象创建代理。该类的基本用法非常简单,如下例所示:
java
// 创建一个可以为给定目标对象生成代理的工厂
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
// 添加一个切面,该类必须是一个 @AspectJ 切面
// 你可以根据需要多次调用此方法来添加不同的切面
factory.addAspect(SecurityManager.class);
// 你也可以添加现有的切面实例,所提供的对象类型必须是一个 @AspectJ 切面
factory.addAspect(usageTracker);
// 现在获取代理对象...
MyInterfaceType proxy = factory.getProxy();kotlin
// 创建一个可以为给定目标对象生成代理的工厂
val factory = AspectJProxyFactory(targetObject)
// 添加一个切面,该类必须是一个 @AspectJ 切面
// 你可以根据需要多次调用此方法来添加不同的切面
factory.addAspect(SecurityManager::class.java)
// 你也可以添加现有的切面实例,所提供的对象类型必须是一个 @AspectJ 切面
factory.addAspect(usageTracker)
// 现在获取代理对象...
val proxy = factory.getProxy<Any>()更多信息请参见 javadoc。
补充教学
1. 为什么需要编程式创建代理?
在大多数 Spring 应用中,我们依赖容器自动处理 AOP。但在以下场景中,AspectJProxyFactory 非常有用:
- 单元测试:如果你只想测试某个切面逻辑(例如权限检查或日志)是否能正确拦截方法,而不想启动庞大的 Spring 容器,可以用编程式方式快速构建测试环境。
- 构建轻量级工具库:如果你在编写一个不依赖完整 Spring 容器的独立 Java/Kotlin 工具库,但又想利用 AOP 带来的解耦优势。
- 极端动态的场景:当切面的逻辑或拦截的目标在运行时才能确定,且无法通过静态配置来表达时。
2. ProxyFactory vs AspectJProxyFactory
ProxyFactory:是 Spring AOP 的基础工厂类。它要求你手动传入Advice、Advisor或Interceptor。它本身不理解@Aspect、@Before等注解。AspectJProxyFactory:是ProxyFactory的子类。它专门为@AspectJ注解风格设计。它能解析你传入的切面类,自动将其中的注解转化为 Spring 内部的 Advisor,大大降低了手动构造的复杂度。
3. 注意点:它不具备“自动发现”能力
使用 AspectJProxyFactory 时,你必须显式调用 addAspect 方法。 它不会像 Spring 容器那样去扫描路径下的 @Aspect 类。这意味着你需要手动管理切面的生命周期和实例化。
4. 性能考量
虽然手动创建代理在运行时的性能与容器创建的代理无异,但在高频创建代理对象的场景下,由于每次都需要解析注解,会有一定的初始化开销。 建议:如果某些代理对象需要被频繁创建,考虑缓存生成的工厂实例或代理类。
5. 关于 TargetObject
即便你传入的是一个原始对象,返回的 proxy 依然是一个由 Spring AOP 构建的代理(JDK 动态代理或 CGLIB 代理)。这回到了我们之前讨论的代理机制——要注意“自调用”问题依然存在。