日韩无码专区无码一级三级片|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)銷(xiāo)解決方案
Spring框架之SpringAOP

AOP概念

讓我們首先定義一些核心的AOP概念和術(shù)語(yǔ):

公司主營(yíng)業(yè)務(wù):網(wǎng)站設(shè)計(jì)制作、做網(wǎng)站、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)公司推出秀洲免費(fèi)做網(wǎng)站回饋大家。

  • Aspect:切面。一個(gè)跨越多個(gè)類(lèi)的關(guān)注點(diǎn)的模塊化。在Spring AOP中,切面是通過(guò)使用常規(guī)類(lèi)(基于模式的方法)或使用@Aspect注解的常規(guī)類(lèi)(@AspectJ風(fēng)格)來(lái)實(shí)現(xiàn)的。
  • Join point:連接點(diǎn)。在程序執(zhí)行過(guò)程中的一個(gè)點(diǎn),例如一個(gè)方法的執(zhí)行或一個(gè)異常的處理。在Spring AOP中,一個(gè)連接點(diǎn)總是代表一個(gè)方法的執(zhí)行。
  • Advice:通知。一個(gè)切面在一個(gè)特定的連接點(diǎn)采取的行動(dòng)。不同類(lèi)型的Advice包括 "around"、"before "和 "after "的通知。(許多AOP框架,包括Spring,都將通知建模為一個(gè)攔截器,并在連接點(diǎn)周?chē)S護(hù)一個(gè)攔截器鏈。
  • Pointcut:切點(diǎn)。帶有通知的連接點(diǎn),在程序中主要體現(xiàn)為書(shū)寫(xiě)Pointcut表達(dá)式。Spring默認(rèn)使用AspectJ的切點(diǎn)表達(dá)式語(yǔ)言。
  • Introduction:代表一個(gè)類(lèi)型聲明額外的方法或字段。Spring AOP允許你為任何Advice的對(duì)象引入新的接口(以及相應(yīng)的實(shí)現(xiàn))。例如,你可以使用引入來(lái)使一個(gè)bean實(shí)現(xiàn)IsModified接口,以簡(jiǎn)化緩存。(Introduction在AspectJ社區(qū)中被稱(chēng)為類(lèi)型間聲明)。
  • Target object:目標(biāo)對(duì)象。被一個(gè)或多個(gè)切面通知的對(duì)象。也被稱(chēng)為 "Advice對(duì)象"。由于Spring AOP是通過(guò)使用運(yùn)行時(shí)代理來(lái)實(shí)現(xiàn)的,這個(gè)對(duì)象總是一個(gè)被代理的對(duì)象。
  • AOP proxy:AOP代理。一個(gè)由AOP框架創(chuàng)建的對(duì)象,以實(shí)現(xiàn)切面契約(通知方法執(zhí)行等)。在Spring框架中,AOP代理是一個(gè)JDK動(dòng)態(tài)代理或CGLIB代理。
  • Weaving:將aspect與其他應(yīng)用類(lèi)型或?qū)ο舐?lián)系起來(lái),以創(chuàng)建一個(gè)Advice對(duì)象。這可以在編譯時(shí)(例如,使用AspectJ編譯器)、加載時(shí)或運(yùn)行時(shí)完成。Spring AOP和其他純Java AOP框架一樣,在運(yùn)行時(shí)進(jìn)行織入。

Spring AOP包括以下類(lèi)型的Advice:

  • Before advice:在連接點(diǎn)之前運(yùn)行的通知,但它沒(méi)有能力阻止執(zhí)行流進(jìn)行到連接點(diǎn)(除非它拋出一個(gè)異常)。
  • After returning advice:在一個(gè)連接點(diǎn)正常完成后運(yùn)行的Advice(例如,如果一個(gè)方法返回而沒(méi)有拋出異常)。
  • After throwing advice:當(dāng)一個(gè)方法通過(guò)拋出異常退出時(shí)要運(yùn)行的Advice。
  • After (finally) advice:無(wú)論連接點(diǎn)以何種方式退出(正?;虍惓7祷兀家\(yùn)行Advice。
  • Around advice:圍繞連接點(diǎn)的Advice,如方法調(diào)用。這是最強(qiáng)大的一種Advice。Around advice可以在方法調(diào)用之前和之后執(zhí)行自定義行為。它還負(fù)責(zé)選擇是否繼續(xù)進(jìn)行連接點(diǎn),或者通過(guò)返回自己的返回值或拋出一個(gè)異常來(lái)縮短Advice方法的執(zhí)行。

Around advice是最通用的一種Advice。由于Spring AOP和AspectJ一樣,提供了全面的Advice類(lèi)型,我們建議你使用功能最弱的Advice類(lèi)型來(lái)實(shí)現(xiàn)所需的行為。例如,如果你只需要用一個(gè)方法的返回值來(lái)更新緩存,那么你最好實(shí)現(xiàn)一個(gè)After returning advice,而不是一個(gè)Around advice,盡管Around advice可以完成同樣的事情。使用最具體的Advice類(lèi)型可以提供一個(gè)更簡(jiǎn)單的編程模型,減少錯(cuò)誤的可能性。

所有的Advice參數(shù)都是靜態(tài)類(lèi)型的,因此你可以使用適當(dāng)類(lèi)型的Advice參數(shù)(例如,方法執(zhí)行的返回值的類(lèi)型),而不是Object數(shù)組。

由切點(diǎn)匹配的連接點(diǎn)的概念是AOP的關(guān)鍵,它區(qū)別于只提供攔截的舊技術(shù)。Pointcuts使Advice能夠獨(dú)立于面向?qū)ο蟮膶哟谓Y(jié)構(gòu)而成為目標(biāo)。例如,你可以將提供聲明性事務(wù)管理的建議應(yīng)用于跨越多個(gè)對(duì)象的一組方法(如服務(wù)層的所有業(yè)務(wù)操作)。

AOP Proxy

Spring AOP默認(rèn)使用標(biāo)準(zhǔn)的JDK動(dòng)態(tài)代理作為AOP代理。這使得任何接口(或一組接口)都可以被代理。

Spring AOP也可以使用CGLIB代理。這對(duì)于代理類(lèi)(不是接口)來(lái)說(shuō)是必要的。默認(rèn)情況下,如果一個(gè)業(yè)務(wù)對(duì)象沒(méi)有實(shí)現(xiàn)一個(gè)接口,就會(huì)使用CGLIB。由于對(duì)接口而不是類(lèi)進(jìn)行編程是很好的做法,業(yè)務(wù)類(lèi)通常實(shí)現(xiàn)一個(gè)或多個(gè)業(yè)務(wù)接口。在那些(希望是罕見(jiàn)的)需要向未在接口上聲明的方法提供Advice的情況下,或者需要將代理對(duì)象作為具體類(lèi)型傳遞給方法的情況下,可以強(qiáng)制使用CGLIB。

@AspectJ

@AspectJ指的是將aspect作為帶有注解的普通Java類(lèi)來(lái)聲明的一種風(fēng)格。

啟用@AspectJ

要在Spring配置中使用@AspectJ切面,需要啟用Spring支持,以根據(jù)@AspectJ切面配置Spring AOP,并根據(jù)Bean是否被這些切面通知而自動(dòng)代理。通過(guò)自動(dòng)代理,我們的意思是,如果Spring確定一個(gè)Bean被一個(gè)或多個(gè)切面所通知,它會(huì)自動(dòng)為該Bean生成一個(gè)代理,以攔截方法調(diào)用,并確保建議在需要時(shí)被運(yùn)行。

@AspectJ支持可以通過(guò)XML或Java風(fēng)格的配置來(lái)啟用。在這兩種情況下,你還需要確保AspectJ的aspectjweaver.jar庫(kù)在你的應(yīng)用程序的classpath上(版本1.8或更高)。

用Java配置啟用@AspectJ 要通過(guò)Java @Configuration啟用@AspectJ支持,請(qǐng)?zhí)砑覢EnableAspectJAutoProxy注解,如下例所示:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}

Aspect聲明

在啟用@AspectJ的情況下,任何在你的應(yīng)用程序上下文中定義的Bean,其類(lèi)是@AspectJ切面(具有@Aspect注解),會(huì)被Spring自動(dòng)檢測(cè)到,并用于配置Spring AOP。接下來(lái)的兩個(gè)例子顯示了一個(gè)不怎么有用的切面所需的最小定義:

package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect {

}
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
@Bean
public NotVeryUsefulAspect myAspect(){
return new NotVeryUsefulAspect();
}
}

Pointcut聲明

Pointcuts確定連接點(diǎn),從而使我們能夠控制Advice的運(yùn)行時(shí)間。Spring AOP只支持Spring Bean的方法執(zhí)行連接點(diǎn),所以你可以把pointcut看作是與Spring Bean上的方法執(zhí)行相匹配。一個(gè)切點(diǎn)聲明有兩個(gè)部分:一個(gè)由名稱(chēng)和任何參數(shù)組成的簽名,以及一個(gè)切點(diǎn)表達(dá)式,它決定了我們到底對(duì)哪些方法的執(zhí)行感興趣。在AOP的@AspectJ注解式中,一個(gè)pointcut簽名是由一個(gè)常規(guī)的方法定義提供的,而pointcut表達(dá)式是通過(guò)使用@Pointcut注解來(lái)表示的(作為pointcut簽名的方法必須有一個(gè)void返回類(lèi)型)。

@Pointcut("execution(* transfer(..))") // the pointcut expression
private void anyOldTransfer() {} // the pointcut signature

Pointcut表達(dá)式

Spring AOP支持以下AspectJ的切點(diǎn)表達(dá)式,以便在切點(diǎn)表達(dá)式中使用:

  • execution:用于匹配方法執(zhí)行的連接點(diǎn)。這是在使用Spring AOP時(shí)要使用的主要切點(diǎn)指定符。
  • within:將匹配限制在某些類(lèi)型的連接點(diǎn)上(當(dāng)使用Spring AOP時(shí),在匹配類(lèi)型中聲明的方法的執(zhí)行)。
  • this:限制匹配到連接點(diǎn)(使用Spring AOP時(shí)方法的執(zhí)行),其中Bean引用(Spring AOP代理)是給定類(lèi)型的實(shí)例。
  • target:限制對(duì)連接點(diǎn)的匹配(使用Spring AOP時(shí)方法的執(zhí)行),目標(biāo)對(duì)象(被代理的應(yīng)用程序?qū)ο螅┦墙o定類(lèi)型的實(shí)例。
  • args:限制對(duì)連接點(diǎn)的匹配(使用Spring AOP時(shí)方法的執(zhí)行),參數(shù)是給定類(lèi)型的實(shí)例。
  • @target:限制對(duì)連接點(diǎn)的匹配(使用Spring AOP時(shí)方法的執(zhí)行),其中執(zhí)行對(duì)象的類(lèi)有一個(gè)給定類(lèi)型的注釋。
  • @args:限制對(duì)連接點(diǎn)的匹配(使用Spring AOP時(shí)方法的執(zhí)行),其中實(shí)際傳遞的參數(shù)的運(yùn)行時(shí)間類(lèi)型有給定類(lèi)型的注釋。
  • @within:限制匹配到具有給定注釋的類(lèi)型中的連接點(diǎn)(使用Spring AOP時(shí),執(zhí)行在具有給定注釋的類(lèi)型中聲明的方法)。
  • @annotation:限制匹配到連接點(diǎn)的主體(在Spring AOP中被運(yùn)行的方法)具有給定注釋的連接點(diǎn)。

Pointcut表達(dá)式組合

你可以通過(guò)使用&&、||和!來(lái)組合pointcut表達(dá)式。你也可以通過(guò)名字來(lái)引用pointcut表達(dá)式。下面的例子顯示了三個(gè)切點(diǎn)表達(dá)式:

@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {}
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}

共享通用pointcut定義

在處理企業(yè)應(yīng)用程序時(shí),開(kāi)發(fā)人員經(jīng)常想從幾個(gè)方面來(lái)引用應(yīng)用程序的模塊和特定的操作集。我們建議定義一個(gè)CommonPointcuts切面,它可以為這個(gè)目的捕獲通用的pointcut表達(dá)。

package com.xyz.myapp;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class CommonPointcuts {
/**
* A join point is in the web layer if the method is defined
* in a type in the com.xyz.myapp.web package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.myapp.web..*)")
public void inWebLayer() {}
/**
* A join point is in the service layer if the method is defined
* in a type in the com.xyz.myapp.service package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.myapp.service..*)")
public void inServiceLayer() {}
/**
* A join point is in the data access layer if the method is defined
* in a type in the com.xyz.myapp.dao package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.myapp.dao..*)")
public void inDataAccessLayer() {}
/**
* A business service is the execution of any method defined on a service
* interface. This definition assumes that interfaces are placed in the
* "service" package, and that implementation types are in sub-packages.
*
* If you group service interfaces by functional area (for example,
* in packages com.xyz.myapp.abc.service and com.xyz.myapp.def.service) then
* the pointcut expression "execution(* com.xyz.myapp..service.*.*(..))"
* could be used instead.
*
* Alternatively, you can write the expression using the 'bean'
* PCD, like so "bean(*Service)". (This assumes that you have
* named your Spring service beans in a consistent fashion.)
*/
@Pointcut("execution(* com.xyz.myapp..service.*.*(..))")
public void businessService() {}
/**
* A data access operation is the execution of any method defined on a
* dao interface. This definition assumes that interfaces are placed in the
* "dao" package, and that implementation types are in sub-packages.
*/
@Pointcut("execution(* com.xyz.myapp.dao.*.*(..))")
public void dataAccessOperation() {}
}

Advice聲明

Advice與一個(gè)切點(diǎn)表達(dá)式相關(guān)聯(lián),在切點(diǎn)匹配的方法執(zhí)行之前、之后或around 運(yùn)行。該切點(diǎn)表達(dá)式可以是對(duì)一個(gè)命名的切點(diǎn)的簡(jiǎn)單引用,也可以是一個(gè)就地聲明的切點(diǎn)表達(dá)式。

Before Advice

你可以通過(guò)使用@Before注解在一個(gè)切面聲明before advice:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
@Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
}

如果我們使用pointcut表達(dá)式,我們可以把前面的例子改寫(xiě)成下面的例子:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
@Before("execution(* com.xyz.myapp.dao.*.*(..))")
public void doAccessCheck() {
// ...
}

After Returning Advice

當(dāng)一個(gè)匹配的方法執(zhí)行正常返回時(shí),返回后的Advice會(huì)運(yùn)行。你可以通過(guò)使用@AfterReturning注解來(lái)聲明它:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
}

有時(shí),你需要訪問(wèn)Advice中返回的值。你可以使用綁定返回值的@AfterReturning的形式來(lái)獲得這種訪問(wèn)權(quán),如下例所示:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning(
pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
returning="retVal")
public void doAccessCheck(Object retVal) {
// ...
}
}

After Throwing Advice

當(dāng)一個(gè)匹配的方法的執(zhí)行因拋出異常而退出時(shí),After Throwing Advice會(huì)運(yùn)行。你可以通過(guò)使用@AfterThrowing注解來(lái)聲明它,如下例所示:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
public void doRecoveryActions() {
// ...
}
}

通常情況下,你希望Advice只在給定類(lèi)型的異常被拋出時(shí)運(yùn)行,而且你也經(jīng)常需要在Advice中訪問(wèn)被拋出的異常。你可以使用throwing屬性來(lái)限制匹配(如果需要的話--否則使用Throwable作為異常類(lèi)型),并將拋出的異常綁定到advice參數(shù)上。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing(
pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
throwing="ex")
public void doRecoveryActions(DataAccessException ex) {
// ...
}
}

After (Finally) Advice

當(dāng)一個(gè)匹配的方法執(zhí)行退出時(shí),After (Finally) Advice會(huì)運(yùn)行。它是通過(guò)使用@After注解來(lái)聲明的。After通知必須準(zhǔn)備好處理正常和異常的返回條件。它通常用于釋放資源和類(lèi)似的目的。下面的例子展示了如何使用after finally通知:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;
@Aspect
public class AfterFinallyExample {
@After("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
public void doReleaseLock() {
// ...
}
}

Around Advice

最后一種advice是Around Advice。Around Advice是一個(gè)與匹配的方法的執(zhí)行而運(yùn)行。它有機(jī)會(huì)在方法運(yùn)行之前和之后進(jìn)行工作,并決定何時(shí)、如何、甚至是否真正運(yùn)行該方法。如果你需要以線程安全的方式分享方法執(zhí)行前后的狀態(tài),例如啟動(dòng)和停止一個(gè)定時(shí)器,那么Around Advice經(jīng)常被使用。

Around Advice是通過(guò)用@Around注解來(lái)聲明一個(gè)方法的。該方法應(yīng)該聲明Object為其返回類(lèi)型,并且該方法的第一個(gè)參數(shù)必須是ProceedingJoinPoint類(lèi)型。在Advice中,你必須在ProceedingJoinPoint上調(diào)用proceed(),以使底層方法運(yùn)行。在沒(méi)有參數(shù)的情況下調(diào)用proceed()將導(dǎo)致調(diào)用者的原始參數(shù)在底層方法被調(diào)用時(shí)被提供給它。對(duì)于高級(jí)用例,有一個(gè)重載的 proceed() 方法,它接受一個(gè)參數(shù)數(shù)組(Object[] )。當(dāng)?shù)讓臃椒ū徽{(diào)用時(shí),數(shù)組中的值將被用作該方法的參數(shù)。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
public class AroundExample {
@Around("com.xyz.myapp.CommonPointcuts.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
}

Advice Parameters

Spring提供了完全類(lèi)型化的Advice,這意味著你可以在Advice簽名中聲明你需要的參數(shù)(就像我們?cè)谇懊婵吹降姆祷睾蛼伋龅睦幽菢樱?,而不是一直使用Object[]數(shù)組。我們將在本節(jié)后面看到如何使參數(shù)和其他上下文值對(duì)Advice主體可用。首先,我們看一下如何編寫(xiě)通用Advice,它可以找出advice當(dāng)前所通知的方法。

訪問(wèn)當(dāng)前連接點(diǎn)

任何Advice方法都可以聲明一個(gè)org.aspectj.lang.JoinPoint類(lèi)型的參數(shù),作為它的第一個(gè)參數(shù)。請(qǐng)注意,aroud advice需要聲明一個(gè)ProceedingJoinPoint類(lèi)型的第一個(gè)參數(shù),它是JoinPoint的一個(gè)子類(lèi)。

JoinPoint接口提供了許多有用的方法:

  • getArgs():返回方法的參數(shù)。
  • getThis():返回代理對(duì)象。
  • getTarget():返回目標(biāo)對(duì)象。
  • getSignature():返回正在被通知方法的描述。
  • toString():打印被通知的方法的有用描述。

向advice傳遞參數(shù)我們已經(jīng)看到了如何綁定返回值或異常值(使用返回后和拋出后建議)。為了使參數(shù)值對(duì)advice主體可用,你可以使用args的綁定形式。如果你在args表達(dá)式中使用參數(shù)名來(lái)代替類(lèi)型名,那么當(dāng)advice被調(diào)用時(shí),相應(yīng)參數(shù)的值將作為參數(shù)值被傳遞。一個(gè)例子可以讓我們更清楚地了解這一點(diǎn)。假設(shè)你想advice執(zhí)行將一個(gè)帳戶對(duì)象作為第一個(gè)參數(shù)的DAO操作,并且你需要在advice中訪問(wèn)該帳戶。你可以寫(xiě)如下內(nèi)容:

@Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation() && args(account,..)")
public void validateAccount(Account account) {
// ...
}

pointcut表達(dá)式args(account,..)部分有兩個(gè)目的。首先,它將匹配限制在方法的執(zhí)行上,即方法至少需要一個(gè)參數(shù),并且傳遞給該參數(shù)的參數(shù)是一個(gè)賬戶的實(shí)例。其次,它使實(shí)際的賬戶對(duì)象通過(guò)賬戶參數(shù)對(duì)advice可用。

另一種寫(xiě)法是聲明一個(gè)pointcut,當(dāng)它與一個(gè)連接點(diǎn)匹配時(shí) "提供 "賬戶對(duì)象的值,然后從advice中引用這個(gè)命名的pointcut:

@Pointcut("com.xyz.myapp.CommonPointcuts.dataAccessOperation() && args(account,..)")
private void accountDataAccessOperation(Account account) {}
@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
// ...
}

Advice參數(shù)和泛型 Spring AOP可以處理類(lèi)聲明和方法參數(shù)中使用的泛型。假設(shè)你有一個(gè)像下面這樣的泛型:

public interface Sample {
void sampleGenericMethod(T param);
void sampleGenericCollectionMethod(Collection param);
}

你可以將方法類(lèi)型的攔截限制在某些參數(shù)類(lèi)型上,辦法是將advice參數(shù)與你想攔截方法的參數(shù)類(lèi)型聯(lián)系起來(lái):

@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
// Advice implementation
}

指定參數(shù)名稱(chēng)Advice調(diào)用中的參數(shù)綁定依賴于將pointcut表達(dá)式中使用的名稱(chēng)與advice和pointcut方法簽名中聲明的參數(shù)名稱(chēng)相匹配。參數(shù)名稱(chēng)無(wú)法通過(guò)Java反射獲得,因此Spring AOP使用以下策略來(lái)確定參數(shù)名稱(chēng)。

如果用戶已經(jīng)明確指定了參數(shù)名,則使用指定的參數(shù)名。Advice和pointcut注解都有一個(gè)可選的argNames屬性,你可以用它來(lái)指定被注解方法的參數(shù)名。這些參數(shù)名在運(yùn)行時(shí)是可用的。下面的例子展示了如何使用argNames屬性:

@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
argNames="bean,auditable")
public void audit(Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// ... use code and bean
}

如果第一個(gè)參數(shù)是JoinPoint、ProceedingJoinPoint或JoinPoint.StaticPart類(lèi)型,你可以從argNames屬性的值中省略參數(shù)的名稱(chēng)。例如,如果你修改前面的advice以接收連接點(diǎn)對(duì)象,argNames屬性不需要包括它:

@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
argNames="bean,auditable")
public void audit(JoinPoint jp, Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// ... use code, bean, and jp
}

帶參數(shù)的進(jìn)程 我們?cè)谇懊嬲f(shuō)過(guò),我們將描述如何編寫(xiě)一個(gè)在Spring AOP和AspectJ中一致運(yùn)行的帶參數(shù)的繼續(xù)調(diào)用。解決方案是確保advice簽名按順序綁定每個(gè)方法參數(shù)。下面的例子展示了如何做到這一點(diǎn):

@Around("execution(List find*(..)) && " +
"com.xyz.myapp.CommonPointcuts.inDataAccessLayer() && " +
"args(accountHolderNamePattern)")
public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
String accountHolderNamePattern) throws Throwable {
String newPattern = preProcess(accountHolderNamePattern);
return pjp.proceed(new Object[] {newPattern});
}

Advice排序

當(dāng)多個(gè)advice都想在同一個(gè)連接點(diǎn)運(yùn)行時(shí),會(huì)發(fā)生什么?Spring AOP遵循與AspectJ相同的優(yōu)先級(jí)規(guī)則來(lái)決定advice的執(zhí)行順序。優(yōu)先級(jí)最高的advice在 "進(jìn)入 "時(shí)首先運(yùn)行。從一個(gè)連接點(diǎn) "出來(lái) "時(shí),優(yōu)先級(jí)最高的advice最后運(yùn)行。

Introductions

Introductions(在AspectJ中被稱(chēng)為類(lèi)型間聲明)使一個(gè)切面能夠聲明advice對(duì)象實(shí)現(xiàn)一個(gè)給定的接口,并代表這些對(duì)象提供該接口的實(shí)現(xiàn)。

你可以通過(guò)使用@DeclareParents注解來(lái)做一個(gè)Introductions。這個(gè)注解被用來(lái)聲明匹配的類(lèi)型有一個(gè)新的父類(lèi)(因此得名)。例如,給定一個(gè)名為UsageTracked的接口和一個(gè)名為DefaultUsageTracked的接口的實(shí)現(xiàn),下面這個(gè)切面聲明所有服務(wù)接口的實(shí)現(xiàn)者也實(shí)現(xiàn)UsageTracked接口(例如,通過(guò)JMX進(jìn)行統(tǒng)計(jì)):

@Aspect
public class UsageTracking {
@DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class)
public static UsageTracked mixin;
@Before("com.xyz.myapp.CommonPointcuts.businessService() && this(usageTracked)")
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}
}

切面實(shí)例模型

默認(rèn)情況下,每個(gè)切面在應(yīng)用上下文中都有一個(gè)實(shí)例。AspectJ把這稱(chēng)為單例實(shí)例模型。我們可以用其他的生命周期來(lái)定義方面。Spring支持AspectJ的perthis和pertarget實(shí)例化模型;目前不支持percflow、percflowbelow和pertypewithin。你可以通過(guò)在@Aspect注解中指定一個(gè)perthis子句來(lái)聲明一個(gè)perthis方面。請(qǐng)看下面的例子:

@Aspect("perthis(com.xyz.myapp.CommonPointcuts.businessService())")
public class MyAspect {
private int someState;
@Before("com.xyz.myapp.CommonPointcuts.businessService()")
public void recordServiceUsage() {
// ...
}
}

AOP demo

業(yè)務(wù)服務(wù)的執(zhí)行有時(shí)會(huì)因?yàn)椴l(fā)性問(wèn)題而失?。ɡ?,死鎖失敗者)。如果該操作被重試,那么它很可能在下一次嘗試中成功。對(duì)于在這種情況下適合重試的業(yè)務(wù)服務(wù)(不需要回到用戶那里解決沖突的idempotent操作),我們希望透明地重試操作,以避免客戶端看到PessimisticLockingFailureException。這是一個(gè)明顯跨越服務(wù)層中多個(gè)服務(wù)的需求,因此,非常適合通過(guò)一個(gè)切面來(lái)實(shí)現(xiàn)。

因?yàn)槲覀兿胫卦嚥僮?,所以我們需要使用around advice,以便我們可以多次調(diào)用Proceed。下面的列表顯示了切面基本的實(shí)現(xiàn)。

@Aspect
public class ConcurrentOperationExecutor implements Ordered {
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
@Around("com.xyz.myapp.CommonPointcuts.businessService()")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
PessimisticLockingFailureException lockFailureException;
do {
numAttempts++;
try {
return pjp.proceed();
}
catch(PessimisticLockingFailureException ex) {
lockFailureException = ex;
}
} while(numAttempts <= this.maxRetries);
throw lockFailureException;
}
}

注意,這個(gè)方面實(shí)現(xiàn)了Ordered接口,這樣我們就可以把切面的優(yōu)先級(jí)設(shè)置得比事務(wù)advice高(我們希望每次重試都是一個(gè)新的事務(wù))。maxRetries和order屬性都是由Spring配置的。主要的動(dòng)作發(fā)生在around advice的doConcurrentOperation中。請(qǐng)注意,就目前而言,我們將重試邏輯應(yīng)用于每個(gè)businessService()。我們嘗試進(jìn)行,如果失敗了,出現(xiàn)PessimisticLockingFailureException,我們就再試一次,除非我們已經(jīng)用盡了所有的重試嘗試。





為了細(xì)化這個(gè)切面,使它只重試冪等操作,我們可以定義下面的冪等注解:

@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
// marker annotation
}

然后我們可以使用注解來(lái)注解服務(wù)操作的實(shí)現(xiàn)。對(duì)只重試冪等操作切面的修改涉及到細(xì)化pointcut表達(dá)式,以便只有@Idempotent操作可以匹配,如下所示:

@Around("com.xyz.myapp.CommonPointcuts.businessService() && " +
"@annotation(com.xyz.myapp.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
// ...
}

當(dāng)前標(biāo)題:Spring框架之SpringAOP
本文網(wǎng)址:http://www.5511xx.com/article/cogeggi.html