學習Spring5必知必會(5)~Spring AOP

一、學習 AOP 思想的準備工作:

1、橫切面關注點

在開發中,為了給業務方法中增加日誌記錄,權限檢查,事務控制等功能,此時我們需要在修改業務方法內添加這些零散的功能代碼(橫切面關注點)。

  • 這些零散存在於業務方法中的功能代碼【例如:日誌記錄,權限檢查,事務控制】,我們稱之為橫切面關注點

    橫切面關注點不屬於業務範圍,應該 從業務代碼中剝離出來.

image

2、AOP思想 (Aspect Oritention Programming):面向切面編程的思想

  • 切面:把一個個的橫切關注點放到某個模塊中去,稱之為切面。

  • 那麼每一個的切面都能影響業務的某一種功能, 切面的目的就是功能增強

    如日誌切面就是一個橫切關注點,應用中許多方法需要做日誌記錄的只需要插入日誌的切面即可.

image

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

image

(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)]

image

2、增強細節:

(1)獲取異常的信息:

image

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;
	}
}