首先上一張AOP的圖示
一:幾個重要的概念
1> 切面:橫切關注點(跨越應用程序多個模塊的功能)被模塊化的特殊對象[驗證切面,日誌切面]
2> 通知:切面中的每一個方法
3> 目標:被通知的方法(業務邏輯中的方法)
4> 代理(proxy):向目標對象應用通知之後創建的對象
5> 連接點:程序執行前的某個特定位置(具體的物理存在):如某個方法調用前,調用後,方法拋出異常後等。連接點由兩個信息確定:方法表示的程序執行點相對點表示的方位。
6> 切點:每個類會有多個連接點(看不到摸不到,非具體物理存在)[通過切點可以定位到很多個連接點]即:連接點是程序類中客觀存在的事務。AOP通過切點定位到特定的鏈接點。類比:連接點相當於數據庫中的記錄,切點相當於查詢條件。切點和連接點不是一一對應的關係,一個切點匹配多個連接點,切點通過接口進行描述,使用類和方法作爲連接點的查詢條件。
二:Spring AOP
1)加入jar包
2)在配置文件中加入aop的命名空間(namespace),完整命名空間如下:
<?xml version="1.0" encoding="GB18030"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
</beans>
3)基於註解的方式
1.在配置文件中加入該配置
<!-- 啓動用JDK動態代理完成對Aop的支持,支持註解的形式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.配置切面及業務邏輯對象
但在 spring4.0 可以配置自動掃描的包,只需要包命明確即可
<context:component-scan base-package="" />
spring2.0不具備該屬性,正確配置如下:1》切面 2》業務邏輯對象
<!-- 切面 -->
<bean id="checkAspect" class="com.inspur.aop.impl.CheckAspect"></bean>
<!-- 業務邏輯對象 -->
<bean id="calculator" class="com.inspur.aop.impl.CalculatorLightImpl">
</bean>
3.通過代碼進行展示 testspring2
package com.inspur.aop.impl;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class CheckAspect {
/**
* 切入點 做標識使用 特徵:private void 方法名(){ }
*/
@Pointcut("execution(* com.inspur.aop.impl.*.*(..))")
public void allCalMethod() {}
@Before("allCalMethod()")
public void check(JoinPoint joinPoint) {
//兩種方法獲取參數的值
/*// 取得參數的值
Object[] objects = joinPoint.getArgs();
if (objects != null && objects.length > 0) {
for (Object arg : objects) {
System.out.println(arg);
}
}*/
String methodName = joinPoint.getSignature().getName();
System.out.println(" methodName " + methodName);
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println(" the method " + methodName + " begin with " + args);
}
}
另:切入點其實可以不進行書寫,僅作爲標識使用。
如不寫,則在通知(@befor,@after等)中寫進執行語句
即@Before("execution(* com.inspur.aop.impl.*.*(..))")
也可以實現作爲切面的功能。
面向切面編程是面向對象編程的一種改進,他的出現在於解決兩大問題:(詳見testSpring2項目)
1.代碼混亂:越來越多的非業務需求(日誌和校驗等)加入後,原有的業務方法急劇膨脹, 每個方法在處理核心業務邏輯時,還必須兼顧其他多個關注點。
2.代碼分散:以日誌需求爲例,只是爲了滿足這個單一的需求,就不得不在多個模塊 裏面多次重複相同的日誌代碼。如果日誌代碼發生改變,也必須修改所有模板。[核心代碼需要寫入衆多的輸出語句,代碼冗餘及維護困難等各種問題]
從上述代碼引申三個問題:
1.切入點的定義[execution:執行]
·任意公共方法的執行
execution(public * *(..))
·任意一個以‘set’開始的方法的執行
execution(* set*(..))
·AccountService接口的任意方法的執行
execution(* com.xyz.service.AccountService.(..))
·定義在service包裏任意方法的執行(最爲常用)
execution(* com.xyz.service.*.*(..))
·定義在service包或子包裏任意方法的執行
)
2.Adivce,通知的類型
·前置通知 @Before
·後置通知 @After
·返回後通知
·異常拋出後通知
·環繞通知
3.JoiPoint方法:任何一個增強方法都可以通過將第一個入參聲明爲 JoinPoint
訪問到連接點上下文的信息。(簡化日誌)
AspectJ 使用 org.aspectj.lang.JoinPoint 接口表示目標類連接點對象,如果是環繞增強時,使用org.aspectj.lang.ProceedingJoinPoint
表示連接點對象,該類是 JoinPoint 的子接口。任何一個增強方法都可以通過將第一個入參聲明爲 JoinPoint 訪問到連接點上下文的信息。我們先來了解一下這兩個接口的主要方法:
1) JoinPoint
java.lang.Object[] getArgs():獲取連接點方法運行時的入參列表;
Signature getSignature() :獲取連接點的方法簽名對象;
java.lang.Object getTarget() :獲取連接點所在的目標對象;
java.lang.Object getThis() :獲取代理對象本身;
2) ProceedingJoinPoint
ProceedingJoinPoint繼承JoinPoint子接口,它新增了兩個用於執行連接點方法的方法:
java.lang.Object proceed() throws java.lang.Throwable:通過反射執行目標對象的連接點處的方法;
java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通過反射執行目標對象連接點處的方法,不過使用新的入參替換原來的入參。
上面的代碼提到了兩種獲取參數的方法,重點記住各個方法的類型及遍歷的方式。