ComposablePointcut 组合切入点
从上面的例子中,每次我们只能定义一个切入点(切点表达式)。有的时候,一个切点可能难以描述目标连接点的信息,而是需要同时满足两个切入点才行,那么ComposablePointcut就派上了用场(aspectJ里面的&& ||等其实也能达到类似的效果)。
但是更好的方式是使用Spring提供的ComposalbePointcut把两个切点组合起来,通过切点的复合运行算表示,ComposalbePointcut可以将多个切点以并集或者交集的方式组合起来,提供切点之间复合运算的功能。
先看一个Demo:
public class Main { public static void main(String[] args) { ProxyFactory factory = new ProxyFactory(new Person()); // 声明一个通知(此处使用环绕通知 MethodInterceptor ) Advice advice = (MethodInterceptor) invocation -> { System.out.println("============>放行前拦截..."); Object obj = invocation.proceed(); System.out.println("============>放行后拦截..."); return obj; }; // 先创建一个流程切入点 ControlFlowPointcut controlFlowPointcut = new ControlFlowPointcut(Main.class, "funabc"); // 再创建一个方法名切入点 NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut(); nameMatchMethodPointcut.addMethodName("say"); // 创建一个复合切点 把上面两者并且进来 ComposablePointcut cut = new ComposablePointcut(); cut.intersection((Pointcut) controlFlowPointcut).intersection((Pointcut)nameMatchMethodPointcut); // 切点+通知(注意:此处放的是复合切面) Advisor advisor = new DefaultPointcutAdvisor(cut, advice); factory.addAdvisor(advisor); Person p = (Person) factory.getProxy(); // 执行方法 p.run(); p.run(10); p.say(); p.sayHi("Jack"); p.say("Tom", 666); funabc(p); } private static void funabc(Person person) { person.run(); person.say(); } } 输出: 我在run... 我在run...<10> 我在say... Hi,Jack,你好 Tom----666 我在run... ============>放行前拦截... 我在say... ============>放行后拦截...
从结果中和上面对比我们能看出,两个切入点有并且的效果。(只有say方法被拦截了,run方法并没有被拦截)
ComposablePointcut 源码分析
public class ComposablePointcut implements Pointcut, Serializable { // 它持有ClassFilter 和 MethodMatcher ,最终通过它去组合匹配 private ClassFilter classFilter; private MethodMatcher methodMatcher; // 构造函数一个共5个 // 匹配所有类所有方法的复合切点 public ComposablePointcut() { this.classFilter = ClassFilter.TRUE; this.methodMatcher = MethodMatcher.TRUE; } // 匹配特定切点的复合切点(相当于把这个节点包装了一下而已) public ComposablePointcut(Pointcut pointcut) { Assert.notNull(pointcut, "Pointcut must not be null"); this.classFilter = pointcut.getClassFilter(); this.methodMatcher = pointcut.getMethodMatcher(); } // 匹配特定类**所有方法**的复合切点 public ComposablePointcut(ClassFilter classFilter) { Assert.notNull(classFilter, "ClassFilter must not be null"); this.classFilter = classFilter; this.methodMatcher = MethodMatcher.TRUE; } // 匹配**所有类**特定方法的复合切点 public ComposablePointcut(MethodMatcher methodMatcher) { Assert.notNull(methodMatcher, "MethodMatcher must not be null"); this.classFilter = ClassFilter.TRUE; this.methodMatcher = methodMatcher; } // 匹配特定类特定方法的复合切点(这个是最为强大的) public ComposablePointcut(ClassFilter classFilter, MethodMatcher methodMatcher) { Assert.notNull(classFilter, "ClassFilter must not be null"); Assert.notNull(methodMatcher, "MethodMatcher must not be null"); this.classFilter = classFilter; this.methodMatcher = methodMatcher; } // 匹配特定类特定方法的复合切点(这个是最为强大的) public ComposablePointcut union(ClassFilter other) { this.classFilter = ClassFilters.union(this.classFilter, other); return this; } // ==========3个并集(union) / 3个交集(intersection) 运算的方法======== public ComposablePointcut intersection(ClassFilter other) { this.classFilter = ClassFilters.intersection(this.classFilter, other); return this; } public ComposablePointcut union(MethodMatcher other) { this.methodMatcher = MethodMatchers.union(this.methodMatcher, other); return this; } public ComposablePointcut intersection(MethodMatcher other) { this.methodMatcher = MethodMatchers.intersection(this.methodMatcher, other); return this; } public ComposablePointcut union(Pointcut other) { this.methodMatcher = MethodMatchers.union( this.methodMatcher, this.classFilter, other.getMethodMatcher(), other.getClassFilter()); this.classFilter = ClassFilters.union(this.classFilter, other.getClassFilter()); return this; } public ComposablePointcut intersection(Pointcut other) { this.classFilter = ClassFilters.intersection(this.classFilter, other.getClassFilter()); this.methodMatcher = MethodMatchers.intersection(this.methodMatcher, other.getMethodMatcher()); return this; } ... }
ComposablePointcut没有提供直接对两个切点类型并集交集的运算的方法。若需要,请参照org.springframework.aop.support.Pointcuts这个工具类里面有对两个Pointcut进行并集、交集的操作(后面再介绍)
AnnotationMatchingPointcut 注解切入点
根据对象是否有指定类型的注解来匹配Pointcut
有两种注解,类级别注解和方法级别注解。
//仅指定类级别的注解, 标注了 ClassLevelAnnotation 注解的类中的**所有方法**执行的时候,将全部匹配。 AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class); // === 还可以使用静态方法创建 pointcut 实例 AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forClassAnnotation(ClassLevelAnnotation.class); //仅指定方法级别的注解,标注了 MethodLeavelAnnotaion 注解的**方法(忽略类匹配)都将匹配** AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(MethodLevelAnnotation.class); ==========这个是同时想限定:=============== //同时限定类级别和方法级别的注解,只有标注了 ClassLevelAnnotation 的类中 ***同时***标注了 MethodLevelAnnotation 的方法才会匹配 AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class, MethodLevelAnnotation.class);
Demo:略
总结
其实,这些基础的知识也是为了去更好的理解Spring的自动代理创建器铺路