SpringAOP學習java
author:luojie正則表達式
AOP的通用術語,並不是spring java所特有。很遺憾AOP的術語不是特別的直觀。但若是讓Spring java來定義本身的專用名詞,可能會更加教人糊塗。
方面(Aspect):對橫向分佈在多個對象中的關注點所作的模塊化。在企業應用中,事務管理就是一個典型的橫切關注點。Spring java將方面實現爲Advisor或攔截器(interceptor)。(按:Advisor是通知和切入點的組合,攔截器實際就是指通知,注意在本文檔中,通常會把環繞通知稱爲攔截器,而將其它類型的通知稱爲通知,這是由於環繞通知實現的是AopAlliance.Intercept.IMethodInterceptor接口,而其它通知類型實現的都是Spring.Aop命名空間下的通知接口。)
鏈接點(Joinpoint):程序執行過程當中的一個點,例如對某個方法的調用或者某個特定異常的拋出均可以稱爲鏈接點。
通知(Advice):AOP框架在某個鏈接點所採起的行爲。通知有多種類型,包括「環繞」通知,「前置」通知和「異常」通知等,後文將對通知類型進行討論。包括Spring.NET在內的不少AOP框架都把通知建模爲攔截器(interceptor),而且會維護一個"包圍"在鏈接點周圍的攔截器鏈。
切入點(Pointcut):指通知的應用條件,用於肯定某個通知要被應用到哪些鏈接點上。AOP框架應容許讓開發人員指定切入點,例如,可使用正則表達式來指定一個切入點。
引入(Introduction):向目標對象添加方法或字段的行爲。Spring.NET容許爲任何目標對象引入新的接口。例如,能夠利用引入讓任何對象在運行期實現IAuditable接口,以簡化對象狀態變化的跟蹤過程。(按:也稱爲mixin,混入)
目標對象(Target object):指包含鏈接點的對象。也稱爲被通知或被代理對象。(按:「被通知對象」實際是「被應用了通知的對象」,在譯文中,將advised object或proxied object統稱爲目標對象,這樣更爲統一)
AOP代理(AOP proxy):由AOP框架在將通知應用於目標對象後建立的對象。在Spring.NET中,AOP代理是使用IL代碼在運行時建立的動態代理。
織入(Weaving):將方面進行組裝,以建立一個目標對象。織入能夠在編譯期完成(例如使用Gripper_Loom.NET編譯器),也能夠在運行時完成。Spring.NET在運行時執行織入。
各類通知類型包括:
環繞通知(Around Advise):包圍(按:即在鏈接點執行的前、後執行)某個鏈接點(如方法調用)的通知。這是功能最強大的一種通知。環繞通知容許在方法調用的先後執行自定義行爲。它能夠決定是讓鏈接點繼續執行,仍是用本身的返回值或異常來將鏈接點「短路」。
前置通知(Before Advise):在某個鏈接點執行以前執行,可是不具有阻止鏈接點繼續執行的能力(除非它拋出異常)。
異常通知(Throws Advise):當方法(鏈接點)拋出異常時執行。Spring.NET的異常通知是強類型的(按:Spring.NET用標識接口來定義異常通知,異常通知的處理方法僅需遵循必定的命名規則,能夠用具體的異常類型聲明其參數,參見12.3.2.3節),因此,能夠在代碼中直接捕捉某個類型的異常(及其子類異常),沒必要從Exception轉型。
後置通知(After returning Advise):在鏈接點正常執行完成後執行,例如,若是方法正常返回,沒有拋出異常時,後置通知就會被執行。
Spring java內置了以上全部類型的通知。在應用時,應儘可能使用功能最少(只要對要實現的行爲來講是足夠的)的通知類型,這樣可簡化編程模型並減小出錯的可能。例如,若是隻需使用某個方法的返回值來更新緩存,那麼用後置通知就比環繞通知合適。由於,儘管環繞通知能夠完成一樣的功能,但在後置通知中不用象環繞通知那樣必須調用IMethodInvocation接口的Proceed()方法來容許鏈接點繼續執行,因此鏈接點老是能正常執行。(按:換句話說,由於環繞通知能夠控制鏈接點的繼續執行,因此若是沒有調用IMethodInvocation接口的Proceed()方法,鏈接點就會被「短路」;而使用後置通知就不存在這個問題)。
切入點是AOP的關鍵概念,使AOP從根本上區別於舊的攔截技術。切入點使通知能夠獨立於OO的繼承層次以外。例如,一個聲明式事務處理的環繞通知能夠應用於不一樣對象的方法。切入點是AOP的結構性要素。spring
2. jar包express
Spring框架的包,和aop相關的jar。編程
3. 學習例子緩存
如在給controller作切面,記錄訪問controller時的日誌mvc
LogAspect.java框架
//@Component//掃描,等價<bean>模塊化
//@Aspect//等價於<aop:aspect ref="">學習
public class LogAspect {
private static Logger log = LoggerFactory.getLogger(getClass());
//@Around("within(org.tedu.cloudnot.controller..*)")
//等價於<aop:around method="logController" pointcut = "">
public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable{
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
String description = "";
Class targetClass = targetClass.getMethods();
Method[] methods = targetClass.getMethods();
for(Method method : methods){
if(method.getName().equals(methodName)){
Class[] clazzs = method.getParameterTypes();
for(Class<?> clas : clazzs){
String parameterName = clas.getName();
description = description = "" + parameterName;
}
}
}
log.info("enter into Controller---------------------------------");
log.info("targetName:" + targetName + ";");
log.info("methodName:" + methodName + ";");
log.info("parameterTypes:" + description + ";");
Object[] args = joinPoint.getArgs();
log.info("parameterValues" + Arrays.toString(args));
Object object = null;
try{
object = joinPoint.proceed();
}catch(Exception ex){
log.error("=============================controller方法執行異常==============");
BaseResult rr = new BaseResult();
rr.setErrorCode();
rr.setErrorMsg);
return rr;
}
Object[] objs = joinPoint.getArgs();
log.info("returnResult:" + object);
return object;
}
public void afterThrowingController(JoinPoint joinPoint, Throwable e){
String params = "";
if(joinPoint.getArgs() != null && joinPoint.getArgs().length > 0){
for(int i = 0; i<joinPoint.getArgs().length; i++){
params += joinPoint.getArgs()[i] + ";";
}
}
try{
System.out.println("------異常通知開始-------------");
System.out.println("異常代碼");
System.out.println("異常信息");
System.out.println("異常方法" + (joinPoint.getTarget().getClass.getName + "."
+ joinPoint.getSignature().getName()+"()"));
System.out.println("請求參數"+ params);
}catch(Exception ex){
log.error("==異常通知異常=====");
log.error("異常信息:{}", ex.getMessage());
}
/*===========記錄本地異常日誌============*/
log.error("異常方法:{}異常代碼:{}異常參數:{}參數{}", new Object[]{1,2,3,4});
}
4. spring.xml配置
<aop:config>
<aop:pointcut expression="(within(com.me..*)or
within(coom.me..*))"
id="controllerPointcut"/>
<aop:aspect id = "logAspect" ref="loggerBean">
<aop:arount method="arountController" pointcut-ref="contrllerPointcut"/>
<aop:after-throwing method="afterThrowingController" pointcut-ref="controllerPointcut" throwing="e"/>
</aop:aspect>
</aop:config>
*須要添加aop的schema配置
*可能須要添加aop的自定義掃描註解(這裏不須要,已測試,使用註解則須要)
*可能若是aop的切面程序沒法起做用,注意多是配置要寫在spring-servlet.xml(即springmvc的配置文件中)
<context:component-scan base-package="com.spring.aop" />
<aop:aspectj-autoproxy />
<!-- 通知spring使用cglib而不是jdk的來生成代理方法 AOP能夠攔截到Controller -->
<aop:aspectj-autoproxy proxy-target-class="true" />
4. SpringAOP的表達式
任意公共方法的執行: execution(public * *(..)) 任何一個以「set」開始的方法的執行: execution(* set*(..)) AccountService 接口的任意方法的執行: execution(* com.xyz.service.AccountService.*(..)) 定義在service包裏的任意方法的執行: execution(* com.xyz.service.*.*(..)) 定義在service包或者子包裏的任意方法的執行: execution(* com.xyz.service..*.*(..)) 在service包裏的任意鏈接點(在Spring AOP中只是方法執行) : within(com.xyz.service.*) 在service包或者子包裏的任意鏈接點(在Spring AOP中只是方法執行) : within(com.xyz.service..*) 實現了 AccountService 接口的代理對象的任意鏈接點(在Spring AOP中只是方法執行) : this(com.xyz.service.AccountService) 'this'在binding form中用的更多:- 請常見如下討論通知的章節中關於如何使得代理對象能夠在通知體內訪問到的部分。 實現了 AccountService 接口的目標對象的任意鏈接點(在Spring AOP中只是方法執行) : target(com.xyz.service.AccountService) 'target'在binding form中用的更多:- 請常見如下討論通知的章節中關於如何使得目標對象能夠在通知體內訪問到的部分。 任何一個只接受一個參數,且在運行時傳入的參數實現了 Serializable 接口的鏈接點 (在Spring AOP中只是方法執行) args(java.io.Serializable) 'args'在binding form中用的更多:- 請常見如下討論通知的章節中關於如何使得方法參數能夠在通知體內訪問到的部分。 請注意在例子中給出的切入點不一樣於 execution(* *(java.io.Serializable)): args只有在動態運行時候傳入參數是可序列化的(Serializable)才匹配,而execution 在傳入參數的簽名聲明的類型實現了 Serializable 接口時候匹配。 有一個 @Transactional 註解的目標對象中的任意鏈接點(在Spring AOP中只是方法執行) @target(org.springframework.transaction.annotation.Transactional) '@target' 也能夠在binding form中使用:請常見如下討論通知的章節中關於如何使得annotation對象能夠在通知體內訪問到的部分。 任何一個目標對象聲明的類型有一個 @Transactional 註解的鏈接點(在Spring AOP中只是方法執行) @within(org.springframework.transaction.annotation.Transactional) '@within'也能夠在binding form中使用:- 請常見如下討論通知的章節中關於如何使得annotation對象能夠在通知體內訪問到的部分。 任何一個執行的方法有一個 @Transactional annotation的鏈接點(在Spring AOP中只是方法執行) @annotation(org.springframework.transaction.annotation.Transactional) '@annotation' 也能夠在binding form中使用:- 請常見如下討論通知的章節中關於如何使得annotation對象能夠在通知體內訪問到的部分。 任何一個接受一個參數,而且傳入的參數在運行時的類型實現了 @Classified annotation的鏈接點(在Spring AOP中只是方法執行) @args(com.xyz.security.Classified)