回顾
前文提到了一个切面(Aspect)由切点(Pointcut)和增强(Advice)组成,切点提供了连接点的具体类的具体方法信息,而增强提供了横切代码以及其织入方法的位子,当单独使用增强时,会默认的作用于类的所有方法上,想要指定特定的方法就需要切点的使用了,下面进行切点的讲解,增强请看上一篇笔记。
切点(Pointcut)
Spring通过org.springframwork.aop.Pointcut接口描述切点,Pointcut由ClassFilter和MethodMatcher构成,通过ClassFilter对位具体的类,通过Methodmatcher定位方法。Spring支持两种方法匹配器:静态方法匹配器和动态方法匹配器,方法匹配器的类型以isRuntime()返回值决定,false为静态。
Spring提供了6种类型的切点:静态方法切点、动态方法切点、注解切点、表达式切点、流程切点、复合切点
静态方法切点
org.springframework.aop.support.StaticMethodMatcherPoint,默认匹配所有类包含两个主要的子类NameMethodmatcherPoint和AbstractRegexpMatcherPoint从类名就可以看出,前者是通过简单字符串匹配方法签名,后者是通过正则表达式配置方法签名。
静态普通方法名匹配切面
还是以之前睡觉前要洗漱为例:
package advisor;
public class Person {
public void sleep() {
System.out.println("I am sleeping.....ZZZZ");
}
public void work() {
System.out.println("I am working....");
}
}
我们只希望在sleep方法的前面加上洗漱而不影响work,使用前置增强:
package advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("I am washing face.....");
}
}
使用静态方法切点:
public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return "sleep".equals(method.getName());
}
@Override
public ClassFilter getClassFilter() {
return new ClassFilter() {
@Override
public boolean matches(Class<?> clazz) {
return Person.class.isAssignableFrom(clazz);
}
};
}
}
可以看到通过重写matchs方法,和getClassFilter方法我们只匹配Person类的sleep方法,使用TestNG测试:
package advisor;
import advice.GreetingBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class GreetingAdvisorTest {
private ProxyFactory pf;
private Person person;
private GreetingAdvisor advisor;
private GreetingBeforeAdvice advice;
@BeforeTest
public void init() {
person = new Person();
advisor = new GreetingAdvisor();
advice = new GreetingBeforeAdvice();
pf = new ProxyFactory();
advisor.setAdvice(advice);
pf.setTarget(person);
pf.addAdvisor(advisor);
}
@Test
public void testStaticMethodAdvisor() {
person = (Person)pf.getProxy();
person.sleep();
System.out.println("-------------------------------");
person.work();
}
}
结果:
I am washing face.....
I am sleeping.....ZZZZ
-------------------------------
I am working....
达到了预期目标。
Spring配置文件的配置方式:
......
<bean id="personTarget" class="advisor.Person"/>
<bean id="advice" class="advice.GreetingBeforeAdvice"/>
<bean id="advisor" class="advisor.GreetingAdvisor"
p:advice-ref="advice"/>
<bean id="proxyFactory" class="org.springframework.aop.framework.ProxyFactoryBean"
abstract="true" p:interceptorNames="advisor"
p:proxyTargetClass="true"/>
<bean id="person" parent="proxyFactory" p:target-ref="personTarget"/>
初始化ApplicationContext获取person的实例,即可使用,效果同上
静态正则表达式方法匹配切面
这种方式使用正则表达式去匹配方法的签名,正则表达式的内容比较多,这里就不介绍了,使用方式与上面大同小异,只是在匹配方法时不时直接比较字符串,而是用正则表达式匹配,例子相同部分就不再提。RegexpMethodPointcutAdvisor是正则表达式的切面实现类,该类功能已经齐备,一般情况下,不需要再继承重写他的方法或扩占方法。
spring配置文件中配置如下:
......
<bean id="personTarget" class="advisor.Person"/>
<bean id="advice" class="advice.GreetingBeforeAdvice"/>
<bean id="advisor" class="org.springframwork.aop.support.RegexpMethodPointcutAdvisor"
p:advice-ref="advice">
<property name="patterns">
<list>
<value>.*sleep.*</value>
</list>
<property>
</bean>
<bean id="proxyFactory" class="org.springframework.aop.framework.ProxyFactoryBean"
abstract="true" p:interceptorNames="advisor"
p:proxyTargetClass="true"/>
<bean id="person" parent="proxyFactory" p:target-ref="personTarget"/>
直接使用ProxyFactory代码:
package advisor;
import advice.GreetingBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.RegexpMethodPointcutAdvisor;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
/**
* Created by rex.chen on 14-1-24.
*/
public class GreetingAdvisorTest {
private ProxyFactory pf;
private Person person;
private GreetingBeforeAdvice advice;
private RegexpMethodPointcutAdvisor regexp;
@BeforeTest
public void init() {
person = new Person();
advice = new GreetingBeforeAdvice();
regexp = new RegexpMethodPointcutAdvisor();
pf = new ProxyFactory();
regexp.setAdvice(advice);
regexp.setPattern(".*sleep.*");
pf.setTarget(person);
pf.addAdvisor(regexp);
}
@Test
public void testStaticMethodAdvisor() {
person = (Person)pf.getProxy();
person.sleep();
System.out.println("-------------------------------");
person.work();
}
}
动态方法切点
org.springframework.aop.support.DynamicMethodMatcherPointcut是动态方法切点的抽象基类,默认情况下匹配所有的类。动态方法切点与静态方法切点的区别是前者使用动态方法匹配器,后者使用静态方法匹配器,区别在于动态方法匹配器会在运行期检查方法的入参的值来匹配方法,每次调用方法时都将检查一次入参,所以对性能影响很大,而静态配置只会匹配一次,所以一般不会用动态方法切点,这里就不介绍了
注解切点
org.springframework.aop.support.annotation.AnnotationMatcherPointcut实现类表示注解切点,可以使用注解配置切点,后面再详细介绍
表达式切点
org.springframwork.aop.support.ExpressionPointcut接口主要是为了支持AspectJ切点表达式语言而定义的接口,后面再详细介绍
流程切点
org.springframwork.aop.support.ControlFlowPointcut,根据程序执行的堆栈信息查看目标方法是否由某一个方法直接或间接发起调用,以此判断是否为匹配的连接点,这里不详细介绍。
复合切点
org.springframwork.aop.support.ComposablePointcut可以使用链式编程创建多个切点,这里不详细介绍。