面向切面編程(AOP)是經過另外一種思考方式來對面向對象編程(OOP)的補充。在抽象的結構中,OOP模塊的基本單元是類,而AOP的基本單元是面。AOP的面可以跨越多個類型和對象來達成模塊化。java
下面是根據個人理解畫的圖:spring
AOP提供了一個不一樣的編程思路,不過springIoc並無依賴AOP,對於Ioc來講,AOP能夠提供支持但不是必須。編程
其中切入點能夠和鏈接點合併,直接在通知中表示:
@Before("execution(com.dust.controller..(..))")
public void before() {/* 方法體 /}
該寫法等價於:
@Pointcut("execution(com.dust.controller..(..))")
public void point(){}
@Befor("point()")
public void before(){/ 方法體 */}api
在AOP中,鏈接點與切入點的關聯關係以及相應的判斷規則是AOP的核心。切入點肯定了AOP入口,可是具體要執行哪部分則由鏈接點決定。鏈接點是方法執行的入口。數組
@Before("execution(* com.dust.controller.*.*(..))")
public void beforController() {
//在Controller被調用前
}
複製代碼
這裏能夠知道,對AOP來講,最小的單元是方法。AOP只能在方法和方法之間切入,而不能切入方法自己緩存
@AfterReturning("execution(* com.dust.controller.*.*(..))")
public void afterController() {
//在Controller執行完以後
}
複製代碼
對方法返回值的獲取:併發
@AfterReturning(
pointcut = "execution(* com.dust.controller.*.*(..))",
returning = "retVal")
public void afterController(Object retVal) {
//對方法返回值的獲取
System.out.println(retVal.toString());
}
複製代碼
其中returning中的參數名稱必需要和advice方法的參數名稱相同,當方法執行返回時,返回值將做爲相應的參數值傳遞給advice方法。若是方法沒有返回值,則該參數爲null。app
@AfterThrowing("execution(* com.dust.controller.*.*(..))")
public void afterController() {
//在Controller拋出異常後執行
}
複製代碼
也能夠設置到拋出給定異常時才執行advice。框架
@AfterThrowing(
pointcut = "execution(* com.dust.controller.*.*(..))",
throwing = "ex")
public void afterController(NullPointerException ex) {
//拋出空指針異常時執行advice
}
複製代碼
@After("execution(* com.dust.controller.*.*(..))")
public void afterController() {
//最終執行通知
}
複製代碼
全部通知方法均可以聲明一個類行爲org.aspectj.lang.JoinPoint的參數。模塊化
環繞通知聲明的參數爲ProceedingJoinPoint,由於須要執行ProceedingJoinPoint的proceed()方法。而其餘的通知則不須要。
JoinPoint提供了不少有用的方法:
一般通知方法獲取方法參數除了上述經過JoinPoint獲取外還能夠經過pointcut獲取
@Pointcut("execution(* com.example.springdemo.controller.*.*(..)) && args(address, text, ..)")
public void inController(String address, String text) {}
@Before("inController(address, text)")
public void beforController(String address, String text) {
System.out.println("在執行控制器以前,獲取參數{address:" + address + ",text:" + text + "}");
}
複製代碼
args(address, text, ..)切入點表達式匹配切入點方法的參數,然後傳入給advice方法。 這是要求全部和該切入點匹配的鏈接點都須要接收參數,還能夠單單在鏈接點接收參數:
@Pointcut("execution(* com.example.springdemo.controller.*.*(..))")
public void inController() {}
@Before("inController() && args(address, text, ..)")
public void beforController(String address, String text) {
System.out.println("在執行控制器以前,獲取參數{address:" + address + ",text:" + text + "}");
}
複製代碼
其餘參數:代理對象(this),目標對象(target)和註釋(@within, @target, @annotation, @args)均可以以相似的方式綁定。
因爲併發問題:死鎖。可能致使業務執行失敗。下次執行又有可能執行成功,所以對於這種操做不但願將重試交給用戶來執行,這個能夠交由系統來執行,這樣對於用戶來講他仍是一次就執行成功了。
因爲要嘗試執行屢次process(),所以使用@Around環繞通知
@Aspect
@Configuration
public class AOPRedo {
//默認最大重試次數
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
@Around("execution(* com.example.springdemo.controller.RestAPIController.*(..))")
public Object apiAroundController(ProceedingJoinPoint pjp) {
int num = 0;
Throwable throwable;
do {
num++;
try {
return pjp.proceed();
} catch (Throwable th) {
System.out.println("嘗試捕獲");
throwable = th;
}
} while (num <= maxRetries);
return null;
}
}
複製代碼
其中重試次數能夠交給配置文件,經過@PropertySource來導入配置信息。
@RestController
@RequestMapping("api")
public class RestAPIController {
@Autowired
EmailService emailService;
private int count = 0;
@RequestMapping("email")
public String email(String address, String info) throws NullPointerException {
if (count++ < 1) {
throw new NullPointerException();
}
return emailService.setEmail(address,info) + "執行次數:" + count;
}
}
複製代碼
執行結果