最近在學習AOP,以前一直很不明白,什麼是AOP?爲何要使用AOP,它有什麼做用?學完以後有一點小小的感觸和本身的理解,因此在這裏呢就跟你們一塊兒分享一下html
AOP(Aspect-Oriented Programming)實際上是OOP(Object-Oriented Programing) 思想的補充和完善。咱們知道,OOP引進"抽象"、"封裝"、"繼承"、"多態"等概念,對萬事萬物進行抽象和封裝,來創建一種對象的層次結構,它強調了java
一種完整事物的自上而下的關係。可是具體細粒度到每一個事物內部的狀況,OOP就顯得無能爲力了。好比日誌功能。日誌代碼每每水平地散佈在全部對象層次當
中,卻與它所散佈到的對象的核心功能毫無關係。對於其餘不少相似功能,如事務管理、權限控制等也是如此。這致使了大量代碼的重複,而不利於各個模塊的重
用。 而AOP技
術則偏偏相反,它利用一種稱爲"橫切"的技術,可以剖解開封裝的對象內部,並將那些影響了多個類而且與具體業務無關的公共行爲 封裝成一個獨立的模塊(稱
爲切面)。更重要的是,它又能以巧奪天功的妙手將這些剖開的切面復原,不留痕跡的融入核心業務邏輯中。這樣,對於往後橫切功能的編輯和重用都可以帶來極大
的方便。 AOP技術的具體實現,無非也就是經過動態代理技術或者是在程序編譯期間進行靜態的"織入"方式。下面是這方面技術的幾個基本術語: 算法
一、join point(鏈接點):是程序執行中的一個精確執行點,例如類中的一個方法。它是一個抽象的概念,在實現AOP時,並不須要去定義一個join point。 spring
二、point cut(切入點):本質上是一個捕獲鏈接點的結構。在AOP中,能夠定義一個point cut,來捕獲相關方法的調用。 app
三、advice(通知):是point cut的執行代碼,是執行「方面」的具體邏輯。 學習
四、aspect(切面):point cut和advice結合起來就是aspect,它相似於OOP中定義的一個類,但它表明的更可能是對象間橫向的關係。 測試
說了這麼多,可能咱們仍是對AOP有點不知所措,不知道是幹什麼的,那麼咱們就以一個例子做爲講解,來理解這個抽象的概念spa
咱們有一個簡易的計算器,進行加減乘除的操做,有一個需求,1.須要在進行算法以前和以後進行輸出一句話代理
那麼對於以上操做咱們可能最容易想到的就是用一個實現類實現這個接口。而後在接口調用方法先後輸出一句話日誌
這樣確實能實現這個需求,可是可能有的同窗就想到了,是否是重複代碼了呢?若是我有上千個方法呢?是否是還得在每一個方法裏增長几行代碼?咱們能不能在不改變原來方法的結構上
也能實現相同的需求呢?這個時候AOP就能幫咱們實現了。下面咱們詳細的講解下如何使用註解的方式來實現AOP
仍是一樣的接口和實現類,只是這時候實現類中沒有了輸出語句,如圖
上圖就是最原始的方法了,也就是說咱們在這個方法裏面只須要關注咱們方法執行的內容,並不須要關注一些方法以外的東西,好比說記錄日誌,方法前輸出語句等等。。
那麼,既然這個方法什麼都不關注的話,那咱們的輸出語句又在哪兒寫呢?這個時候咱們就定義一個專門的類,用它來做爲切面,代碼以下所示
package advice; import java.util.Arrays; import java.util.List; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect //聲明註解 public class CalculationAnnotation { /** * 定義前置通知 * execution(* biz.UserBiz.*(..)) 表示 全部修飾符的全部返回值類型 biz.UserBiz 包下的全部方法 * 在方法執行以前執行 * */ @Before("execution(* biz.CalculationImpl.*(..))") public void before(JoinPoint join){ //獲取方法名 String mathName=join.getSignature().getName(); //獲取參數列表 List<Object> args = Arrays.asList(join.getArgs()); System.out.println("前置通知---->before 方法名是:"+mathName+"\t參數列表是:"+args); } /** * 後置通知 * 在方法返回後執行,不管是否發生異常 * 不能訪問到返回值 * * */ @After("execution(* biz.CalculationImpl.*(..))") public void after(){ System.out.println("後置通知---->after...."); } }
@Aspect ----->表示聲明這個類是一個切面,
這樣呢,我們這個切面就聲明完畢了,那麼,咱們能夠想到,這個時候咱們只是聲明瞭一個切面而已,並無在那個地方用到這個切面對不對?也就是說咱們配置的切面還跟咱們程序尚未任何的關聯關係
這樣的話呢,就引出了咱們的配置文件了也就是咱們Spring的配置文件applicationContext.xml,配置以下
<?xml version="1.0" encoding="UTF-8"?> <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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd "> <!-- 配置Bean --> <bean id ="CalculationImpl" class="biz.CalculationImpl"></bean> <!-- 將切面類交與Spring容器管理 --> <bean class="advice.CalculationAnnotation"></bean> <!-- 使用註解自動生成代理對象 --> <aop:aspectj-autoproxy/> </beans>
這個時候咱們能夠看看配置文件裏到底寫了什麼,寫這些是幹啥的,有什麼用。
這個你們確定都懂是吧,這沒話說,也就是將CalculationImpl類交給Spring容器來管理,若是有不懂的童鞋能夠看看個人另外一篇關於Spring IOC 的文章
那麼這行代碼呢?這行代碼的意思就是將咱們的CalculationAnnotation類交給Spring容器管理,由於咱們在CalculationAnnotation類中不是聲明瞭一個@Aspect切面註解嗎對不對
當Spring容器初始化的時候它會找有沒有這個節點,若是有的話呢,容器就會根據你的Bean配置,找看那個類中配置了@Aspect切面註解
若是找到了的話那麼就根據你的註解來執行相應的代碼,什麼意思呢?好比說如圖所示
好,那麼咱們就來看看執行以後結果會是怎樣的呢?
這樣咱們是否是就完成了以前的需求呢?在執行代碼前輸出一行語句,若是咱們想要作到日誌的記錄的話,是否是隻須要把輸出語句修改成記錄日誌的代碼就能夠了呢。並且我尚未影響任何的功能性代碼
也就是對源代碼並無作任何的修改,那麼既然有前置加強的話確定也有後置加強和其餘加強操做下面我就講講後置加強,
其實對於其餘的加強類型的話呢,既然知道前置加強是怎麼一回事了,那麼其餘四種就垂手可得了
後置加強,其實咱們只須要在切面類也就是咱們寫前置加強的類中直接添加後置 加強代碼便可,如圖
只是將註解標籤給進行了一道修改,其餘的任何操做咱們都不須要在進行修改,示例結果如圖所示
這樣是否是就完成了在方法先後執行與方法無關的代碼呢?可能有些童鞋有疑問,爲何輸出語句是在最後輸出的,不該該是夾在中間嗎?可是咱們看測試代碼,我是執行了add方法,接收了一個返回值,而後在方法的外面輸出的我接收的返回值變量,那麼這樣的話,可不就是咱們看到的結果嘛。
因爲時間的關係呢,我今天就先給你們分享下前置加強和後置加強。至於返回,異常和環繞的話呢,我就下次在跟你們分享吧!但願你們可以學到點東西吧!
轉載:https://www.cnblogs.com/liujiayun/p/5912628.html