Infra 中的 Pointcut API
本节描述 Infra 如何处理关键的 pointcut 概念。
概念
Infra 的 pointcut 模型支持独立于 advice 类型的 pointcut 重用。 你可以使用相同的 pointcut 针对不同的 advice。
infra.aop.Pointcut 接口是中心接口,用于将 advice 定位到特定的类和方法。完整的接口如下:
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
将 Pointcut 接口拆分为两部分允许重用类和方法匹配部分以及细粒度的组合操作(例如与另一个方法匹配器执行“并集”操作)。
ClassFilter 接口用于将 pointcut 限制为给定的目标类集。如果 matches() 方法始终返回 true,则匹配所有目标类。
以下清单显示了 ClassFilter 接口定义:
public interface ClassFilter {
boolean matches(Class clazz);
}
MethodMatcher 接口通常更重要。完整的接口如下:
public interface MethodMatcher {
boolean matches(Method m, Class<?> targetClass);
boolean isRuntime();
boolean matches(Method m, Class<?> targetClass, Object... args);
}
matches(Method, Class) 方法用于测试此 pointcut 是否曾匹配目标类上的给定方法。
可以在创建 AOP 代理时执行此评估,以避免在每次方法调用时进行测试。
如果对于给定的方法,双参数 matches 方法返回 true,并且 MethodMatcher 的 isRuntime() 方法返回 true,
则在每次方法调用时都会调用三参数 matches 方法。
这使得 pointcut 可以在目标 advice 开始之前立即查看传递给方法调用的参数。
大多数 MethodMatcher 实现都是静态的,这意味着它们的 isRuntime() 方法返回 false。
在这种情况下,永远不会调用三参数 matches 方法。
| 如果可能,请尝试使 pointcut 为静态的,允许 AOP 框架在创建 AOP 代理时缓存 pointcut 评估的结果。 |
Pointcut 上的操作
Infra 支持 pointcut 上的操作(特别是并集和交集)。
并集意味着任一 pointcut 匹配的方法。
交集意味着两个 pointcut 都匹配的方法。
并集通常更有用。
你可以通过使用 infra.aop.support.Pointcuts 类中的静态方法或使用同一包中的 ComposablePointcut 类来组合 pointcut。
但是,使用 AspectJ pointcut 表达式通常是一种更简单的方法。
AspectJ 表达式 Pointcut
自 2.0 以来,Infra 使用的最重要的 pointcut 类型是 infra.aop.aspectj.AspectJExpressionPointcut。
这是一个使用 AspectJ 提供的库来解析 AspectJ pointcut 表达式字符串的 pointcut。
有关支持的 AspectJ pointcut 原语的讨论,请参阅 上一章。
便捷的 Pointcut 实现
Infra 提供了几个方便的 pointcut 实现。你可以直接使用其中的一些;其他的旨在在特定于应用程序的 pointcut 中进行子类化。
静态 Pointcut
静态 pointcut 基于方法和目标类,不能考虑方法的参数。静态 pointcut 对于大多数用法来说已经足够了——也是最好的。 Infra 只能在首次调用方法时评估静态 pointcut 一次。 之后,不需要在每次方法调用时再次评估 pointcut。
本节的其余部分描述了 Infra 中包含的一些静态 pointcut 实现。
正则表达式 Pointcut
指定静态 pointcut 的一种明显方法是正则表达式。除 Infra 外,其他几个 AOP 框架也使这成为可能。
infra.aop.support.JdkRegexpMethodPointcut 是一个通用的正则表达式 pointcut,它使用 JDK 中的正则表达式支持。
使用 JdkRegexpMethodPointcut 类,你可以提供模式字符串列表。
如果其中任何一个匹配,则 pointcut 评估为 true。(因此,结果 pointcut 实际上是指定模式的并集。)
以下示例显示了如何使用 JdkRegexpMethodPointcut:
@Configuration
public class JdkRegexpConfiguration {
@Bean
public JdkRegexpMethodPointcut settersAndAbsquatulatePointcut() {
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPatterns(".*set.*", ".*absquatulate");
return pointcut;
}
}
Infra 提供了一个名为 RegexpMethodPointcutAdvisor 的便利类,它让我们也可以引用一个 Advice
(请记住,Advice 可以是拦截器、前置通知、抛出通知等)。在幕后,Infra 使用 JdkRegexpMethodPointcut。
使用 RegexpMethodPointcutAdvisor 简化了连接,因为一个 bean 封装了 pointcut 和 advice,如下例所示:
@Configuration
public class RegexpConfiguration {
@Bean
public RegexpMethodPointcutAdvisor settersAndAbsquatulateAdvisor(Advice beanNameOfAopAllianceInterceptor) {
RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor();
advisor.setAdvice(beanNameOfAopAllianceInterceptor);
advisor.setPatterns(".*set.*", ".*absquatulate");
return advisor;
}
}
你可以将 RegexpMethodPointcutAdvisor 与任何 Advice 类型一起使用。
动态 Pointcut
动态 pointcut 的评估成本比静态 pointcut 高。它们考虑方法参数以及静态信息。 这意味着必须在每次方法调用时评估它们,并且结果不能被缓存,因为参数会变化。
主要示例是 control flow pointcut。
控制流 Pointcut
Infra 控制流 pointcut 在概念上类似于 AspectJ cflow pointcut,尽管功能不那么强大。
(目前无法指定 pointcut 在与另一个 pointcut 匹配的连接点下方运行。)
控制流 pointcut 匹配当前调用堆栈。例如,如果连接点由 com.mycompany.web 包中的方法或 SomeCaller 类调用,它可能会触发。
控制流 pointcut 通过使用 infra.aop.support.ControlFlowPointcut 类指定。
| 控制流 pointcut 在运行时的评估成本甚至比其他动态 pointcut 高得多。 在 Java 1.4 中,成本大约是其他动态 pointcut 的五倍。 |
Pointcut 超类
Infra 提供了有用的 pointcut 超类来帮助你实现自己的 pointcut。
因为静态 pointcut 最有用,你可能应该子类化 StaticMethodMatcherPointcut。
这只需要实现一个抽象方法(尽管你可以覆盖其他方法来自定义行为)。
以下示例显示了如何子类化 StaticMethodMatcherPointcut:
-
Java
class TestStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
// 如果自定义标准匹配,则返回 true
}
}
也有用于动态 pointcut 的超类。 你可以将自定义 pointcut 与任何 advice 类型一起使用。