隨筆-314  評論-209  文章-0  trackbacks-0

          在閱讀此文之前請你熟悉一些IOC的知識,同時(shí)了解AOP的概念。

          Spring 中所有的通知都是以 Java 類的形式編寫的。 Spring 是采用運(yùn)行期的方式來將切面織入到系統(tǒng)中的。

          代理 Bean 只有在第一次被應(yīng)用系統(tǒng)需要的時(shí)候才被創(chuàng)建。 Spring 有兩種方式創(chuàng)建代理: Proxy 類創(chuàng)建代理 ( 實(shí)現(xiàn)過接口的目標(biāo)類 ) 和運(yùn)用 CGLIB 庫創(chuàng)建代理 ( 沒有實(shí)現(xiàn)過任何接口的目標(biāo)類 ) 。需要注意兩點(diǎn): 1 、對接口創(chuàng)建代理優(yōu)于對類創(chuàng)建代理,因?yàn)檫@樣會產(chǎn)生更加松耦合的系統(tǒng), 2 、標(biāo)記為 final 的方法是不能被通知的,因?yàn)?/span> Spring 在實(shí)現(xiàn)的時(shí)候是為目標(biāo)類產(chǎn)生子類。

          Spring 只支持方法聯(lián)接點(diǎn)。

           

           

          Spring 中通知的類型:

          1 Around     org.aopalliance.intercept.MethodInterceptor        欄截對目標(biāo)對象方法的調(diào)用

          2 Before       org.springframework.aop.MethodBeforAdvice     在目標(biāo)方法被調(diào)用之前調(diào)用

          3 After        org.springframework.aop.AfterReturningAdvice  當(dāng)目標(biāo)方法被調(diào)用之后調(diào)用

          4 Thorws     org.springframework.aop.ThrowsAdvice            當(dāng)目標(biāo)方法拋出異常時(shí)調(diào)用

           

          前置通知 實(shí)現(xiàn) MethodBeforeAdvice 接口,該接口有以下方法:

          void befor(Method method,Object []args,Object target) throws Throwable;

          例:

          public class WelcomeAdvice implements MethodBeforeAdvice{

                 public void before(Method method,Object []args,Object target){

                        System.out.println("Hello,"+((Customer)args[0]).getName());

                 }

          }

          配置文件如下:

          <beans>

          <bean id="aaaTargetObject" class="AAA"/>   // 目標(biāo)類 , 這是一個(gè)實(shí)現(xiàn)某個(gè)接口的類

          <bean id="welcomeAdvice" class="WelcomeAdvice"/>  // 通知類

          <bean id="aaa" class="org.springframework.aop.framework.proxyFactoryBean">

                 <property name="interceptorNames">

                        <list>

                               <value>welcomAdvice</value>

                        </list>

                 </property>

                 <property name="target">

                        <ref bean="aaaTargetObject"/>

                 </property>

          </bean>

          </beans>

           

          后置通知 實(shí)現(xiàn) AfterReturningAdvice 接口,該接口有以下方法:

          void afterReturning(Object returnValue,Method method,Object []args,Object target) throws Throwable;

          例:

          public class ThankYouAdvice implements AfterReturningAdvice{

                 public void afterReturning(Object returnValue,Method method,Object []args,Object target){

                        System.out.println("Thank you, come again.");

                 }

          }

          在前置通知和后置通知中都不能改變參數(shù)中傳進(jìn)來的值,改變執(zhí)行流程的惟一方法就是拋出異常。

           

          環(huán)繞通知 實(shí)現(xiàn) MethodInterceptor 接口,該接口有以下方法:

          Object invoke(MethodInvocation invocation) throws Throwable;

          環(huán)繞通知能夠控制目標(biāo)方法是否真的被調(diào)用,通過調(diào)用 MethodInvocation.proceed() 方法來調(diào)用目標(biāo)方法。它還可以讓你控制返回的對象。

          例:

          public class ExampleAroundInterceptor implements MethodInterceptor{

                 public Object invoke(MethodInvocation invocation) throws Throwable{

                        // 調(diào)用之前可以做一些處理

                        Object returnObject=invocation.proceed();// 調(diào)用目標(biāo)方法

                        // 調(diào)用之后還可以做一些處理

                        return returnObject;

                 }

          }

           

          異常通知 實(shí)現(xiàn) ThrowsAdvice 接口,此接口是一個(gè)標(biāo)示接口,沒有定義必須實(shí)現(xiàn)的方法,但是下面兩個(gè)方法中必須實(shí)現(xiàn)一個(gè):

          void afterThrowing(Throwable throwable);

          void afterThrowing(Method method,Object []args,Object target,Throwable throwable);

          ThrowsAdvice 要處理的異常取決于你的方法定義的異常類型,在一個(gè)實(shí)現(xiàn)類中,也可以實(shí)現(xiàn)多個(gè) afterThrowing 方法,根據(jù)拋出的異常類型調(diào)用適當(dāng)?shù)姆椒ā?/span>

           

          引入通知 給目標(biāo)對象添加新的方法 ( 以及屬性 )

           

           

          定義切入點(diǎn)

          切入點(diǎn) 就是應(yīng)用通知的地方,切入點(diǎn)決定了一個(gè)特定的類的特定方法是否滿足一條特定的規(guī)則,如果一個(gè)方法符合的話,通知就應(yīng)用到該方法上。 Spring 的切入點(diǎn)可以讓我們以一種靈活的方式定義在什么地方將通知織入到我們的類中。

          切入點(diǎn)的核心接口是 Pointcut 接口,同時(shí)它也需要兩個(gè)接口支持,如下:

          public interface Pointcut{

                 ClassFilter getClassFilter(); // 決定一個(gè)類是否符合要求

                 MethodMatcher getMethodMatcher(); // 決定一個(gè)方法是否符合要求

          }

          public interface ClassFilter{

                 boolean matches(Class clazz);

          }

          此接口總是包含一個(gè)簡單的 ClassFilter 接口的實(shí)現(xiàn) --ClassFilter.TRUE ,它是規(guī)范的適合任何類的 ClassFilter 實(shí)例,它適合于只根據(jù)方法決定什么時(shí)候符合要求的切入點(diǎn)。

          public interface MethodMatcher{

                 boolean matches(Method m,class targetClass);// 靜態(tài)的決定一個(gè)方法是否被通知織入

                 public boolean isRuntime();// 決定是靜態(tài)的還是動態(tài)的織入

                 public boolean mathes(Method m,Class targetClass,Object []args);// 動態(tài)的決定一個(gè)方法是否被通知織入,系統(tǒng)開銷會增加,不推薦使用

          }

           

           

          大多數(shù)的切面是由定義切面行為的通知和定義切面在什么地方執(zhí)行的切入點(diǎn)給合而成的, Spring 為此提供了 Advisor ,它把通知和切入點(diǎn)組合到一個(gè)對象中。接口 PointcutAdvisor 提供了這些功能,接口如下:

          public interface PointcutAdvisor{

                 Pointcut getPointcut();

                 Advice getAdvice();

          }

          大多數(shù) Spring 自帶的切入點(diǎn)都有一個(gè)對應(yīng)的 PointcutAdvisor 。這樣方便你在一個(gè)地方定義通知和切入點(diǎn)。

           

          使用 Spring 的靜態(tài)切入點(diǎn)

          Spring 為創(chuàng)建靜態(tài)切入點(diǎn)提供了方便的父類: StaticMethodMatcherPointcut 。如果想自定義靜態(tài)切入點(diǎn)的話,繼承這個(gè)類,并實(shí)現(xiàn) isMatch 方法就 OK 了。

          Spring 提供了一個(gè)靜態(tài)切入點(diǎn)的實(shí)現(xiàn)類: NameMatchMethodPointcut 。它有如下兩個(gè)方法:

          Public void setMappedName(String);

          Public void setMappedNames(String []);

          這個(gè)類通過名字映射,上面兩個(gè)方法的參數(shù)中均可以使用通配符 *

           

          規(guī)則表達(dá)式切入點(diǎn), Spring 提供了 RegexpMethodPointcut 讓你利用正則表達(dá)式的力量來定義切入點(diǎn)。

          符號

          描述

          示例

          .

          匹配任何單個(gè)字符

          setFoo. 匹配 setFooB, 但不匹配 setFoo setFooBar

          +

          匹配前一個(gè)字符一次或多次

          setFoo.+ 匹配 setFooBar setFooB, 但不匹配 setFoo

          *

          匹配前一個(gè)字符 0 次或多次

          setFoo.* 匹配 setFoo,setFooB setFooBar

          \

          匹配任何正則表達(dá)式符號

          \.setFoo. 匹配 bar.setFoo, 但不匹配 setFoo

          如果你想匹配所有 setXxx 方法,我們需要使用 .*set*. 模版 ( 第一個(gè)通配符匹配任何類名。筆者認(rèn)為此處《 Spring in action 》一書中有誤,我認(rèn)為此處應(yīng)為 .*set.*) 。如果使用 RegexpMethodPointcut ,你需要在你的應(yīng)用系統(tǒng)中引入 ORO 類庫。

          <bean id=”queryPointcutAdvisor”

                 Class=”org.springframework.aop.support.RegExPointcutAdvisor”>

                 <property name=”pattern”>

                        <value>.*get.+By.+</value>

                 </property>

                 <property name=”advice”>

                        <ref bean=”queryInterceptor”/>

                 </property>

          </bean>

           

          使用動態(tài)切入點(diǎn)

          Spring 提供了一種內(nèi)置的動態(tài)切入點(diǎn): ControlFlowPointcut 。這個(gè)切入點(diǎn)根據(jù)線程調(diào)用堆棧的信息來匹配方法。也就是說,它可以配置成只有當(dāng)指定方法或類能在當(dāng)前線程執(zhí)行堆棧中找到時(shí),返回 true

          <bean id=”servletPointcut” class=”org.springframework.aop.support.ControlFlowPointcut”>

                 <construct-arg>

                        <value>javax.servlet.http.HttpServlet</value>

                 </construct-arg>

          </bean>

          <bean id=”servletAdvisor” class=”org.springframework.aop.support.DefaultPointcutAdvisor”>

                 <property name=”advice”>

                        <ref bean=”servletInterceptor”/>

                 </property>

                 <property name=”pointcut”>

                        <ref bean=”servletPointcut”/>

                 </property>

          </bean>

          注: ControlFlowPointcut 明顯比其他的動態(tài)切入點(diǎn)慢。

           

          切入點(diǎn)實(shí)施

          Spring 支持在切入點(diǎn)上進(jìn)行操作 合并與產(chǎn)叉 來創(chuàng)建新的切入點(diǎn)。只有當(dāng)切入點(diǎn)都匹配時(shí)交叉集合才匹配,任何一個(gè)切入點(diǎn)匹配都會使合并集合匹配。 Spring 為創(chuàng)建這兩種切入點(diǎn)提供了兩個(gè)類:第一個(gè)類是 ComposablePointcut 。通過將已有的 ComposablePointcut 、切入點(diǎn)、 MethodMatcher 以及 ClassFilter 對象進(jìn)行合并或交叉,組裝成一個(gè)新的 ComposablePointcut 對象。這可以通過調(diào)用 ComposablePointcut 實(shí)例的 intersection () union () 方法實(shí)現(xiàn)。

          ComposablePointcut cp=new ComposablePoint()

          cp=p.intersection(myPointcut).union(myMethodmatcher);

          為了對兩個(gè) Pointcut 對象進(jìn)行合并,必須使用 Pointcuts 類。這是一個(gè)工具類,它擁有很多操作 Pointcut 對象的靜態(tài)方法。如:

          Pointcut union=Pointcuts.union(pointcut1,pointcut2);

           

          創(chuàng)建引入

          引入與其他類型的 Spring 通知有所不同。其它類型通知是在方法調(diào)用的周圍織入到不同的連接點(diǎn),而引入則是影響整個(gè)類,他們通過給需要消息的類型添加方法和屬性來實(shí)現(xiàn)。也就是說,你可以用一個(gè)已存在的類讓它實(shí)現(xiàn)另外的接口,維持另外的狀態(tài) ( 這也叫混合 ) 。換句話說,引入讓你能夠動態(tài)地建立復(fù)合對象,提供了多態(tài)繼承的好處。

          實(shí)現(xiàn) IntroductionInterceptor

          Spring 通過一個(gè)特殊的方法攔截器接口 IntroductionMethodInterceptor 來實(shí)現(xiàn)引入。這個(gè)接口有一個(gè)方法:

          Boolean implementsInterface(Class intf);

          如果 IntroductionMethodInterceptor 是為了實(shí)現(xiàn)指定接口,那么方法 implementsInterface 應(yīng)該返回 true 。就是說,對用這個(gè)接口聲明的方法的任何調(diào)用將被委托給 IntroductionMethodInterceptor invoke() 方法。 Invoke() 方法負(fù)責(zé)實(shí)現(xiàn)這個(gè)方法,不能調(diào)用 MethodInvocation.proceed() 。它引入了新的接口,調(diào)用目標(biāo)對象是沒有用的。

           

          使用 ProxyBeanFactory

          BeanFactory 對象是一個(gè)負(fù)責(zé)創(chuàng)建其他 JavaBean JavaBean 。屬性列表如下:

          屬性

          使 

          target

          代理的目標(biāo)對象

          proxyInterfaces

          代理應(yīng)該實(shí)現(xiàn)的接口列表

          interceptorNames

          需要應(yīng)用到目標(biāo)對象上的通知 Bean 的名字。可以是攔截器, Advisor 或其他通知類型的名字。這個(gè)屬性必須按照在 BeanFactory 中使用的順序設(shè)置。

          singleton

          是否返回的是同一個(gè)代理實(shí)例。如果使用的是狀態(tài)通知,應(yīng)該設(shè)置為 false

          aopProxyFactory

          使用的 ProxyFactoryBean 實(shí)現(xiàn)

          exposeProxy

          目標(biāo)對象是否需要得到當(dāng)前的代理。通過調(diào)用 AopContext.getCurrentProxy 實(shí)現(xiàn)。記住這樣做會在你的代碼中引入 Spring 專有的 AOP 代碼,所以,盡量避免使用。

          frozen

          一旦工廠被創(chuàng)建,是否可以修改代理的通知。

          optimize

          是否對創(chuàng)建的代理進(jìn)行優(yōu)化 ( 僅適用于 CGLIB)

          ProxyTargetClass

          是否代理目標(biāo)類,而不是實(shí)現(xiàn)接口。 ( 需要 CGLIB 支持 )

          大多數(shù)情況下我們只用到前三個(gè)屬性。

          如果想避免將目標(biāo)對象暴露給系統(tǒng)中其他 Bean 的話,可以將它聲明為一個(gè)內(nèi)部 Bean

          proxyInterfaces 屬性指定了從工廠中創(chuàng)建的 Bean 需要實(shí)現(xiàn)的接口。如:

          <property name=”proxyInterfaces”>

                 <value>com.springinaction.service.CourseService</value>

          </property>

          這樣就讓 ProxyBeanFactory 知道它創(chuàng)建的所有 Bean 都要實(shí)現(xiàn) CourseService 接口。可以像這樣只提供一個(gè)接口,也可以用 <list> 提供多個(gè)接口。

          interceptorNames 屬性定義了一個(gè)應(yīng)用到目標(biāo)對象上的 Advisor 或通知 Bean 的列表。目標(biāo) Bean 也可以放在此屬性的 <list> 列表的最后,但是最好還是用 Target 屬性設(shè)置目標(biāo) Bean

           

          自動代理

          Spring 有一個(gè)自動代理機(jī)制,它可以讓容器為我們產(chǎn)生代理。 Spring 有兩個(gè)類提供這種服務(wù): BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator

          BeanNameAutoProxyCreator 為匹配一系列名字的 Bean 自動創(chuàng)建代理。它允許在名字的兩端進(jìn)行通配符匹配。通常用于為符合相同命名規(guī)則的 Bean 應(yīng)用一個(gè)或一組切面 ( 主要是解決 target 屬性配置時(shí)目標(biāo)類過多的問題。 ) 如:

          <bean id=”preformanceThresholdProxyCreator

          Class=”org.springframework.aop.framework.autoproxy.BeanNameAutoProxyProlyCreator”>

          <property name=”beanNames”>

                 <list>

                        <value>*Service</value>

                 </list>

          </property>

          <propery name=”interceptorNames”>

                 <value>performaceThresholdInterceptor</value>

          </property>

          </bean>

          如果 Bean 是一個(gè) Advisor 或攔截器,它將應(yīng)用到代理對象的所有方法上。如果是通知的話, Advisor 切入點(diǎn)會根據(jù)不同 Bean 將通知應(yīng)用到不同的地方。

          自動代理框架對于代理需要暴露哪些接口作了一些假設(shè)。目標(biāo)對象實(shí)現(xiàn)的任何接口代理對象都要暴露出來。如果目標(biāo)類沒有實(shí)現(xiàn)任何接口,那么應(yīng)用于前面討論過的 ProxyFactoryBean 一樣的規(guī)則 動態(tài)生成一個(gè)子類。

          DefaultAdvisorAutoProxyCreator

          這個(gè)類的奇妙之處在于它實(shí)現(xiàn)了 BeanPostProcessor 接口。當(dāng) ApplicationContext 讀入所有 Bean 的配置信息后, DefaultAdvisorAutoProxyCreator 將掃描上下文,尋找所有的 Advisor 。它將這些 Advisor 應(yīng)用到所有符合 Advisor 切入點(diǎn)的 Bean 中。這個(gè)代理創(chuàng)建器只能與 Advisor 配合使用 ( 我們知道,一個(gè) Advisor 是一個(gè)切入點(diǎn)和一個(gè)通知的結(jié)合體。 DefaultAdvisorAutoProxyCreator 需要 Advisor 來知道哪些 Bean 需要通知。 )

          <bean id=”advisor” class=”org.springframework.aop.support.RegexpMethodPointcutAdvisor”>

                 <property name=”advice”>

                        <bean class=”performaceThresholdInterceptor”/>

                 </property>

                 <property name=”pattern”>

                        <value>.+Service\..+</value>

                 </property>

          </bean>

          <bean id=”autoProxyCreator”

          class=”org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator”/>

          在讀入所有 Bean 的定義信息后, BeanFactory 中的所有 Advisor 將被釋放出來。它們會將它們的通知配置到任何匹配它們的切入點(diǎn)的 Bean ( 這就是它真正的威力 )

          不用顯示地將 Advisor 與其他東西結(jié)合,現(xiàn)在只要簡單地定義它們,然后讓它們自動地應(yīng)用到它們匹配的地方。這樣松耦合 Bean 以及它們的通知就實(shí)現(xiàn)了。

           

          Spring 也支持通過元數(shù)據(jù) (Metadata) 驅(qū)動自動代理。這種類型自動代理,代理配置是通過源代碼屬性而不是外部配置文件獲得的。最常見的元數(shù)據(jù)自動配置是聲明式事務(wù)支持。

          posted on 2006-08-19 10:23 xzc 閱讀(417) 評論(0)  編輯  收藏 所屬分類: Spring
          <2006年8月>
          303112345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          常用鏈接

          留言簿(12)

          隨筆分類

          隨筆檔案

          收藏夾

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 达孜县| 瓮安县| 古蔺县| 江阴市| 临桂县| 阜新市| 县级市| 巢湖市| 通榆县| 甘孜县| 辉南县| 莱芜市| 德格县| 揭西县| 库尔勒市| 辽阳县| 蛟河市| 吉首市| 乌兰察布市| 新田县| 普宁市| 遂昌县| 邯郸市| 察雅县| 章丘市| 时尚| 八宿县| 七台河市| 平乡县| 固始县| 肥西县| 麻栗坡县| 抚松县| 福泉市| 湾仔区| 义乌市| 郧西县| 潢川县| 神农架林区| 静海县| 同江市|