注:AOP稱為面向切面編程,在程序開發(fā)中常用來解決一些系統(tǒng)公共的問題筒繁,比如日志噩凹,事務(wù),權(quán)限攔截等等毡咏,本文主要講述Spring AOP的使用驮宴。
基本概念
1、Aspect(切面):通常是一個(gè)類呕缭,里面可以定義切入點(diǎn)和通知堵泽,比如Log類修己。
2、JointPoint(連接點(diǎn)):程序執(zhí)行過程中明確的點(diǎn)迎罗,即在哪個(gè)地方進(jìn)行攔截箩退。
3、Advice(通知):AOP在特定的切入點(diǎn)上執(zhí)行的增強(qiáng)處理佳谦,主要包括以下幾種方式:before,after,afterReturning,afterThrowing,around
(1)前置通知(Before):在目標(biāo)方法前調(diào)用戴涝。
(2)后置通知(After):在目標(biāo)方法后調(diào)用,無論是否有異常都執(zhí)行钻蔑。
(3)環(huán)繞通知(Around):將 Before 和 After 啥刻,甚至拋出增強(qiáng)和返回增強(qiáng)合到一起,即動(dòng)態(tài)代理功能一樣咪笑。
(4)返回通知(AfterReturning):在方法返回結(jié)果后執(zhí)行可帽,該增強(qiáng)可以接收到目標(biāo)方法返回結(jié)果,前提沒有異常的情況下窗怒。
(5)異常通知(AfterThrowing):在目標(biāo)方法拋出對應(yīng)的類型后執(zhí)行映跟,可以接收到對應(yīng)的異常信息,前提拋出異常的情況下扬虚。
4努隙、Pointcut(切入點(diǎn)):就是帶有通知的連接點(diǎn),在程序中主要體現(xiàn)為書寫切入點(diǎn)表達(dá)式
5辜昵、AOP代理:AOP框架創(chuàng)建的對象荸镊,代理就是目標(biāo)對象的加強(qiáng)。
具體實(shí)例
該實(shí)例功能堪置,在做除法運(yùn)算時(shí)躬存,將其日志打印輸出,具體代碼如下:
Calc.java 業(yè)務(wù)類
package com.itbofeng;
import org.springframework.stereotype.Service;
//核心業(yè)務(wù)邏輯類舀锨,保證業(yè)務(wù)邏輯類的功能單一岭洲,使用Spring Aop降低代碼的侵入性
@Service
public class Calc {
//進(jìn)行觸發(fā)運(yùn)算功能
public int div(int a, int b) {
return a/b;
}
}
Log.java 切面類:日志記錄
package com.itbofeng;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
//切面類:日志記錄功能
@Aspect
@Component
public class Log {
//切點(diǎn)
@Pointcut("execution(public * com.itbofeng.*.*(..))")
public void logPointCut(){}
//前置通知
@Before("logPointCut()")
public void calcBefore(JoinPoint joinPoint){
String name=joinPoint.getSignature().getName();
Object [] args=joinPoint.getArgs();
System.out.println("Log.calcBefore exec method "+name+",the args are"+ Arrays.asList(args));
}
//返回通知
@AfterReturning(returning = "ret",pointcut = "logPointCut()")
public void calcAfterReturning(JoinPoint joinPoint,Object ret){
String name=joinPoint.getSignature().getName();
Object [] args=joinPoint.getArgs();
System.out.println("Log.AfterReturning exec method "+name+",the args are"+ Arrays.asList(args)+",the result is "+ret);
}
//異常通知
@AfterThrowing(pointcut="logPointCut()",throwing="e")
public void calcAfterThrowing(JoinPoint joinPoint,Exception e){
String name=joinPoint.getSignature().getName();
Object [] args=joinPoint.getArgs();
System.out.println("Log.AfterThrowing exec method "+name+",the args are"+ Arrays.asList(args)+",the Exception is "+e.getMessage());
}
//后置通知
@After("logPointCut()")
public void calcAfter(JoinPoint joinPoint){
String name=joinPoint.getSignature().getName();
Object [] args=joinPoint.getArgs();
System.out.println("Log.calcAfter exec method "+name+",the args are"+ Arrays.asList(args)+"....");
}
@Around("logPointCut()")
public Object calcAround(ProceedingJoinPoint proceedingJoinPoint){
//相當(dāng)于前置通知
Object [] args=proceedingJoinPoint.getArgs();//參數(shù)
System.out.println("Log.calcAround before...,the args are"+Arrays.asList(args));
Object ret=null;
try {
ret=proceedingJoinPoint.proceed();//返回值
//相當(dāng)于返回通知
System.out.println("Log.calcAround AfterReturning...,the result is "+ret);
} catch (Throwable e) {
//相當(dāng)于異常通知
System.out.println("Log.calcAround AfterThrowing..."+e.getMessage());
e.printStackTrace();
}finally {
//相當(dāng)于后置通知
System.out.println("Log.calcAround after...");
return ret;
}
}
}
MainConfig.java 配置類
package com.itbofeng;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan
//開啟AspectJ自動(dòng)代理功能相當(dāng)于xml中的<aop:aspectj-autoproxy />
@EnableAspectJAutoProxy
//配置類:這里使用的是基于注解的方式,相當(dāng)于xml方式的配置文件
public class MainConfig {
}
AopApplicationTests.java 測試類
package com.itbofeng;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
//測試類
public class AopApplicationTests {
@Test
public void testAop() throws Exception {
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfig.class);
Calc calc=applicationContext.getBean(Calc.class);
calc.div(1,1);//改變這里為1/0可以測試異常情況下
}
}
運(yùn)行結(jié)果:
運(yùn)行1:前提說明:先注釋環(huán)繞通知坎匿,運(yùn)行calc.div(1,1);正常情況下:
Log.calcBefore exec method div,the args are[1, 1]
Log.calcAfter exec method div,the args are[1, 1]....
Log.AfterReturning exec method div,the args are[1, 1],the result is 1
Process finished with exit code 0
運(yùn)行2:前提說明:先注釋環(huán)繞通知盾剩,運(yùn)行calc.div(1,0);異常情況下:
Log.calcBefore exec method div,the args are[1, 0]
Log.calcAfter exec method div,the args are[1, 0]....
Log.AfterThrowing exec method div,the args are[1, 0],the Exception is / by zero
java.lang.ArithmeticException: / by zero
運(yùn)行3:前提說明:先注釋除環(huán)繞外的其他通知,運(yùn)行calc.div(1,1);正常情況下:
Log.calcAround before...,the args are[1, 1]
Log.calcAround AfterReturning...,the result is 1
Log.calcAround after...
Process finished with exit code 0
運(yùn)行3:前提說明:先注釋除環(huán)繞外的其他通知碑诉,運(yùn)行calc.div(1,0);異常情況下:
Log.calcAround before...,the args are[1, 0]
Log.calcAround AfterThrowing.../ by zero
Log.calcAround after...
java.lang.ArithmeticException: / by zero
小結(jié):
本文主要講解了AOP中的基本概念彪腔,以及簡單的使用Spring AOP侥锦,主要是為了體驗(yàn)一下什么是AOP进栽,在本文中需要掌握以下三個(gè)方面:
1、AOP的理解:面向切面編程恭垦,是一種編程思想快毛,就像面向?qū)ο缶幊桃粯?Spring AOP只是一個(gè)具體的實(shí)現(xiàn)格嗅,就像Java是面向?qū)ο笳Z言一樣。
2唠帝、Spring AOP在應(yīng)用程序中應(yīng)用場景:在應(yīng)用程序中AOP主要承擔(dān)一些系統(tǒng)級(jí)別的功能也即一些通用的功能屯掖,如日志記錄,權(quán)限校驗(yàn)襟衰,錯(cuò)誤處理贴铜,事務(wù)控制等。
3瀑晒、注解的使用:需要掌握理解這些注解的使用绍坝,以及每個(gè)通知方法執(zhí)行時(shí)機(jī)。
最后:我這里有Spring苔悦、Spring Boot相關(guān)的視頻教程(由于各種原因轩褐,不方便在此公開,還請見諒玖详,不過這視頻是我見到我認(rèn)為最好的教程把介,不過教程也建議有過Spring使用經(jīng)驗(yàn)的朋友觀看,這樣效果會(huì)更加好蟋座,有好多地方會(huì)涉及原理講解拗踢,沒有使用過的話,壓力會(huì)有點(diǎn)大)向臀,我就是一邊沒事的時(shí)候看看教程秒拔,加上自己的理解,寫文章的飒硅,所以文章有誤的地方砂缩,還請大家指正,我一定會(huì)虛心學(xué)習(xí)的三娩,有需要視頻教程的朋友庵芭,可以私信我。如果大家覺得文章不錯(cuò)雀监,點(diǎn)個(gè)贊双吆,關(guān)注一下,感謝你的支持会前。