??? ??? 在結(jié)束了上一篇Spring 1.x中AOP的使用之后,我又馬不停蹄的打開(kāi)Eclipse,做著Spring2.X下了AOP的Sample。在上一篇文章中的配置過(guò)程中,由于對(duì)自動(dòng)代理不是很熟,出現(xiàn)了循環(huán)引用的異常信息。當(dāng)初在閱讀PicoContainer源碼時(shí)看到循環(huán)引用不以為然,后來(lái)在學(xué)習(xí)AspectJ時(shí)小有印象,這次在折騰了半個(gè)多小時(shí)后可加深了印象。
??? ??? 好啦,閑言少敘,奔向文本的主題吧。和Spring1.X相比,Spring2.X使用AspectJ的語(yǔ)法來(lái)聲明AOP,這使得它更“標(biāo)準(zhǔn)”,更靈活了。還是那句話(huà),如果你不了解AspectJ并且打算使用Spring2.X的AspectJ式AOP,那就學(xué)學(xué)AspectJ吧,這方面的書(shū)還是很多了。
??? ??? Spring2.X下的切面有兩種實(shí)現(xiàn)方式,一種是以Java文件定義切面(其只是普通的Java類(lèi)),然后在配置文件中聲明定義的切面;另一種是在Java類(lèi)中引入和AOP相關(guān)的元數(shù)據(jù)(注釋?zhuān)?/p>
??? ??? 先介紹第一種配置方式。需要指出的是,Spring2.X的beans名稱(chēng)空間和Spring1.X有所不同,它采用了Schema而不是DTD(也可采用DTD方式,具體的請(qǐng)參考文檔)。還是引入毫無(wú)意義的日志切面,定義的切面類(lèi)LogingAspect 如下:
public
?
class
?LogingAspect?{
public
?
void
?logMethod(JoinPoint?jp){
????????System.err.println(jp.getTarget().getClass());
????????System.err.println(jp.getSignature().getName());
}
}
??? ??? 同時(shí)在配置文件中如下配置:
<
bean?id
=
"
logAspectTarget
"
?
class
=
"
hibernatesample.service.util.LogingAspect
"
></
bean
>
<
aop:config
>
<
aop:aspect?id
=
"
logAspect
"
?ref
=
"
logAspectTarget
"
>
<
aop:pointcut?id
=
"
businessService
"
?expression
=
"
execution(*?hibernatesample.service.*.*(..))
"
/>
<
aop:after?pointcut
-
ref
=
"
businessService
"
?method
=
"
logMethod
"
/>
</
aop:aspect
>
</
aop:config
>
???????對(duì)于上面的切面,切入點(diǎn)businessService是在配置文件中聲明的,其表達(dá)式采用了 AspectJ的語(yǔ)法,LogingAspect?類(lèi)中logMethod(JoinPoint jp)方法根據(jù)配置文件信息可知其是after通知,方法的JoinPoint參數(shù)不是必須的,它是來(lái)自于AspectJ的實(shí)用類(lèi),這里用它不過(guò)輸出一些和連接點(diǎn)有關(guān)的信息。當(dāng)然,在Spring2.X中,切入點(diǎn)和通知能更靈活的使用,我們可以如AspectJ一樣傳遞參數(shù)給通知。如果需要在Service中引入事務(wù)功能,需要如下配置事務(wù)通知:
<
tx:advice?id
=
"
txAdvice
"
?transaction
-
manager
=
"
transactionManager
"
>
<
tx:attributes
>
<
tx:method?name
=
"
get*
"
?read
-
only
=
"
true
"
/>
<
tx:method?name
=
"
find*
"
?read
-
only
=
"
true
"
/>
<
tx:method?name
=
"
*
"
/>
</
tx:attributes
>
</
tx:advice
>
<
aop:config
>
<
aop:pointcut?id
=
"
demoServiceMethods
"
?expression
=
"
execution(*?hibernatesample.service.*.*(..))
"
/>
<
aop:advisor?advice
-
ref
=
"
txAdvice
"
?pointcut
-
ref
=
"
demoServiceMethods
"
/>
<
aop:aspect?id
=
"
logAspect
"
?ref
=
"
logAspectTarget
"
>
<
aop:pointcut?id
=
"
businessService
"
?expression
=
"
execution(*?hibernatesample.service.*.*(..))
"
/>
<
aop:after?pointcut
-
ref
=
"
businessService
"
?method
=
"
logMethod
"
/>
</
aop:aspect
>
</
aop:config
>
??? ??? 完成上面的工作相當(dāng)于完成了
Spring1.X 的
自動(dòng)代理。
我們接下來(lái)需要定義的任何
Service Bean
都可以很純粹很純粹了:
<
bean?
id
="accountService"
?class
="hibernatesample.service.impl.AccountServiceImpl"
>
<
property?
name
="accountDAO"
?ref
="accountDAO"
></
property
>
</
bean
>
??? ??? 第二種實(shí)現(xiàn)
AOP
的方式和第一種相比,只是在
LogingAspect
中加入了注釋?zhuān)∪チ伺渲梦募泻?/font>
LogingAspect
相關(guān)的配置。重新編寫(xiě)的
LogingAspect
如下:
@Aspect
public
?
class
?LogingAspect?{
@Pointcut(
"
execution(*?hibernatesample.service.*.*(..))
"
)
public
?
void
?businessService(){}
@After(
"
businessService()
"
)
public
?
void
?logMethod(JoinPoint?jp){
System.err.println(jp.getTarget().getClass());
System.err.println(jp.getSignature().getName());
}
}
??? ??? 而簡(jiǎn)化后的配置文件可以去除上面的如下和
logAspect
相關(guān)的配置信息:
<
aop:aspect?
id
="logAspect"
?ref
="logAspectTarget"
>
<
aop:pointcut?
id
="businessService"
?expression
="execution(*?hibernatesample.service.*.*(..))"
/>
<
aop:after?
pointcut-ref
="businessService"
?method
="logMethod"
/>
</
aop:aspect
>
<
bean?
id
="logAspectTarget"
?class
="hibernatesample.service.util.LogingAspect"
></
bean
>
??? ??? 還沒(méi)有完,為了使
Spring
應(yīng)用
LogingAspect?
的注釋?zhuān)枰谂渲梦募刑砩?/font>
??? ??? <aop:aspectj-autoproxy/>
???? ??
? 如果覺(jué)得事務(wù)的配置沒(méi)有使用注釋更簡(jiǎn)潔(我倒不會(huì)有這樣的想法,畢竟在配置文件中聲明的事務(wù)只是那么固定的幾段,除非作用于類(lèi)上的事務(wù)邏輯上很復(fù)雜),也可以使用Spring提供的事務(wù)注釋作用于類(lèi)文件上,這可是更細(xì)粒度的事務(wù)聲明了。
??????坦率的說(shuō),由于時(shí)間有限,該文寫(xiě)的比較粗糙。對(duì)于Spring AOP有興趣并有疑問(wèn)的朋友,可以參考Spring的文檔,它的文檔做的還是不錯(cuò)的。