面向切面編程(AOP)
為什么會有面向切面編程?
面向切面是針對對面向對象編程的一種補充,
在開發時有時使用面向對象并不能很好完成一些額外的功能業務時,可以采用AOP來進行補充。
切面編程在代碼中主要做了什么?
對目標對象的進行額外的增強或擴展,
原理就是對方法前后進行了織入了增強代碼,
而增強代碼本質也就是方法,就是對目標行為植入額外的邏輯代碼,從而增強原有的功能。
了解AOP編程
在Spring中的AOP是基于JDK動態代理和CGLIB動態代理是實現的。
而Spring只是基于AspectJ的切點表達式語法(如@Pointcut),再結合動態代理機制(JDK Proxy或CGLIB)實現的輕量級AOP。
其支持范圍僅限于支持方法執行連接點且必須納入Spring容器管理
咨詢:AspectJ 作為獨立的 AOP 語言擴展,支持全連接點類型和更底層的代碼織入
實現SpringAOP案例:
對服務層的方法額外增強
Service層:目標連接點對象
@Service
public class UserService {
public String save(String name){
System.out.println("保存用戶");
return "holle"+name;
}
}
配置類
Spring AOP 的設計目標是為 Spring 生態提供簡單易用、低侵入性的 AOP 支持,而非替代 AspectJ。
通過@EnableAspectJAutoProxy即用啟用,無需額外編譯器或復雜的編織配置
@Configuration
@ComponentScan(basePackages = "edu.keep")
@EnableAspectJAutoProxy(proxyTargetClass = true)
//啟用AspectJ注解處理器
public class AppConfig {
}
切面類
@Aspect
@Component
public class UserServiceAspect {
/**
* 使用@Pointcut注解標識在一個任意的方法上表示未切入點
* 主要是來編寫切入點表達式(AspectJ的切入點表達式)
* execution()是用來執行切入點表達式的一個函數
* 下面的表達式中:
* 第一個星號:表示連接點方法的訪問修飾符
* 第二個星號:表示所有類
* 第三個星號:表示類下面的所有方法
* (星號可以改為具體的類名或者方法名)
* (..)表示任意類型和個數的參數
*
* 因此,被切入點找到的這些方法都稱之為連接點
*/
@Pointcut("execution(* edu.keep.service.UserService.*(..))")
public void pointcut(){
}
/**
* 前置通知
* @param jp 連接點信息(目標方法的信息)
*/
@Before("pointcut()")
public void before(JoinPoint jp){
System.out.println("前置通知,參數:"+jp.getArgs()[0]);
}
/**
* 后置通知
* @param jp 連接點信息
* @param returnVal 連接點方法的返回值
*/
@AfterReturning(value = "pointcut()",returning = "returnVal")
public Object after(JoinPoint jp,String returnVal){
System.out.println("后置通知,返回值:"+returnVal);
return returnVal;
}
/**
* 環繞通知
* @param jp 連接點信息
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("環繞通知前...");
//調用目標方法
Object proceed = jp.proceed();
System.out.println("環繞通知后...");
return proceed;
}
/**
* 異常通知
*/
@AfterThrowing(value = "pointcut()",throwing = "e")
public void afterThrowing(JoinPoint jp,Exception e){
System.out.println("異常通知,異常:"+e.getMessage());
}
/**
* 最終通知
* @param jp 連接點信息
*/
@After("pointcut()")
public void after(JoinPoint jp){
System.out.println("最終通知");
}
}
總結具體實現流程:
1.定義切入點:通過表達式指定增強的方法
2.編寫通知或增強,本質就是定義增強邏輯(如日志、事務)方法,并在指定時機(方法執行前、后、異常時)觸發。
3.動態代理:通過 JDK 動態代理(接口代理)或 CGLIB 動態代理(類代理)生成代理對象,包裹目標對象。
應用場景
日志操作:
可以在業務方法前后進行日志的記錄,不需要每個業務方法中都編寫重復的代碼
權限管理:
可以在調用目標方法前確定是否有權限
事務管理 :
可以在調用業務方法前開啟事務,方法執行完成后提交事務
AOP術語
切面(Aspect):
切面(本質上也是一個類在)是用于編寫增強邏輯的一個類,這個類很類似于JDK動態代理中的回調處理器或者cglib中的方法攔截器,主要就是將需要增強目標對象的功能代碼編寫在這個類中,而這些功能增強的代碼就是切面邏輯。
通知/增強(Advice):
增強(本質上也是方法)就是對目標行為植入額外的邏輯代碼,從而增強原有的功能。增強分為五種類型:
1)前置通知(在目標方法調用之前執行)
2)后置通知(在目標方法正確返回之后執行)
3)環繞通知(在目標方法調用前后執行)
4)異常通知(當目標方法拋出異常時執行,并且不會執行后置通知)
5)最終通知(不管目標方法有無異常都會執行)
切入點(Pointcut):
切入點類似一個切入的坐標,目的就是要找到目標對象的哪些方法進行切入或增強。切入點可以使用表達式進行描述。
連接點(Joinpoint):
目標對象的方法(被切入的方法)就稱之為連接點,一個切入點可以對應目標對象的的多個連接點。
代理(Proxy):
在運行時動態創建的對象,稱之為代理對象,負責調用目標對象的方法,并執行增強功能。
目標(Target):
被代理的對象就是目標對象。
織入(Weaver):
將切面中的增強邏輯應用到目標具體的連接點上并產生代理的過程稱之為織入。
因此通常描述為“將通知織入到具體的目標”。
織入的時機可以分為以下幾種:
類加載時織入,需要特殊的類加載器(LTW)
編譯時織入,需要特殊的編譯器(CTW)
運行時織入,通常使用JDK或者CGLIB在程序運行創建代理對象,
spring就是基于運行時織入的。(注意:spring僅僅只是用到了AspectJ的切入點表達式和注解,但并沒有使用AspectJ的類加載和編譯時織入功能,而是使用JDK和CGLIB在運行時生成代理對象。)

浙公網安備 33010602011771號