●AOP(Aspect-Oriented Programming,面向切面編程):是一種新的方法論,是對傳統 OOP(Object-Oriented Programming,面向對象編程)的補充。java
●AOP編程操做的主要對象是切面(aspect),而切面模塊化橫切關注點。spring
●在應用AOP編程時,仍然須要定義公共功能,但能夠明確的定義這個功能應用在哪裏,以什麼方式應用,而且沒必要修改受影響的類。這樣一來橫切關注點就被模塊化到特殊的類裏——這樣的類咱們一般稱之爲「切面」。數據庫
●AOP的好處:express
○每一個事物邏輯位於一個位置,代碼不分散,便於維護和升級編程
○業務模塊更簡潔,只包含核心業務代碼框架
從每一個方法中抽取出來的同一類非核心業務。(抽離到方法中處理非核心業務)模塊化
封裝橫切關注點信息的類,每一個關注點體現爲一個通知方法。ui
切面必需要完成的各個具體工做spa
被通知的對象代理
向目標對象應用通知以後建立的代理對象
橫切關注點在程序代碼中的具體體現,對應程序執行的某個特定位置。例如:類某個方法調用前、調用後、方法捕獲到異常後等。
在應用程序中可使用橫縱兩個座標來定位一個具體的鏈接點:
定位鏈接點的方式。每一個類的方法中都包含多個鏈接點,因此鏈接點是類中客觀存在的事物。若是把鏈接點看做數據庫中的記錄,那麼切入點就是查詢條件——AOP能夠經過切入點定位到特定的鏈接點。
切點經過org.springframework.aop.Pointcut 接口進行描述,它使用類和方法做爲鏈接點的查詢條件。
AspectJ:Java社區裏最完整最流行的AOP框架。
在Spring2.0以上版本中,可使用基於AspectJ註解或基於XML配置的AOP。
●aopalliance.jar
●aspectj.weaver.jar
●spring-aspects.jar
<aop:aspectj-autoproxy>
當Spring IOC容器偵測到bean配置文件中的<aop:aspectj-autoproxy>元素時,會自動爲與AspectJ切面匹配的bean建立代理
①要在Spring中聲明AspectJ切面,只須要在IOC容器中將切面聲明爲bean實例。②當在Spring IOC容器中初始化AspectJ切面以後,Spring IOC容器就會爲那些與 AspectJ切面相匹配的bean建立代理。
③在AspectJ註解中,切面只是一個帶有@Aspect註解的Java類,它每每要包含不少通知。
④通知是標註有某種註解的簡單的Java方法。
⑤AspectJ支持5種類型的通知註解:
[1]@Before:前置通知,在方法執行以前執行
[2]@After:後置通知,在方法執行以後執行
[3]@AfterRunning:返回通知,在方法返回結果以後執行
[4]@AfterThrowing:異常通知,在方法拋出異常以後執行
[5]@Around:環繞通知,圍繞着方法執行
經過表達式的方式定位一個或多個具體的鏈接點。
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類型的方法 |
表達式 |
execution (* *.add(int,..)) || execution(* *.sub(int,..)) |
含義 |
任意類中第一個參數爲int類型的add方法或sub方法 |
切入點表達式一般都會是從宏觀上定位一組方法,和具體某個通知的註解結合起來就可以肯定對應的鏈接點。那麼就一個具體的鏈接點而言,咱們可能會關心這個鏈接點的一些具體信息,例如:當前鏈接點所在方法的方法名、當前傳入的參數值等等。這些信息都封裝在JoinPoint接口的實例對象中。
l 在具體的鏈接點上要執行的操做。
l 一個切面能夠包括一個或者多個通知。
l 通知所使用的註解的值每每是切入點表達式。
l 前置通知:在方法執行以前執行的通知
l 使用@Before註解
l 後置通知:後置通知是在鏈接點完成以後執行的,即鏈接點返回結果或者拋出異常的時候
l 使用@After註解
l 返回通知:不管鏈接點是正常返回仍是拋出異常,後置通知都會執行。若是隻想在鏈接點返回的時候記錄日誌,應使用返回通知代替後置通知。
l 使用@AfterReturning註解
l 在返回通知中訪問鏈接點的返回值
l 異常通知:只在鏈接點拋出異常時才執行異常通知
l 將throwing屬性添加到@AfterThrowing註解中,也能夠訪問鏈接點拋出的異常。Throwable是全部錯誤和異常類的頂級父類,因此在異常通知方法能夠捕獲到任何錯誤和異常。
l 若是隻對某種特殊的異常類型感興趣,能夠將參數聲明爲其餘異常的參數類型。而後通知就只在拋出這個類型及其子類的異常時才被執行
l 環繞通知是全部通知類型中功能最爲強大的,可以全面地控制鏈接點,甚至能夠控制是否執行鏈接點。
l 對於環繞通知來講,鏈接點的參數類型必須是ProceedingJoinPoint。它是 JoinPoint的子接口,容許控制什麼時候執行,是否執行鏈接點。
l 在環繞通知中須要明確調用ProceedingJoinPoint的proceed()方法來執行被代理的方法。若是忘記這樣作就會致使通知被執行了,但目標方法沒有被執行。
l 注意:環繞通知的方法須要返回目標方法執行以後的結果,即調用 joinPoint.proceed();的返回值,不然會出現空指針異常。
l 在編寫AspectJ切面時,能夠直接在通知註解中書寫切入點表達式。但同一個切點表達式可能會在多個通知中重複出現。
l 在AspectJ切面中,能夠經過@Pointcut註解將一個切入點聲明成簡單的方法。切入點的方法體一般是空的,由於將切入點定義與應用程序邏輯混在一塊兒是不合理的。
l 切入點方法的訪問控制符同時也控制着這個切入點的可見性。若是切入點要在多個切面中共用,最好將它們集中在一個公共的類中。在這種狀況下,它們必須被聲明爲public。在引入這個切入點時,必須將類名也包括在內。若是類沒有與這個切面放在同一個包中,還必須包含包名。
l 其餘通知能夠經過方法名稱引入該切入點
l 在同一個鏈接點上應用不止一個切面時,除非明確指定,不然它們的優先級是不肯定的。
l 切面的優先級能夠經過實現Ordered接口或利用@Order註解指定。
l 實現Ordered接口,getOrder()方法的返回值越小,優先級越高。
l 若使用@Order註解,序號出如今註解中
上面的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是有很大關係的,即聲明式事務的底層是用事務實現的!
附:AOP經過註解實現類的代碼:
1 //@Component註解將該類添加到IOC容器中, 2 @Component 3 //@Aspect註解是切面類的標誌 4 @Aspect 5 //@Order註解標註優先級 6 @Order(value=30) 7 public class AOPObject { 8 //環繞通知 9 @Around(value = "execution(public * com.neuedu.Bean.MathClass.*(..))") 10 public Object show(ProceedingJoinPoint joinPoint){ 11 //同前置通知 12 Signature signature = joinPoint.getSignature(); 13 Object[] args = joinPoint.getArgs(); 14 List<Object> asList = Arrays.asList(args); 15 String name = signature.getName(); 16 17 Object result=null; 18 try{ 19 try{ 20 System.out.println(name+"方法開始前,"+asList); 21 result = joinPoint.proceed(args); 22 }finally{ 23 //同後置通知 24 System.out.println("方法正常結束"); 25 } 26 //同返回通知 27 System.out.println("方法最終結束.result爲:"+result.toString()); 28 }catch(Throwable ex){ 29 //同異常通知 30 System.out.println("方法異常"+ex.getMessage()); 31 } 32 33 return result; 34 } 35 //重用切面方法 36 /* @Pointcut(value="execution(public * com.neuedu.Bean.MathClass.*(..))") 37 public void show(){ 38 39 } 40 //前置通知 41 @Before(value= "show()") 42 public void Begin(JoinPoint joinPoint){ 43 Object[] args = joinPoint.getArgs(); 44 int[] a=new int[args.length]; 45 for(int i=0;i<args.length;i++){ 46 a[i]=Integer.parseInt(args[i].toString()); 47 System.out.println("參數"+i+"爲:"+a[i]); 48 } 49 Signature signature = joinPoint.getSignature(); 50 String name = signature.getName(); 51 System.out.println(name+"方法開始前,"); 52 } 53 //後置通知 54 @After(value= "show()") 55 public void After(){ 56 System.out.println("方法正常結束"); 57 } 58 //異常通知 59 @AfterThrowing(value= "show()") 60 public void AfterThrowing(){ 61 System.out.println("方法異常"); 62 } 63 //返回通知 64 @AfterReturning(value= "show()",returning="result") 65 public void AfterReturning(JoinPoint joinPoint, Object result){ 66 System.out.println("方法最終結束.result爲:"+result); 67 }*/ 68 } 69