注册 | 登录 忘记密码? 51cto首页 | 博客 | 论坛 | 招聘
热点文章 IB客座主编(四)美国西蒙公..
 帮助

Spring AOP本质(6)


2008-05-09 17:01:54
 标签:Spring AOP   [推送到技术圈]

版权声明:原创作品,如需转载,请与作者联系。否则将追究法律责任。
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
 
下面看看几个关键接口的定义:
 
1、切入点(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[]);
}
 
2、通知者(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
 

本文出自 “熔 岩” 博客,转载请与作者联系!



上一篇 Spring AOP 本质(5)  下一篇 Spring AOP本质(7)



    文章评论
 
2008-05-10 16:56:05
写的好

 

发表评论

昵   称:
验证码:  点击图片可刷新验证码  博客过2级,无需填写验证码
内   容: