1.AOP概念:Aspect Oriented Programming 面向切面編程spring
2.做用:本質上來講是一種簡化代碼的方式數據庫
繼承機制express
封裝方法編程
動態代理ide
……測試
3.情景舉例ui
①數學計算器接口[MathCalculator]spa
int add(int i,int j);.net
int sub(int i,int j);代理
int mul(int i, int j);
int div(int i,int j);
②提供簡單實現[EasyImpl]
③在簡單實現的基礎上讓每個計算方法都可以打印日誌[LoginImpl]
④缺陷
[1]手動添加日誌繁瑣,重複
[2]統一修改不便
[3]對目標方法原本要實現的核心功能有干擾,使程序代碼很臃腫,不易於開發維護
⑤使用動態代理實現
[1]建立一個類,讓這個類可以提供一個目標對象的代理對象
[2]在代理對象中打印日誌
4.AOP術語!
AOP概述
●AOP(Aspect-Oriented Programming,面向切面編程):是一種新的方法論,是對傳統 OOP(Object-Oriented Programming,面向對象編程)的補充。
●Spring的AOP既可使用xml配置的方式實現,也可使用註解的方式來實現!
4.1 橫切關注點
從每一個方法中抽取出來的同一類非核心業務。(抽離到方法中處理非核心業務)
4.2 切面(Aspect)
封裝橫切關注點信息的類,每一個關注點體現爲一個通知方法。
4.3 通知(Advice)
切面必需要完成的各個具體工做
4.4 目標(Target)
被通知的對象
4.5 代理(Proxy)
向目標對象應用通知以後建立的代理對象
4.6 鏈接點(Joinpoint)
橫切關注點在程序代碼中的具體體現,對應程序執行的某個特定位置。例如:類某個方法調用前、調用後、方法捕獲到異常後等。
在應用程序中可使用橫縱兩個座標來定位一個具體的鏈接點:
4.7 切入點(pointcut):
定位鏈接點的方式。每一個類的方法中都包含多個鏈接點,因此鏈接點是類中客觀存在的事物。
若是把鏈接點看做數據庫中的記錄,那麼切入點就是查詢條件——AOP能夠經過切入點定位到特定的鏈接點。
切點經過org.springframework.aop.Pointcut 接口進行描述,它使用類和方法做爲鏈接點的查詢條件。
5.在Spring中使用AOP實現日誌功能
①Spring中可使用註解或XML文件配置的方式實現AOP。
②導入jar包
com.springsource.net.sf.cglib -2.2.0.jar
com.springsource.org.aopalliance-1.0.0 .jar
com.springsource.org.aspectj.weaver-1.6.8 .RELEASE.jar
commons-logging-1.1.3. jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE. jar
③開啓基於註解的AOP功能
< aop:aspectj-autoproxy />
④聲明一個切面類,並把這個切面類加入到IOC容器中
@Aspect//表示這是一個切面類
@Component//加入IOC容器
public class LogAspect {}
⑤在切面類中聲明通知方法
[1]前置通知:@Before
[2]返回通知:@AfterReturning
[3]異常通知:@AfterThrowing
[4]後置通知:@After
[5]環繞通知:@Around :環繞通知是前面四個通知的集合體!
1 @Aspect//表示這是一個切面類
2 @Component//將本類對象加入到IOC容器中!
3 public class LogAspect { 4 @Before(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))") 5 public void showBeginLog(){ 6 System.out.println("AOP日誌開始"); 7 } 8 @After(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))") 9 public void showReturnLog(){ 10 System.out.println("AOP方法返回"); 11 } 12 @AfterThrowing(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))") 13 public void showExceptionLog(){ 14 System.out.println("AOP方法異常"); 15 } 16 @AfterReturning(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))") 17 public void showAfterLog(){ 18 System.out.println("AOP方法結束"); 19 } 20 }
⑥被代理的對象也須要加入IOC容器
@Component//加入IOC容器
1 public class MathCalculatorImpl { 2 public int add(int i,int j){ 3 int result = i+j; 4 return result; 5 } 6 public int sub(int i,int j){ 7 int result = i-j; 8 return result; 9 } 10 public int multi(int i,int j){ 11 int result = i*j; 12 return result; 13 } 14 public int divide(int i,int j){ 15 int result = i/j; 16 return result; 17 } 18 }
1.上述案例經過junit測試,會發現,咱們調用目標類的四個方法只有add方法被加入了4個通知,
若是想全部的方法都加上這些通知,能夠在切入點表達式處,將execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int)) 換成:
execution(public int com.neuedu.aop.target.MathCalculatorImpl.*(int, int))這樣只要是有兩個參數,且參數類型爲int的方法在執行的時候都會執行其相應的通知方法!
2.①切入點表達式的語法格式
execution([權限修飾符] [返回值類型] [簡單類名/全類名] [方法名]([參數列表]))
2.1 做用
經過表達式的方式定位一個或多個具體的鏈接點。
2.2 語法細節
①切入點表達式的語法格式
execution([權限修飾符] [返回值類型] [簡單類名/全類名] [方法名]([參數列表])) |
②舉例說明
表達式 |
execution(* com.atguigu.spring.ArithmeticCalculator.*(..)) |
含義 |
ArithmeticCalculator接口中聲明的全部方法。 第一個「*」表明任意修飾符及任意返回值。 第二個「*」表明任意方法。 「..」匹配任意數量、任意類型的參數。 若目標類、接口與該切面類在同一個包中能夠省略包名。 |
表達式 |
execution(public * ArithmeticCalculator.*(..)) |
含義 |
ArithmeticCalculator接口的全部公有方法 |
表達式 |
execution(public double ArithmeticCalculator.*(..)) |
含義 |
ArithmeticCalculator接口中返回double類型數值的方法 |
表達式 |
execution(public double ArithmeticCalculator.*(double, ..)) |
含義 |
第一個參數爲double類型的方法。 「..」 匹配任意數量、任意類型的參數。 |
表達式 |
execution(public double ArithmeticCalculator.*(double, double)) |
含義 |
參數類型爲double,double類型的方法 |
③在AspectJ中,切入點表達式能夠經過 「&&」、「||」、「!」等操做符結合起來。
表達式 |
execution (* *.add(int,..)) || execution(* *.sub(int,..)) |
含義 |
任意類中第一個參數爲int類型的add方法或sub方法 |
execution(* *.*(..))
1.任意參數,任意類型
2.任意返回值
3.用@PointCut註解統一聲明,而後在其它通知中引用該統一聲明便可!
須要注意的是:權限是不支持寫通配符的,固然你能夠寫一個*表示全部權限全部返回值!
4.最詳細的切入點表達式:
execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))
5.最模糊的切入點表達式:
execution (* *.*(..))
6..統一聲明切入點表達式
@Pointcut(value= "execution(public int com.atguigu.aop.target.EazyImpl.add(int,int))")
public void myPointCut(){}
三、通知方法的細節
①在通知中獲取目標方法的方法名和參數列表
[1]在通知方法中聲明一個JoinPoint類型的形參
[2]調用JoinPoint對象的getSignature()方法獲取目標方法的簽名
[3]調用JoinPoint對象的getArgs()方法獲取目標方法的實際參數列表
②在返回通知中獲取方法的返回值
[1]在@AfterReturning註解中添加returning屬性
@AfterReturning (value="myPointCut()", returning= "result")
[2]在返回通知的通知方法中聲明一個形參,形參名和returning屬性的值一致
showReturnLog(JoinPoint joinPoint, Object result)
③在異常通知中獲取異常對象
[1]在@ AfterThrowing註解中添加throwing屬性
@AfterThrowing (value="myPointCut()",throwing= "throwable" )
[2]在異常通知的通知方法中聲明一個形參,形參名和throwing屬性值一致
showExceptinLog(JoinPoint joinPoint, Throwable throwable)
根據接口類型獲取target對象時,實際上真正放在IOC容器中的對象是代理對象,而並非目標對象自己!
四、.環繞通知:@Around
4.1.環繞通知須要在方法的參數中指定JoinPoint的子接口類型ProceedingJoinPoint爲參數
@Around(value="pointCut()")
public void around(ProceedingJoinPoint joinPoint){
}
4.2.環繞通知會將其餘4個通知能幹的,本身都給幹了!
注意:@Around修飾的方法必定要將方法的返回值返回!自己至關於代理!
1 @Around(value="pointCut()") 2 public Object around(ProceedingJoinPoint joinPoint){ 3 Object[] args = joinPoint.getArgs(); 4 Signature signature = joinPoint.getSignature(); 5 String methodName = signature.getName(); 6 List<Object> list = Arrays.asList(args); 7 Object result = null; 8 try { 9 //目標方法以前要執行的操做
10 System.out.println("[環繞日誌]"+methodName+"開始了,參數爲:"+list); 11 //調用目標方法
12 result = joinPoint.proceed(args); 13 //目標方法正常執行以後的操做
14 System.out.println("[環繞日誌]"+methodName+"返回了,返回值爲:"+result); 15 } catch (Throwable e) { 16
17 //目標方法拋出異常信息以後的操做
18 System.out.println("[環繞日誌]"+methodName+"出異常了,異常對象爲:"+e); 19 throw new RuntimeException(e.getMessage()); 20 }finally{ 21 //方法最終結束時執行的操做!
22 System.out.println("[環繞日誌]"+methodName+"結束了!"); 23 } 24 return result; 25 }
對於同一個代理對象,可j以同時有多個切面共同對它進行代理。
能夠在切面類上經過@Order (value=50)註解來進行設置,值越小優先級越高!
1 @Aspect 2 @Component 3 @Order(value=40) 4 public class TxAspect { 5
6 @Around(value="execution(public * com.neuedu.aop.target.MathCalculatorImpl.*(..))") 7 public Object around(ProceedingJoinPoint joinPoint){ 8 Object[] args = joinPoint.getArgs(); 9 Signature signature = joinPoint.getSignature(); 10 String methodName = signature.getName(); 11 List<Object> list = Arrays.asList(args); 12 Object result = null; 13 try { 14 //目標方法以前要執行的操做
15 System.out.println("[事務日誌]"+methodName+"開始了,參數爲:"+list); 16 //調用目標方法
17 result = joinPoint.proceed(args); 18
19 //目標方法正常執行以後的操做
20 System.out.println("[事務日誌]"+methodName+"返回了,返回值爲:"+result); 21 } catch (Throwable e) { 22 //目標方法拋出異常信息以後的操做
23 System.out.println("[事務日誌]"+methodName+"出異常了,異常對象爲:"+e); 24 throw new RuntimeException(e.getMessage()); 25 }finally{ 26 //方法最終結束時執行的操做!
27 System.out.println("[事務日誌]"+methodName+"結束了!"); 28 } 29 return result; 30 } 31
32 }
一、注意:上面的AOP都是經過註解實現的,AOP實際上也能夠經過xml配置的方式實現!
①<!-- 1.將須要加載到IOC容器中的bean配置好 -->
<bean id="logAspect" class="com.neuedu.aop.proxy.LogAspect"></bean>
<bean id="txAspect" class="com.neuedu.aop.target.TxAspect"></bean>
<bean id="calculator" class="com.neuedu.aop.target.MathCalculatorImpl"></bean>
②<!-- 2.配置AOP,須要導入AOP名稱空間 -->
<aop:config>
③<!-- 聲明切入點表達式 -->
<aop:pointcut expression="execution(* com.neuedu.aop.target.MathCalculatorImpl.*(..))" id="myPointCut"/>
④<!-- 配置日誌切面類,引用前面的類 ,經過order屬性控制優先級-->
<aop:aspect ref="logAspect" order="25">
⑤<!-- 經過method屬性指定切面類的切面方法,經過pointcut-ref指定切入點表達式 -->
<aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
<aop:after method="showAfterLog" pointcut-ref="myPointCut"/>
<aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="ex"/>
<aop:after-returning method="showReturnLog" pointcut-ref="myPointCut" returning="result"/>
<aop:around method="around" pointcut-ref="myPointCut"/>
</aop:aspect>
⑥<!-- 配置事務切面類,引用前面的類 -->
<aop:aspect ref="txAspect" order="20">
<aop:around method="around" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
須要知道的是:事務的管理是和AOP是有很大關係的,即聲明式事務的底層是用事務實現的!