4.3PointCut 和Advisor
之前定义的Advice都是直接织入至代理的接口执行前后的,或者在执行方法过程中异常发生时织入的。事实上还可以定义更细致的织入时机。Pointcut定义了感兴趣的jointpoint(Advice的应用时机)。在spring中,使用pointcutadvisor提供的pointcut实例,具体结合advice,spring内建的pointcut都有对应的pointcutadvisor。
4.3.1NameMatchMethodPointcutAdvisor
Pointcut定义了jointpoint,在spring中使用pointcutadvisor来提供pointcut,结合advice,pointcutAdvisor为advisor的子接口,advisor接口的定义如下:
Public interface Advisor{
BooleanisPerInstance();
AdvicegetAdvice();
}
Public interface PointcutAdvisorextends Advisor{
PointcutgetPointcut();
}
Org.springfranework.aop.support.NameMatchMethodPointcutAdvisor,这个是最基本的PointcutAdvisor,用以提供spring中静态的pointcut实例,可使用表示式expression指定advice应用目标上的方法名称,或者是用*来指定,例如当hello*表示执行代理对象上以hello作为开头的方法名称时,它都会应用指定的advices。如果不加的话就是该接口的所有方法都会被进行或前或后的插入新代码。
例子:
#IHello
package onlyfun.pointcutadvisor;
public interfaceIHello {
public voidhelloNewbie(String name);
public voidhelloMaster(String name);
}
#HelloSpeaker
package onlyfun.pointcutadvisor;
public classHelloSpeaker implements IHello {
@Override
public void helloNewbie(String name) {
// TODO Auto-generated method stub
System.out.println("helloNewbie name.... :["+name+"]");
}
@Override
public void helloMaster(Stringname){
// TODO Auto-generated method stub
System.out.println("helloMaster name ... :["+name+"]");
}
}
#LofAfterAdvisor
package onlyfun.pointcutadvisor;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.aop.AfterReturningAdvice;
public classLogAfterAdvice implements AfterReturningAdvice {
private Logger logger = Logger.getLogger(this.getClass().getName());
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
logger.log(Level.INFO,"method is ending ...["+method+"]");
}
}
#spring-config.xml
<bean id="helloSpeaker"class ="onlyfun.pointcutadvisor.HelloSpeaker"/>
<bean id = "logAfterAdvice2" class ="onlyfun.pointcutadvisor.LogAfterAdvice"/>
<bean id = "helloAdvisor" class ="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name ="mappedName" value ="*Newbie"/>
<!--
<propertyname = "mappedName">
<list>
<value>*Newbie</value>
<value>*Master</value>
</list>
</property>
-->
<property name ="advice" ref="logAfterAdvice2"/>
</bean>
<bean id = "helloProxy" class = "org.springframework.aop.framework.ProxyFactoryBean">
<property name ="proxyInterfaces" value="onlyfun.pointcutadvisor.IHello"/>
<property name ="target" ref="helloSpeaker"/>
<property name ="interceptorNames">
<list>
<value>helloAdvisor</value>
</list>
</property>
</bean>
这里的HelloSpeaker需要单独声明一个bean,这样spring容器才会需要时创建一个,不然的话会报错。
这里使用了NameMatchMethodPointcutAdvisor类,mappedName可以映射接口的方法,具体是哪一个,单个或者list列表列出都可以,然后是advice指定由那个advice处理被拦截的程序执行相应服务。mappedName只有列出来的方法在其执行的时候才会被拦截,没有列出的则不会被拦截处理。
#test
package onlyfun.pointcutadvisor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public classPointcutAdvisorTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContextcontext = new ClassPathXmlApplicationContext("spring-config.xml");
IHello hello = (IHello) context.getBean("helloProxy");
hello.helloNewbie("dudud");
hello.helloMaster("master dudud");
}
}
4.3.2RegExpMethodPointcutAdvisor
另外一种使用正则表达式匹配需要拦截的方法,org.springframework.aop.support.RegExpPointcutAdvisor可以让我们使用regular expression来编写pointcut表达式,使用spring中静态的Pointcut的实例。
正则表达式:.符合任何一个字符, +符合前一个字符一次或者多次, *符合前一个字符零次或多次, \转意字符。
RegexpMethdPointcutAdvisor的pattern属性可是用来匹配要符合的完整类名称(包名)和方法名称,举个例子如onlyfun.caterpillar.IHello下的hello开始的方法名称,可以如:onlyfun\.caterpillar\.IHello\.hello.*
这里只要修改一下配置文件即可
<bean id="helloSpeaker" class ="onlyfun.pointcutadvisor.HelloSpeaker"/>
<bean id = "logAfterAdvice2" class ="onlyfun.pointcutadvisor.LogAfterAdvice"/>
<bean id ="helloAdvisor" class ="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name ="advice" ref="logAfterAdvice2"/>
<property name ="pattern" value="onlyfun\.pointcutadvisor\.IHello\.hello.*"/>
</bean>
只要作如上修改即可。
4.3.4 pointcut接口
Spring的pointcut接口是通过org.springframework.aop.Pointcut接口来实现的,定义如下:
Package org.springframework.aop;
Public interface Pointcut{
ClassFiltergetClassFilter();
MethodMatchergetMethodMatch();
PointcutTRUE=TruePointcut.INSTANCE;
}
切点:pointcut,classFilter决定一个类是否要执行advice,methodMatcher决定一个方法是否要执行advice,
Pointcut.TRUE返回的是一个TRUEPointcut 实例,其中有:
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
#methodMatcher
public interfaceMethodMatcher {
boolean matches(Method method, Class<?> targetClass);
boolean isRuntime();
boolean matches(Method method, Class<?> targetClass, Object[] args);
MethodMatcher TRUE =TrueMethodMatcher.INSTANCE;
}
一般静态的pointcut都是使用matches第一个方法,而动态的则先需要判断isRuntime,然后再执行第二个matches方法,这个带有参数。(如ControlFlowPointcut).
4.3.5
Pointcut的交集和并集操作
如果建立了多个pointcut,而且可以将几个pointcut进行组合,称为另一个pointcuts的定义时,可以对现有的pointcuts对象进行交集intersection,并集union,org.springframework.aop.support.ComposablePointcut是Pointcut的实现类,可以操作它的intersection()、union()方法。
Pointcut pc = newComposablePointcut().union(classFilter).intersection(methodMatcher).intersection(pointcut);
也可以使用org.springframework.aop.support.PointCuts的union方法,处理两个pointcut。
package onlyfun.pointcutadvisor;
import java.util.List;
importorg.springframework.aop.ClassFilter;
importorg.springframework.aop.MethodMatcher;
importorg.springframework.aop.Pointcut;
importorg.springframework.aop.framework.AopConfigException;
importorg.springframework.aop.support.Pointcuts;
public class UnionPoint implementsPointcut {
privatePointcut pointcut;
publicPointcut getPointcut() {
if(pointcut== null)
{
thrownew AopConfigException("no set the pointcut!");
}
returnpointcut;
}
publicvoid setPointcut(Pointcut pointcut) {
this.pointcut= pointcut;
}
publicvoid setPointcut(List pointcuts){
if(pointcuts== null || pointcuts.size() ==0){
thrownew AopConfigException("at least to set one pointcut");
}
pointcut= (Pointcut) pointcuts.get(0);
for(inti=1;i<pointcuts.size();i++){
Pointcutnext = (Pointcut) pointcuts.get(i);
pointcut= Pointcuts.union(pointcut, next);
}
}
@Override
publicClassFilter getClassFilter() {
//TODO Auto-generated method stub
returngetPointcut().getClassFilter();
}
@Override
publicMethodMatcher getMethodMatcher() {
//TODO Auto-generated method stub
returngetPointcut().getMethodMatcher();
}
}
4.4 introduction
在spring中,introduction是一种特殊的advice,从行为上看,他不像beforeAdvice,afteradvice等在方法的前后介入服务,而是直接介入整个对象的行为,就好像整个对象多了一些可以操作的方法,为对象加入了一些新的职责。
在springaop中可以通过实现org.springframework.aop.introductionInterceptor来实现introduction。
IntroductionInterceptor继承自MethodInterceptor,DynamicIntroductionAdvice.
DynamicIntroductionAdvice中:
booleanimplementsInterface(Class<?> intf);
如果返回为true,则是为对象添加了新的行为。而MethodInterceptor接口中是invoke()方法
@Override
public Objectinvoke(MethodInvocation methodInvocation) throwsThrowable {
return null;
}
此时如果要让目标执行额外的方法就不能再使用methodInvocation的proceed()方法。
这里有DelegatingIntroductionInterceptor这个可是spring对于IntroductionInterceptor接口的实现,方便操作,使用方法类似;
,BeanNameAutoProxyCreator,这个是对于原有代理的升级,可以根据相应的BeanName进行匹配,如
<bean id =”introductionProxyCreator”class =”org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator”>
<propertyname =”beanNames”>
<list>
<value>*Service</value>
</list>
</property>
<propertyname =”interceptorNames” value =”lockAdvisor”/>
</bean>
这里的beanNames未相应的target,设置成原来target中含有的可以匹配的形式即可。
/DefaultAdvisorAutoProxyCreator,这里会匹配默认的Advisor.
具体可以参考《spring2.0技术手册》林信良著。
参考例子:
#IHello
package onlyfun.introductioninterceptor;
public interfaceISome {
public void doSome();
}
#Some
package onlyfun.introductioninterceptor;
public classSome implementsISome {
@Override
public void doSome() {
// TODO Auto-generated method stub
System.out.println("原来对象的职责");
}
}
#IOther
package onlyfun.introductioninterceptor;
public interfaceIOther {
public voiddoOther();
}
#OtherInterceptor
package onlyfun.introductioninterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.IntroductionInterceptor;
public class OtherIntroduction implements IntroductionInterceptor, IOther{
@Override
public Objectinvoke(MethodInvocation methodInvocation) throws Throwable {
// TODO Auto-generated methodstub
//如果执行的方法来自IOther接口的定义
if(implementsInterface(methodInvocation.getMethod().getDeclaringClass())){
//执行额外的mixin行为
return methodInvocation.getMethod().invoke(this,methodInvocation.getArguments());
}else{
returnmethodInvocation.proceed();
}
}
@Override //是否实现外有接口(对象是否引入外有方法)
public booleanimplementsInterface(Class<?> clazz) {
// TODO Auto-generated method stub
returnclazz.isAssignableFrom(IOther.class);
}
@Override
public void doOther() {
// TODO Auto-generated methodstub
System.out.println("doother operation.....");
}
}
#spring-config2.xml
<?xml version="1.0"encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="some" class="onlyfun.introductioninterceptor.Some"/>
<bean id = "otherIntroduction" class ="onlyfun.introductioninterceptor.OtherIntroduction"/>
<bean id ="otherAdvisor" class ="org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg ref ="otherIntroduction"/>
<constructor-arg value = "onlyfun.introductioninterceptor.IOther"/>
</bean>
<bean id ="someProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name ="proxyInterfaces" value ="onlyfun.introductioninterceptor.ISome"/>
<property name ="target" ref="some"/>
<property name ="interceptorNames" >
<list>
<value>otherAdvisor</value>
</list>
</property>
</bean>
</beans>
#test
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config2.xml");
ISomesome = (ISome)context.getBean("someProxy");
some.doSome();
((IOther)some).doOther();
如果是使用的继承DelegatingIntroductionInterceptor类的话如下:
除了继承该类之外,还需要实现IOther接口,并重写invoke方法。
packageonlyfun.introductioninterceptor;
importorg.aopalliance.intercept.MethodInvocation;
importorg.springframework.aop.support.DelegatingIntroductionInterceptor;
public class OtherIntroduction2extends DelegatingIntroductionInterceptor implements IOther{
privatestatic final long serialVersionUID = 1L;
@Override
publicObject invoke(MethodInvocation mi) throws Throwable {
//TODO Auto-generated method stub
returnsuper.invoke(mi);
}
@Override
publicvoid doOther() {
//TODO Auto-generated method stub
System.out.println("doother method ...");
}
}
其他和之前文件一样,再者就是修改一下spring-config2.xml即可。