日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
AOP那點(diǎn)事兒(續(xù)集)

在上篇中,我們從寫死代碼,到使用代理;從編程式 Spring AOP 到聲明式 Spring AOP。一切都朝著簡(jiǎn)單實(shí)用主義的方向在發(fā)展。沿著 Spring AOP 的方向,Rod Johnson(老羅)花了不少心思,都是為了讓我們使用 Spring 框架時(shí)不會(huì)感受到麻煩,但事實(shí)卻并非如此。那么,后來(lái)老羅究竟對(duì) Spring AOP 做了哪些改進(jìn)呢?

現(xiàn)在繼續(xù)!

9. Spring AOP:切面

之前談到的 AOP 框架其實(shí)可以將它理解為一個(gè)攔截器框架,但這個(gè)攔截器似乎非常武斷。比如說(shuō),如果它攔截了一個(gè)類,那么它就攔截了這個(gè)類中所有的方法。類似地,當(dāng)我們?cè)谑褂脛?dòng)態(tài)代理的時(shí)候,其實(shí)也遇到了這個(gè)問(wèn)題。需要在代碼中對(duì)所攔截的方法名加以判斷,才能過(guò)濾出我們需要攔截的方法,想想這種做法確實(shí)不太優(yōu)雅。在大量的真實(shí)項(xiàng)目中,似乎我們只需要攔截特定的方法就行了,沒(méi)必要攔截所有的方法。于是,老羅同志借助了 AOP 的一個(gè)很重要的工具,Advisor(切面),來(lái)解決這個(gè)問(wèn)題。它也是 AOP 中的核心!是我們關(guān)注的重點(diǎn)!

也就是說(shuō),我們可以通過(guò)切面,將增強(qiáng)類與攔截匹配條件組合在一起,然后將這個(gè)切面配置到 ProxyFactory 中,從而生成代理。

這里提到這個(gè) “攔截匹配條件” 在 AOP 中就叫做 Pointcut(切點(diǎn)),其實(shí)說(shuō)白了就是一個(gè)基于表達(dá)式的攔截條件罷了。

歸納一下,Advisor(切面)封裝了 Advice(增強(qiáng))與 Pointcut(切點(diǎn) )。當(dāng)您理解了這句話后,就往下看吧。

我在 GreetingImpl 類中故意增加了兩個(gè)方法,都以 “good” 開頭。下面要做的就是攔截這兩個(gè)新增的方法,而對(duì) sayHello () 方法不作攔截。

@Component
public class GreetingImpl implements Greeting {

@Override
public void sayHello(String name) {
System.out.println("Hello! " + name);
}

public void goodMorning(String name) {
System.out.println("Good Morning! " + name);
}

public void goodNight(String name) {
System.out.println("Good Night! " + name);
}
}

在 Spring AOP 中,老羅已經(jīng)給我們提供了許多切面類了,這些切面類我個(gè)人感覺(jué)最好用的就是基于正則表達(dá)式的切面類??纯茨兔靼琢耍?/p>



















注意以上代理對(duì)象的配置中的 interceptorNames,它不再是一個(gè)增強(qiáng),而是一個(gè)切面,因?yàn)橐呀?jīng)將增強(qiáng)封裝到該切面中了。此外,切面還定義了一個(gè)切點(diǎn)(正則表達(dá)式),其目的是為了只將滿足切點(diǎn)匹配條件的方法進(jìn)行攔截。

需要強(qiáng)調(diào)的是,這里的切點(diǎn)表達(dá)式是基于正則表達(dá)式的。示例中的 “aop.demo.GreetingImpl.good.*” 表達(dá)式后面的 “.*” 表示匹配所有字符,翻譯過(guò)來(lái)就是 “匹配 aop.demo.GreetingImpl 類中以 good 開頭的方法”。

除了 RegexpMethodPointcutAdvisor 以外,在 Spring AOP 中還提供了幾個(gè)切面類,比如:

  • DefaultPointcutAdvisor:默認(rèn)切面(可擴(kuò)展它來(lái)自定義切面)
  • NameMatchMethodPointcutAdvisor:根據(jù)方法名稱進(jìn)行匹配的切面
  • StaticMethodMatcherPointcutAdvisor:用于匹配靜態(tài)方法的切面

總的來(lái)說(shuō),讓用戶去配置一個(gè)或少數(shù)幾個(gè)代理,似乎還可以接受,但隨著項(xiàng)目的擴(kuò)大,代理配置就會(huì)越來(lái)越多,配置的重復(fù)勞動(dòng)就多了,麻煩不說(shuō),還很容易出錯(cuò)。能否讓 Spring 框架為我們自動(dòng)生成代理呢?

10. Spring AOP:自動(dòng)代理(掃描 Bean 名稱)

Spring AOP 提供了一個(gè)可根據(jù) Bean 名稱來(lái)自動(dòng)生成代理的工具,它就是 BeanNameAutoProxyCreator。是這樣配置的:




...







以上使用 BeanNameAutoProxyCreator 只為后綴為 “Impl” 的 Bean 生成代理。需要注意的是,這個(gè)地方我們不能定義代理接口,也就是 interfaces 屬性,因?yàn)槲覀兏揪筒恢肋@些 Bean 到底實(shí)現(xiàn)了多少接口。此時(shí)不能代理接口,而只能代理類。所以這里提供了一個(gè)新的配置項(xiàng),它就是 optimize。若為 true 時(shí),則可對(duì)代理生成策略進(jìn)行優(yōu)化(默認(rèn)是 false 的)。也就是說(shuō),如果該類有接口,就代理接口(使用 JDK 動(dòng)態(tài)代理);如果沒(méi)有接口,就代理類(使用 CGLib 動(dòng)態(tài)代理)。而并非像之前使用的 proxyTargetClass 屬性那樣,強(qiáng)制代理類,而不考慮代理接口的方式??梢?Spring AOP 確實(shí)為我們提供了很多很好地服務(wù)!

既然 CGLib 可以代理任何的類了,那為什么還要用 JDK 的動(dòng)態(tài)代理呢?肯定您會(huì)這樣問(wèn)。

根據(jù)多年來(lái)實(shí)際項(xiàng)目經(jīng)驗(yàn)得知:CGLib 創(chuàng)建代理的速度比較慢,但創(chuàng)建代理后運(yùn)行的速度卻非???,而 JDK 動(dòng)態(tài)代理正好相反。如果在運(yùn)行的時(shí)候不斷地用 CGLib 去創(chuàng)建代理,系統(tǒng)的性能會(huì)大打折扣,所以建議一般在系統(tǒng)初始化的時(shí)候用 CGLib 去創(chuàng)建代理,并放入 Spring 的 ApplicationContext 中以備后用。

以上這個(gè)例子只能匹配目標(biāo)類,而不能進(jìn)一步匹配其中指定的方法,要匹配方法,就要考慮使用切面與切點(diǎn)了。Spring AOP 基于切面也提供了一個(gè)自動(dòng)代理生成器:DefaultAdvisorAutoProxyCreator。

11. Spring AOP:自動(dòng)代理(掃描切面配置)

為了匹配目標(biāo)類中的指定方法,我們?nèi)匀恍枰?Spring 中配置切面與切點(diǎn):




...










這里無(wú)需再配置代理了,因?yàn)榇韺?huì)由 DefaultAdvisorAutoProxyCreator 自動(dòng)生成。也就是說(shuō),這個(gè)類可以掃描所有的切面類,并為其自動(dòng)生成代理。

看來(lái)不管怎樣簡(jiǎn)化,老羅始終解決不了切面的配置,這件繁重的手工勞動(dòng)。在 Spring 配置文件中,仍然會(huì)存在大量的切面配置。然而在有很多情況下 Spring AOP 所提供的切面類真的不太夠用了,比如:想攔截指定注解的方法,我們就必須擴(kuò)展 DefaultPointcutAdvisor 類,自定義一個(gè)切面類,然后在 Spring 配置文件中進(jìn)行切面配置。不做不知道,做了您就知道相當(dāng)麻煩了。

老羅的解決方案似乎已經(jīng)掉進(jìn)了切面類的深淵,這還真是所謂的 “面向切面編程” 了,最重要的是切面,最麻煩的也是切面。

必須要把切面配置給簡(jiǎn)化掉,Spring 才能有所突破!

神一樣的老羅總算認(rèn)識(shí)到了這一點(diǎn),接受了網(wǎng)友們的建議,集成了 AspectJ,同時(shí)也保留了以上提到的切面與代理配置方式(為了兼容老的項(xiàng)目,更為了維護(hù)自己的面子)。將 Spring 與 AspectJ 集成與直接使用 AspectJ 是不同的,我們不需要定義 AspectJ 類(它是擴(kuò)展了 Java 語(yǔ)法的一種新的語(yǔ)言,還需要特定的編譯器),只需要使用 AspectJ 切點(diǎn)表達(dá)式即可(它是比正則表達(dá)式更加友好的表現(xiàn)形式)。

12. Spring + AspectJ(基于注解:通過(guò) AspectJ execution 表達(dá)式攔截方法)

下面以一個(gè)最簡(jiǎn)單的例子,實(shí)現(xiàn)之前提到的環(huán)繞增強(qiáng)。先定義一個(gè) Aspect 切面類:

@Aspect
@Component
public class GreetingAspect {

@Around("execution(* aop.demo.GreetingImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
before();
Object result = pjp.proceed();
after();
return result;
}

private void before() {
System.out.println("Before");
}

private void after() {
System.out.println("After");
}
}

注意:類上面標(biāo)注的 @Aspect 注解,這表明該類是一個(gè) Aspect(其實(shí)就是 Advisor)。該類無(wú)需實(shí)現(xiàn)任何的接口,只需定義一個(gè)方法(方法叫什么名字都無(wú)所謂),只需在方法上標(biāo)注 @Around 注解,在注解中使用了 AspectJ 切點(diǎn)表達(dá)式。方法的參數(shù)中包括一個(gè) ProceedingJoinPoint 對(duì)象,它在 AOP 中稱為 Joinpoint(連接點(diǎn)),可以通過(guò)該對(duì)象獲取方法的任何信息,例如:方法名、參數(shù)等。

下面重點(diǎn)來(lái)分析一下這個(gè)切點(diǎn)表達(dá)式:

execution(* aop.demo.GreetingImpl.*(..))

  • execution ():表示攔截方法,括號(hào)中可定義需要匹配的規(guī)則。
  • 第一個(gè) “*”:表示方法的返回值是任意的。
  • 第二個(gè) “*”:表示匹配該類中所有的方法。
  • (..):表示方法的參數(shù)是任意的。

是不是比正則表達(dá)式的可讀性更強(qiáng)呢?如果想匹配指定的方法,只需將第二個(gè) “*” 改為指定的方法名稱即可。

如何配置呢?看看是有多簡(jiǎn)單吧:


xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">





兩行配置就行了,不需要配置大量的代理,更不需要配置大量的切面,真是太棒了!需要注意的是 proxy-target-class="true" 屬性,它的默認(rèn)值是 false,默認(rèn)只能代理接口(使用 JDK 動(dòng)態(tài)代理),當(dāng)為 true 時(shí),才能代理目標(biāo)類(使用 CGLib 動(dòng)態(tài)代理)。

Spring 與 AspectJ 結(jié)合的威力遠(yuǎn)遠(yuǎn)不止這些,我們來(lái)點(diǎn)時(shí)尚的吧,攔截指定注解的方法怎么樣?

13. Spring + AspectJ(基于注解:通過(guò) AspectJ @annotation 表達(dá)式攔截方法)

為了攔截指定的注解的方法,我們首先需要來(lái)自定義一個(gè)注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Tag {
}

以上定義了一個(gè) @Tag 注解,此注解可標(biāo)注在方法上,在運(yùn)行時(shí)生效。

只需將前面的 Aspect 類的切點(diǎn)表達(dá)式稍作改動(dòng):

@Aspect
@Component
public class GreetingAspect {

@Around("@annotation(aop.demo.Tag)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
...
}

...
}

這次使用了 @annotation () 表達(dá)式,只需在括號(hào)內(nèi)定義需要攔截的注解名稱即可。

直接將 @Tag 注解定義在您想要攔截的方法上,就這么簡(jiǎn)單:

@Component
public class GreetingImpl implements Greeting {

@Tag
@Override
public void sayHello(String name) {
System.out.println("Hello! " + name);
}
}

以上示例中只有一個(gè)方法,如果有多個(gè)方法,我們只想攔截其中某些時(shí),這種解決方案會(huì)更加有價(jià)值。

除了 @Around 注解外,其實(shí)還有幾個(gè)相關(guān)的注解,稍微歸納一下吧:

  • @Before:前置增強(qiáng)
  • @After:后置增強(qiáng)
  • @Around:環(huán)繞增強(qiáng)
  • @AfterThrowing:拋出增強(qiáng)
  • @DeclareParents:引入增強(qiáng)

此外還有一個(gè) @AfterReturning(返回后增強(qiáng)),也可理解為 Finally 增強(qiáng),相當(dāng)于 finally 語(yǔ)句,它是在方法結(jié)束后執(zhí)行的,也就說(shuō)說(shuō),它比 @After 還要晚一些。

最后一個(gè) @DeclareParents 竟然就是引入增強(qiáng)!為什么不叫做 @Introduction 呢?我也不知道為什么,但它干的活就是引入增強(qiáng)。

14. Spring + AspectJ(引入增強(qiáng))

為了實(shí)現(xiàn)基于 AspectJ 的引入增強(qiáng),我們同樣需要定義一個(gè) Aspect 類:

@Aspect
@Component
public class GreetingAspect {

@DeclareParents(value = "aop.demo.GreetingImpl", defaultImpl = ApologyImpl.class)
private Apology apology;
}

只需要在 Aspect 類中定義一個(gè)需要引入增強(qiáng)的接口,它也就是運(yùn)行時(shí)需要?jiǎng)討B(tài)實(shí)現(xiàn)的接口。在這個(gè)接口上標(biāo)注了 @DeclareParents 注解,該注解有兩個(gè)屬性:

  • value:目標(biāo)類
  • defaultImpl:引入接口的默認(rèn)實(shí)現(xiàn)類

我們只需要對(duì)引入的接口提供一個(gè)默認(rèn)實(shí)現(xiàn)類即可完成引入增強(qiáng):

public class ApologyImpl implements Apology {

@Override
public void saySorry(String name) {
System.out.println("Sorry! " + name);
}
}

以上這個(gè)實(shí)現(xiàn)會(huì)在運(yùn)行時(shí)自動(dòng)增強(qiáng)到 GreetingImpl 類中,也就是說(shuō),無(wú)需修改 GreetingImpl 類的代碼,讓它去實(shí)現(xiàn) Apology 接口,我們單獨(dú)為該接口提供一個(gè)實(shí)現(xiàn)類(ApologyImpl),來(lái)做 GreetingImpl 想做的事情。

還是用一個(gè)客戶端來(lái)嘗試一下吧:

public class Client {

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("aop/demo/spring.xml");
Greeting greeting = (Greeting) context.getBean("greetingImpl");
greeting.sayHello("Jack");

Apology apology = (Apology) greeting; // 強(qiáng)制轉(zhuǎn)型為 Apology 接口
apology.saySorry("Jack");
}
}

從 Spring ApplicationContext 中獲取 greetingImpl 對(duì)象(其實(shí)是個(gè)代理對(duì)象),可轉(zhuǎn)型為自己靜態(tài)實(shí)現(xiàn)的接口 Greeting,也可轉(zhuǎn)型為自己動(dòng)態(tài)實(shí)現(xiàn)的接口 Apology,切換起來(lái)非常方便。

使用 AspectJ 的引入增強(qiáng)比原來(lái)的 Spring AOP 的引入增強(qiáng)更加方便了,而且還可面向接口編程(以前只能面向?qū)崿F(xiàn)類),這也算一個(gè)非常巨大的突破。

這一切真的已經(jīng)非常強(qiáng)大也非常靈活了!但仍然還是有用戶不能嘗試這些特性,因?yàn)樗麄冞€在使用 JDK 1.4(根本就沒(méi)有注解這個(gè)東西),怎么辦呢?沒(méi)想到 Spring AOP 為那些遺留系統(tǒng)也考慮到了。

15. Spring + AspectJ(基于配置)

除了使用 @Aspect 注解來(lái)定義切面類以外,Spring AOP 也提供了基于配置的方式來(lái)定義切面類:














使用元素來(lái)進(jìn)行 AOP 配置,在其子元素中配置切面,包括增強(qiáng)類型、目標(biāo)方法、切點(diǎn)等信息。

無(wú)論您是不能使用注解,還是不愿意使用注解,Spring AOP 都能為您提供全方位的服務(wù)。

好了,我所知道的比較實(shí)用的 AOP 技術(shù)都在這里了,當(dāng)然還有一些更為高級(jí)的特性,由于個(gè)人精力有限,這里就不再深入了。

還是依照慣例,給一張牛逼的高清無(wú)碼思維導(dǎo)圖,總結(jié)一下以上各個(gè)知識(shí)點(diǎn):

再來(lái)一張表格,總結(jié)一下各類增強(qiáng)類型所對(duì)應(yīng)的解決方案:

最后給一張 UML 類圖描述一下 Spring AOP 的整體架構(gòu):


分享名稱:AOP那點(diǎn)事兒(續(xù)集)
標(biāo)題網(wǎng)址:http://m.5511xx.com/article/dpdcijp.html