Spring中AOP
阿新 • • 發佈:2018-12-18
動態代理
特點: 位元組碼隨用隨建立,隨用隨載入 作用: 不修改原始碼的基礎上對方法增強 分類: 1. 基於介面的動態代理 涉及的類: Proxy 提供者: jdk官方 如何建立代理物件: 使用Proxy類中的newProxyInstance方法 建立代理物件的要求: 被代理的類至少實現一個介面,如果沒有則不能使用 newProxyInstance方法的引數: ClassLoader:類載入器 它是用於載入代理物件位元組碼的,和被代理物件使用相同的類載入器,固定寫法 Class[]:位元組碼陣列 它是用於讓代理物件和被代理物件有相同的方法,固定寫法 InvocationHandler:用於提供增強的程式碼 它是用於讓我們寫如何代理,我們一般都是寫一個該介面的實現類,通常是一個匿名內部類(不是必須的) 此介面的實現類都是誰用誰寫 *存在的問題:當我們的類沒有實現任何的介面的時候是不可以使用的 代理物件執行方法,其實就是代替被代理物件執行方法 被代理物件實現介面 生成代理物件 代理物件.被代理物件的方法 2. 基於子類的動態代理(cglib) 涉及的類: Enhancer 提供者: 第三方cglib庫 如何建立代理物件: 使用Enhancer類中的create方法 建立代理物件的要求: 被代理的類不是最終類,不能被final修飾 create方法的引數: Class:位元組碼 它是用於指定被代理物件的位元組碼 Callback:用於提供增強的程式碼 它是用於讓我們寫如何代理,我們一般都是寫一個該介面的實現類,通常是一個匿名內部類(不是必須的) 此介面的實現類都是誰用誰寫 我們一般寫的都是該介面的子介面實現類:MethodIntercepor
Spring中的AOP
概述: AOP:全稱是 Aspect Oriented Programming 即:面向切面程式設計。 簡單的說它就是把我們程式重複的程式碼抽取出來,在需要執行的時候,使用動態代理的技術,在不修改原始碼的 基礎上,對我們的已有方法進行增強 作用以及優勢: 作用: 在程式執行期間,不修改原始碼對已有方法進行增強。 優勢: 減少重複程式碼 提高開發效率 維護方便 實現方式: 使用動態代理技術 AOP相關術語: 連線點: 目標物件的所有方法(所有的切入點都是連線點) 切入點: 被增強的都是切入點 增強/通知: 關於增強的程式碼塊(可以是抽取出來的類) 引介: 是一種特殊的通知在不修改類程式碼的前提下, Introduction 可以在執行期為類動態地新增一些方法或 Field。 Target: 被代理的物件 織入: 是指把增強應用到目標物件來建立新的代理物件的過程。 spring 採用動態代理織入,而 AspectJ 採用編譯期織入和類裝載期織入。 代理(Proxy): 一個類被AOP織入增強後,產生的結果代理類 切面: 切入點與通知的結合
Spring在AOP開發中代理方式的選擇
proxy-target-class
預設false: 自動選擇
true: 使用cglib代理
1. 基於XML的
<aop:config proxy-target-class="true">
.....
</aop:config>
2. 基於註解
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
Spring中基於XML的AOP的配置
1. 把通知bean交給Spring來管理 2. 使用aop:config標籤表明開始AOP的配置 3. 使用aop:aspect標籤表明開始配置切面 id屬性:給切面提供一個唯一標識 ref屬性:是指定通知類bean的id 4. 在aop:aspect標籤的內部使用對用的標籤來配置通知的型別 前置通知: aop:before(表示配置前置通知) method屬性:用於指定類中哪個方法是前置通知 pointcut屬性:用於指定切入點表示式,該表示式的含義指的是對業務層的哪些方法增強 切入點表示式的寫法: 關鍵字: execution(表示式) 表示式: 訪問修飾符 返回值 包名.包名...類名.方法名(引數列表) 標準表示式的寫法: execution(public void com.qin.service.impl.AccountServiceImpl.xxx(..)) 訪問修飾符可以省略 void com.itheima.service.impl.AccountServiceImpl.saveAccount() 返回值可以使用萬用字元,表示任意返回值 * com.itheima.service.impl.AccountServiceImpl.saveAccount() 包名可以使用萬用字元,表示任意包。但是有幾級包,就需要寫幾個*. * *.*.*.*.AccountServiceImpl.saveAccount()) 包名可以使用..表示當前包及其子包 * *..AccountServiceImpl.saveAccount() 類名和方法名都可以使用*來實現通配 * *..*.*() 引數列表: 可以直接寫資料型別: 基本型別直接寫名稱 int 引用型別寫包名.類名的方式 java.lang.String 可以使用萬用字元表示任意型別,但是必須有引數 可以使用..表示有無引數均可,有引數可以是任意型別 全通配寫法: * *..*.*(..) 實際開發中切入點表示式的通常寫法: 切到業務層實現類下的所有方法 * com.itheima.service.impl.*.*(..) 5. 例 <!-- 配置目標類--> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean> <!-- 配置通知類 --> <bean id="logger" class="com.itheima.utils.Logger"></bean> <!--配置AOP--> <aop:config> <!-- 配置切入點表示式 id屬性用於指定表示式的唯一標識。expression屬性用於指定表示式內容 此標籤寫在aop:aspect標籤內部只能當前切面使用。 它還可以寫在aop:aspect外面,此時就變成了所有切面可用 --> <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut> <!--配置切面 --> <aop:aspect id="logAdvice" ref="logger"> <!-- 配置前置通知:在切入點方法執行之前執行--> <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before> <!-- 配置後置通知:在切入點方法正常執行之後值。它和異常通知永遠只能執行一個--> <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1" returning="val"></aop:after-returning> <!-- 配置異常通知:在切入點方法執行產生異常之後執行。它和後置通知永遠只能執行一個--> <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1" throwing="ex"></aop:after-throwing> <!-- 配置最終通知:無論切入點方法是否正常執行它都會在其後面執行--> <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after> <!-- 配置環繞通知 --> <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around> </aop:aspect> </aop:config>
Spring中基於註解的AOP
1. 目標物件
即被代理的物件
如:業務層的AccountServiceImpl
2. 通知物件
即散落在其他類中可以抽取成共性的程式碼塊,可以提取成為一個類(比如日誌記錄,程式碼效能監控)
3. 在核心配置中開啟對AOP的支援
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4. 使用註解在通知上配置切面
4.0 表示當前類是一個切面類
@Aspect
4.1 配置切點
@Pointcut("execution(* com.itheima.service.impl.*.update(..))")
private void pt1(){}
@Pointcut("execution(* com.itheima.service.impl.*.save(..))")
private void pt2(){}
4.3 在方法上配置註解
@Before 前置通知
@After 最終通知
@AfterReturning 後置通知
@AfterThrowing 異常通知
@Around 環繞通知