Spring AOP本質(6)

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
相關文章
相關標籤/搜索