問題描述
面向對象設計中有一個弊端,當需要為多個不具有繼承關系的對象引入同一個公共行為時,例如日志、安全檢測等,需要在每個類中都要去引用一個公共行為,這樣的話會產生大量的重復代碼。
記錄操作日志,需要在每個方法里都要去進行相同的操作。
這只是記錄操作,加入要去進行統計方法執行的時間,或者對這個方法進行權限的校驗,會產生更多的冗余代碼。
而且這些公共操作和真正的業務交織在一起,會使代碼的可讀性直線下降。
public class UserController {
private Logger logger = Logger.getLogger(this.getClass());
public void login(){
logger.info("操作時間 "+LocalDate.now()+LocalTime.now());
/** 統計操作時間 */
/** 權限校驗 */
}
public void exit(){
logger.info("操作時間 "+LocalDate.now()+LocalTime.now());
/** 統計操作時間 */
/** 權限校驗 */
}
}
在這種情況下,面向切面設計應運而生。面向切面設計因為縮寫AOP,全稱Aspect Oriented Programming。
AOP是對OOP的一個補足,提供了另一種程序結構。將能夠公用的重復的抽取出來,運用動態代理技術,對類進行增強。
AOP的概念
來了解一下核心AOP的概念和術語,這些術語不是Spring定的,不幸的是,AOP的術語不是非常的直觀。然而Spring要是要用自己的術語,將會更加的難懂。
- 切面(Aspect): 切面是通知和切點的結合。
- 連接點(Join point):
- 通知(Advice):通知在 AOP 中代表切面的工作,它定義了切面具體要做什么,何時做。
- 切入點(Pointcut):在 AOP 中切點表示切面生效的地方。
通知類型:
- 前置通知(Before advice):在連接點前面執行,前置通知不會影響連接點的執行,除非此處拋出異常。
- 正常返回通知(After returning advice):在切入點選擇的連接點處的方法正常執行完畢時執行的通知,必須是連接點處的方法沒拋出任何異常正常返回時才調用。
- 異常返回通知(After throwing advice): 在切入點選擇的連接點處的方法拋出異常返回時執行的通知,必須是連接點處的方法拋出任何異常返回時才調用異常通知。
- 返回通知(After (finally) advice): 在切入點選擇的連接點處的方法之后執行的通知(無論方法執行是否成功都會被調用)
- 環繞通知(Around advice): 環繞著在切入點選擇的連接點處的方法所執行的通知,環繞通知可以在方法調用之前和之后自定義任何行為,并且可以決定是否執行連接點處的方法、替換返回值、拋出異常等等。
配置方法
先定義一個切面:
// 切面的注解
@Aspect
// 聲明這個類是組件,把這個組件交由給Spring去管理
@Component
public class LoggerAOP {
}
再定義一個切點:
// Pointcut里面是指定切點具體是哪個類
@Pointcut("execution(* com.tianzeng.controller.*.*(..))")
public void poincut() {}
然后接下來的操作全都是圍繞這個切點進行的
// 前置通知
@Before("poincut()")
public void before() {
logger.debug("前置通知");
}
// 正常返回通知
@AfterReturning("poincut()")
public void afterReturning() {
logger.debug("正常返回通知");
}
// 異常返回通知
@AfterThrowing("poincut()")
public void afterThrowing() {
logger.debug("異常返回通知");
}
// 返回通知
@After("poincut()")
public void after() {
logger.debug("返回通知");
}
// 環繞通知
@Around("poincut()")
public void around(ProceedingJoinPoint point) throws Throwable{
logger.debug("執行目標方法之前");
// 放行方法
point.proceed(point.getArgs());
logger.debug("執行目標方法之后");
}
跑起來測試一下: