Spring AOP本质(5)-Pointcut
在前面四个例子中,只要实现一个Advice,而后调用ProxyFactory.addAdvice()方法为代理设定通知,不用设置切入点,从代理对上调用的方法就被通知到了。其缘由就在于执行addAdvice()方法时,ProxyFactory会将Advice对象委派给addAdvistor()方法,后台会自动建立一个DefaultPointcutAdvistor实例,并将Advice加入其中。而默认的DefaultPointcutAdvistor会将切入点设为全部的方法。
假如咱们不想经过代理来执行某些方法,也就是说,在执行某些方法的时候不通知,这时候该如何实现呢?
Spring提供一系列接口来实现这个目标。最主要的接口以下:
implements org.springframework.aop.Pointcut
org.springframework.aop.ClassFilter
org.springframework.aop.MethodMatcher
下面看看几个关键接口的定义:
一、切入点(Pointcut)
/**
* 切入点
*/
public
interface Pointcut {
//切入点的一个单例
public
static
final Pointcut TRUE = TruePointcut.INSTANCE;
//类过滤器
public ClassFilter getClassFilter();
//方法过滤器
public MethodMatcher getMethodMatcher();
}
/**
* 类过滤器
*/
public
interface ClassFilter {
//类过滤器单例
public
static
final ClassFilter TRUE = TrueClassFilter.INSTANCE;
//类匹配方法
public
boolean matches(Class class1);
}
/**
* 方法过滤器
*/
public
interface MethodMatcher {
//方法过滤器单例
public
static
final MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
//静态方法匹配方法
public
boolean matches(Method method, Class class1);
//判断静态仍是动态匹配,返回true动态匹配,false静态匹配
public
boolean isRuntime();
//对象(动态)匹配方法
public
boolean matches(Method method, Class class1, Object aobj[]);
}
二、通知者(Advisor)
/**
* 通知者接口
*/
public
interface Advisor {
//切面是否为per instance
public
boolean isPerInstance();
//获取切面上的通知
public Advice getAdvice();
}
/**
* 通知者子接口,Spring中标准的切面都应该实现此接口
*/
public PointcutAdvisor
extends Advisor {
//获取通知者的切点
public Pointcut getPointcut();
}
为了看的明白,仍是回顾一下框架图:
还有不少接口和类没有画出,这里简要说明下。
在org.springframework.aop.support包下,还有一些很重要的切点类,是Spring定义好的,几乎能够知足所用应用的须要。
DynamicMethodMatcherPointcut
NameMatchMethodPointcut
Perl5RegexpMethodPointcut
StaticMethodMatcherPointcut
JdkRegexpMethodPointcut
ControlFlowPointcut
ComposablePointcut
与这些切点对应,还有一些切面类,名字都是以PointcutAdvisor结尾。
经过上面的原理图,简单查看一下API,就能够直到,经过通知Advice和切点Pointcut能够生成通知者Advisor。有了通知者,有了目标对象,就能够经过ProxyFactory生成代理对象。
下面给个例子看看Spring如何经过切点来选取类和方法的,并如通知所选取的方法。
例子:扩展StaticMethodMatcherPointcut,实现静态切入点过滤。
/**
* 业务组件:BeanOne
*/
public
class BeanOne {
public
void foo() {
System.out.println(
"BeanOne的foo()被调用!");
}
public
void bar() {
System.out.println(
"BeanOne的bar()被调用!");
}
}
/**
* 业务组件:BeanTwo
*/
public
class BeanTwo {
public
void foo() {
System.out.println(
"BeanTwo的foo()被调用!");
}
public
void bar() {
System.out.println(
"BeanTwo的bar()被调用!");
}
}
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 自定义通知:Advice
*/
public
class SimpleAdvice
implements MethodInterceptor {
public Object invoke(MethodInvocation invocation)
throws Throwable {
System.out.println(
">> 业务方法调用前动做,被代理调用目标方法是: " + invocation.getMethod().getName());
Object retVal = invocation.proceed();
System.out.println(
">> 业务方法调用结束后动做!");
return retVal;
}
}
import java.lang.reflect.Method;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
/**
* 自定义静态切入点:Pointcut
*/
public
class SimpleStaticPointcut
extends StaticMethodMatcherPointcut {
public
boolean matches(Method method, Class cls) {
//类方法名为foo时候匹配
System.out.println(
"切入点方法匹配,正在匹配"+cls.getName()+
"的"+method.getName()+
"方法!");
return (
"foo".equals(method.getName()));
}
public ClassFilter getClassFilter() {
return
new ClassFilter() {
public
boolean matches(Class cls) {
System.out.println(
"切入点类匹配,正在匹配"+cls.getName()+
"类!");
//BeanOne类匹配
return (cls == BeanOne.
class);
}
};
}
}
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
/**
* 客户端测试
*/
public
class StaticPointcutExample {
public
static
void main(String[] args) {
//建立目标对象
BeanOne one =
new BeanOne();
BeanTwo two =
new BeanTwo();
//定义代理对象
BeanOne proxyOne;
BeanTwo proxyTwo;
//建立一个切入点
Pointcut pc =
new SimpleStaticPointcut();
//建立一个通知
Advice advice =
new SimpleAdvice();
//建立一个通知者(即通知和切入点的结合)
Advisor advisor =
new DefaultPointcutAdvisor(pc, advice);
//建立一个代理工厂
ProxyFactory pf =
new ProxyFactory();
//将方面加入工厂
pf.addAdvisor(advisor);
//将目标加入工厂
pf.setTarget(one);
//获取代理对象产品
proxyOne = (BeanOne) pf.getProxy();
//这个时候会进行匹配检查
//建立一个代理工厂
pf =
new ProxyFactory();
pf.addAdvisor(advisor);
pf.setTarget(two);
proxyTwo = (BeanTwo) pf.getProxy();
/*
org.springframework.aop.framework.ProxyFactory中
设置的代理目标一次仅能一个,这点不要犯迷糊,我查过源码了.
*/
//从代理产品上调用目标方法
proxyOne.foo();
proxyTwo.foo();
proxyOne.bar();
proxyTwo.bar();
}
}
运行结果:
- Using JDK 1.4 collections 切入点类匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanOne类! 切入点方法匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanOne的foo方法! 切入点类匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanOne类! 切入点方法匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanOne的bar方法! 切入点类匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanOne类! 切入点方法匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanOne的hashCode方法! 切入点类匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanOne类! 切入点方法匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanOne的toString方法! 切入点类匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanTwo类! 切入点类匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanTwo类! 切入点类匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanTwo类! 切入点类匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanTwo类! 切入点类匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanOne类! 切入点方法匹配,正在匹配com.apress.prospring.ch6.staticpc.BeanOne的foo方法! >> 业务方法调用前动做,被代理调用目标方法是: foo BeanOne的foo()被调用! >> 业务方法调用结束后动做! BeanTwo的foo()被调用! BeanOne的bar()被调用! BeanTwo的bar()被调用! Process finished with exit code 0