學習Spring5必知必會(5)~Spring AOP
- 2022 年 3 月 2 日
- 筆記
- Spring AOP
一、學習 AOP 思想的準備工作:
1、橫切面關注點
在開發中,為了給業務方法中增加日誌記錄,權限檢查,事務控制等功能,此時我們需要在修改業務方法內添加這些零散的功能代碼(橫切面關注點)。
-
這些零散存在於業務方法中的功能代碼【例如:日誌記錄,權限檢查,事務控制】,我們稱之為
橫切面關注點
,橫切面關注點不屬於業務範圍,應該 從業務代碼中剝離出來.
2、AOP思想 (Aspect Oritention Programming):面向切面編程的思想
-
切面:
把一個個的橫切關注點放到某個模塊中去
,稱之為切面。 -
那麼每一個的切面都能影響業務的某一種功能,
切面的目的就是功能增強
,如日誌切面就是一個橫切關注點,應用中許多方法需要做日誌記錄的只需要插入日誌的切面即可.
3、AOP 思想的原理:是動態代理
4、了解 AOP 術語:
Joinpoint
:連接點,被攔截到需要被增強的方法
。
where:去哪裡做增強
- Pointcut:切入點,哪些包中的哪些類中的哪些方法,可認為是連接點的集合。
where:去哪些地方做增強
-
Advice
:增強(通知),當攔截到 Joinpoint 之後,在方法執行的什麼時機(when)做什麼樣(what)的增強
。 根據時機分為:前置增強、後置增強、異常增強、最終增強、環繞增強
-
Aspect
:切面,Pointcut+Advice, 去哪些地方+在什麼時機+做什麼增強
-
Target:目標對象,被代理的目標對象,委託對象。
-
Weaving:織入,把 Advice 加到 Target 上之後,創建出 Proxy 對象的過程。
-
Proxy:一個類被 AOP 織入增強後,產生的代理類 Advice(增強)執行時機,代理對象。
5、Pointcot 語法 【找到具體的某個方法-哪個包.哪個類.哪個方法
】
(1)AspectJ 切入點語法如下(表示在哪些包下的哪些類中的哪些方法上做切入增強):
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
即 ★execution(<修飾符>? <返回類型> <聲明類型>? <方法名>(<參數>) <異常>)
- 例子:public static Class java.lang.Class.forName(String className)throws ClassNotFoundException
(2)切入點表達式中的通配符(看具體的方法,先從方法名位置開始看
):
*:匹配任何部分,但是只能表示一個單詞。
..:可用於全限定名中和方法參數中,分別表示子包和 0 到 N 個參數。
二、AOP 開發:
1、依賴:
- spring-aop.jar
- com.springsource.org.aopalliance.jar [spring5的spring-aop.jar已經包含]
- com.springsource.org.aspectj.weaver.jar
2、配置:
(1)引入AOP的約束(在beans的基礎進行修改即可)
- 當然也可以使用插件sts,打開xml文件方式選擇 Spring Config Editor
(2)配置AOP:
<!-- AOP 配置:在什麼地點、什麼時機、做什麼 -->
<!-- 1、what【what(人物)】:做什麼增強(關聯what) -->
<bean id="transactionManager" class="com.shan.tx.TransactionManager"/>
<aop:config proxy-target-class="false"> <!-- 屬性proxy-target-class配置是否使用真實對象 -->
<!-- 配置AOP切面 -->
<aop:aspect ref="transactionManager"> <!-- 關聯what -->
<!-- 2、where【where+what(地點+人物)】:在哪些包中的哪些類中的哪些方法上做增強 -->
<aop:pointcut id="txPoint" expression="execution(* com.shan.service..*Service*.*(..))"/>
<!-- 3、when【when+where+what(時間+地點+人物)】:在方法執行的什麼時機(在哪裡where-關聯pointcut)做增強 -->
<aop:before method="open" pointcut-ref="txPoint"/>
<aop:after-returning method="commit" pointcut-ref="txPoint"/>
<aop:after-throwing method="rollback" pointcut-ref="txPoint"/>
<aop:after method="close" pointcut-ref="txPoint"/>
<aop:around method="aroundMethod" pointcut-ref="txPoint"/>
</aop:aspect>
</aop:config>
三、AOP 各種增強
1、增強的分類:
■ 根據被增強的方法
的執行時機分為:前置增強、後置增強、異常增強、最終增強、環繞增強
- 前置增強:權限控制、日誌記錄等 [被增強的方法執行之前]
- 後置增強:提交事務、統計分析數據結果等 [被增強的方法正常執行之後(中途沒有異常)]
- 最終增強:回滾事務、記錄日誌異常信息等 [被增強的方法出現異常]
- 最終增強:釋放資源等 [finally最後操作]
- 環繞增強:緩存、性能日誌、權限、事務管理等 [可以自定義在被增強方法的什麼時機執行(返回一個Object,參數processdingJoinpoint)]
2、增強細節:
(1)獲取異常的信息:
public void rollback(Throwable ex) {
System.out.println("回滾事務~,異常信息:" +ex.getMessage());
}
(2)獲取被增強方法的信息【獲取被增強方法的信息,並且可以傳遞給增強方法】:
- Spring AOP:Joinpoint類 連接點,訪問被增強方法的真實對象,代理對象,方法參數等
- 可以作為前置、後置、異常、最終增強方法的參數,
第一個參數
//可以作為前置、後置、異常、最終增強方法的參數,**`第一個參數`**
public void open(JoinPoint jp) {
System.out.println("開啟事務~");
System.out.println("代理對象:" +jp.getThis().getClass());
System.out.println("目標對象:" +jp.getTarget().getClass());
System.out.println("被增強方法的參數:" +Arrays.toString(jp.getArgs()));
System.out.println("連接點方法的簽名:" +jp.getSignature());
System.out.println("當前連接點的類型:" +jp.getKind());
}
(3) 環繞增強方法調用真實對象的方法【參數processdingJoinpoint】:
-
參數processdingJoinpoint:是JointPoin 的子類,只能用於環繞增強,
作為第一個參數
還可以調用真實對象中被增強的方法。
//調用真實對象的方法 ret = pjp.proceed();
public Object aroundMethod(ProceedingJoinPoint pjp) {
Object ret = null;
System.out.println("開啟事務~");
try {
ret = pjp.proceed();//調用真實對象的方法
System.out.println("調用真實對象的方法...~");
System.out.println("提交事務~");
} catch (Throwable e) {
System.out.println("回滾事務~,錯誤信息:" + e.getMessage());
}finally {
System.out.println("關閉資源~");
}
return ret;
}
四、使用註解配置AOP
AOP 註解:
(1)在配置文件中添加註解的解析器的配置【第三方程序(賦予註解的特殊功能)】:
- 使用cglib註解:配置屬性proxy-target-class=”true”
<!-- AOP註解的解析器 -->
<aop:aspectj-autoproxy/>
(2)使用註解@Aspect
(配置一個AOP切面
)
- @Pointcut (
配置where
) - @Before、@AfterReturning、@AfterThrowing、@After、@Around(
配置when
)
@Component@Aspect //配置一個AOP切面
public class TransactionManager {
//where
//xml:<aop:pointcut id="txPoint" expression="execution(* com.shan.service..*Service*.*(..))"/>
@Pointcut("execution(* com.shan.service..*Service*.*(..))")
public void txPoint() {
}
//@Before("txPoint()")
public void open(JoinPoint jp) {
System.out.println("開啟事務~");
}
//@AfterReturning("txPoint()")
public void commit() {
System.out.println("提交事務~");
}
//@AfterThrowing(value="txPoint()", throwing="ex")
public void rollback(Throwable ex) {
System.out.println("回滾事務~,異常信息:" +ex.getMessage());
}
//@After("txPoint()")
public void close() {
System.out.println("關閉資源~");
}
@Around("txPoint()")
public Object aroundMethod(ProceedingJoinPoint pjp) {
Object ret = null;
System.out.println("開啟事務~");
try {
ret = pjp.proceed();//調用真實對象的方法
System.out.println("調用真實對象的方法...~");
System.out.println("提交事務~");
} catch (Throwable e) {
System.out.println("回滾事務~,錯誤信息:" + e.getMessage());
}finally {
System.out.println("關閉資源~");
}
return ret;
}
}