文章目录
Pointcut接口
pointcut是切入点,是很多连接点的集合,目的是要找到合适的地方来应用通知。在Spring中目前只支持一种形式的连接点,也就是在调用public的非静态方法时,可以应用通知。
对于切入点来说,有两个方面需要确定。一个是到底应用到那些类上,第二个是能够应用到那些方法上面。从这个思路出发,Pointcut接口刚好提供了两个方法
-
ClassFilter getClassFilter()
- ClassFilter
- 专门用于检查目标类是否是一个合适的切入点
- 方法 boolean matches(Class<?> clazz)
- 常量 ClassFilter TRUE=TrueClassFilter.INSTANCE
- ClassFilter
-
MthodMather getMthodMathcer()
- MthodMather
- 实例主要是检查方法是否合适以便应用切面
- 方法 boolean isRuntime()
- 方法 boolean matches(Method m,Class<?> targetClass)
- 方法 boolean matches(Method m,Class<?> targetClass,Object[] args)
- MethodMatcher接口的实现支持静态和动态两种类型,取决于在调用方法匹配判断之前调用的方法isRuntime的返回值
- 如果返回的是true:动态,先调用一次matches(Method m,Class<?> targetClass)方法,若返回true,则后续调用matches(Method m,Class<?> targetClass,Object[] args)
- 第一个matches方法返回true,后面的matches方法在每次调用匹配方法时都会调用一次,进行方法参数的匹配
- 返回false:静态,只调用一次matches(Method m,Class<?> targetClass)方法
- 如果返回的是true:动态,先调用一次matches(Method m,Class<?> targetClass)方法,若返回true,则后续调用matches(Method m,Class<?> targetClass,Object[] args)
- MthodMather
Spring在处理Pointcut匹配的时候,先调用的是getClassFilter方法检查目标类是否匹配,若返回true,再调用getMthodMather检查方法是否匹配
API层次
上图中,都是跟Pointcut接口相关的类,其中有一个抽象的StaticMthodMatcherPointcut类(静态的),还有一个抽象的DynamicMethodMatcherPointcut类(动态的)。
StaticMethodMatcherPointcut
示例代码
接口
package com.csdn.springaop.target;
public interface MyInterface {
public void method1();
public void method2() throws Exception ;
}
目标类
package com.csdn.springaop.target;
public class Target implements MyInterface {
public void method1() {
System.out.println("Target method1");
}
public void method2() throws Exception {
throw new Exception("Target method2 Error!");
}
}
测试代码
package com.csdn.springaop.test;
import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
import com.csdn.springaop.advice.MyAfterReturningAdvice;
import com.csdn.springaop.advice.MyAroundAdvice;
import com.csdn.springaop.advice.MyBeforeAdvice;
import com.csdn.springaop.advice.MyThrowsAdvice;
import com.csdn.springaop.target.MyInterface;
import com.csdn.springaop.target.Target;
public class SpringTest2 {
@Test
public void test1() {
Target target=new Target();
MyBeforeAdvice myBeforeAdvice=new MyBeforeAdvice();
ProxyFactory proxyFactory =new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(myBeforeAdvice);
MyInterface target1=(MyInterface)proxyFactory.getProxy();
target1.method1();
System.out.println("开始运行toString方法");
target1.toString();
}
}
上面运行结果
MyBeforeAdvice
Target method1
开始运行toString方法
MyBeforeAdvice
从结果中可以看到,调用代理目标类的toString方法也执行了Before通知,原因是我们调用ProxyFactory工厂的addAdvice方法,并没有限制那些方法能够应用到通知。
下面我们将测试代码修改下
@Test
public void test2() {
Target target=new Target();
MyBeforeAdvice myBeforeAdvice=new MyBeforeAdvice();
ProxyFactory proxyFactory =new ProxyFactory();
proxyFactory.setTarget(target);
Pointcut pointcut=new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return "method1".equals(method.getName());
}
};
DefaultPointcutAdvisor advisor=new DefaultPointcutAdvisor(pointcut,myBeforeAdvice);
proxyFactory.addAdvisor(advisor);
MyInterface target1=(MyInterface)proxyFactory.getProxy();
target1.method1();
System.out.println("开始运行toString方法");
target1.toString();
}
原来的测试代码中,向代理工厂中加入了通知后,并没有限定应用通知的方法,所以我们加入了一个特定的切入点,这个切入点能够限定到底哪个类的哪些方法能够引用到通知。根据AOP的核心概念,这已经组合成了一个切面,我们已经可以使用下面的接口
- Advisor接口
- 表示切面,有两个子接口
- PointcutAdvisor
- 基于切入点的
- DefaultPointcutAdvisor是PointcutAdvisor的默认实现
- IntroductionAdvisor
- 跟引入相关的
- PointcutAdvisor
- 表示切面,有两个子接口
输出结果
MyBeforeAdvice
Target method1
开始运行toString方法
可以看到,只有method1方法应用到通知了。
DynamicMethodMatcherPointcut
上面应用到一个静态MethodMatcher的抽象类,下面再应用一个动态的MthodMatcher的抽象类,并重写其带有三个参数的matches方法
@Test
public void test3() {
Target target=new Target();
MyBeforeAdvice myBeforeAdvice=new MyBeforeAdvice();
ProxyFactory proxyFactory =new ProxyFactory();
proxyFactory.setTarget(target);
Pointcut pointcut=new DynamicMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return "method1".equals(method.getName())&&args.length==1&&args[0].equals("one");
}
};
DefaultPointcutAdvisor advisor=new DefaultPointcutAdvisor(pointcut,myBeforeAdvice);
proxyFactory.addAdvisor(advisor);
MyInterface target1=(MyInterface)proxyFactory.getProxy();
System.out.println("运行method()");
target1.method1();
System.out.println("运行method(\"one\")");
target1.method1("one");
System.out.println("运行method(\"two\")");
target1.method1("two");
}
在目标中重载一个method1方法
public void method1(String s) {
System.out.println("Target method1"+s);
}
上面运行结果
运行method()
Target method1
运行method("one")
MyBeforeAdvice
Target method1one
运行method("two")
Target method1two
可以看出,只有method1方法,并且调用时参数是“one”的情况应用到了通知。
DefaultPointcutAdvisor
构造方法
- public DefaultPointcutAdvisor()
- public DefaultPointcutAdvisor(Advice advice)
- public DefaultPointcutAdvisor(Pointcut pointcut,Advice advice)
普通方法
- setPointcut(Pointcut pointcut)
- setAdvice(Advice advice)
- setOrder(int order)