您好,登錄后才能下訂單哦!
這篇文章給大家介紹Java SpringAOP切面類該如何理解,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
簡單的來說,就是動態的在方法的指定位置添加指定的代碼。
在軟件開發的過程中,有很多業務,特別是在編寫核心業務的時候,往往需要很多其他的輔助業務,比如說身份驗證(銀行轉賬需要身份驗證)、數據緩存、日志輸出。這些往往在某個核心業務中處于輔助的部分。這些輔助的任務都有個特點,就是這些業務都處在核心業務的同一個切面上?
什么意思呢?
假如有加減乘除四個方法,方法開始位置和方法結束位置只是一個標志,方法執行位置處是核心業務,我們想在這四個方法的核心代碼前執行一些準備操作,那么我們可以在方法開始位置和方法執行位置之間加入一段代碼,那么這些準備操作實際上就是在同一個切面上的。同理,在四個方法的任意處切一刀,都是一個切面。
什么時候需要用切面類?
對于一些方法,抽取出來同一類非核心業務,然后可以將提取出來的業務編寫成一個切面類,切面類可以;例如加減乘除,加入日志功能,那么日志功能就是非核心業務。
切面類有什么用?
解決代碼混亂問題,非核心業務和核心業務代碼處于同一個方法中會影響代碼的質量,甚至可能會影響到核心業務
在數據處理之前顯示我們傳入的數據
遇到異常返回
處理結束顯示處理完成
日志如何實現
最簡單的方法,在數據處理之前手動輸出。
public void receiveMoney(int receiveMoney) throws ReceiveMoneyException { System.out.println("[收錢]:參數為"+receiveMoney); System.out.println("[收錢]數據處理中。。。。"); checkAmount(receiveMoney); System.out.println("[收錢]數據處理事務完成完成"); }
這樣我們的日志功能就可以實現了,但是,這只是其中一個輔助業務,一個項目中有很多業務,各種繁瑣的功能和日志都實現在一個方法中,代碼結構會無比的混亂,特別是一個日志功能和核心功能放在一起,很容易發生問題,并且一個業務中往往還要很多其他非核心的業務需要處理,比如說在接受錢之前,需要驗明身份,來路不明的錢銀行不能直接接收,若身份核驗正確,那么接收到錢后還得進行數據緩存。
身份驗證、數據緩存、異常處理等非核心業務如果處理不好往往會導致核心業務代碼結構混亂。
那么怎樣能將日志功能、身份驗證加入到核心業務方法之中,但是不影響核心業務 的代碼
切面類能完成這些任務
切面類能動態的在指定位置添加指定代碼
AspectJ 支持 5 種類型的通知注解:
@Before:
前置通知, 在方法執行之前執行
@After:
后置通知, 在方法執行之后執行
@AfterRunning:
返回通知, 在方法返回結果之后執行
@AfterThrowing
: 異常通知, 在方法拋出異常之后
@Around:
環繞通知, 圍繞著方法執行
通知是啥?簡單理解就是上面說到的輔助業務,我們在劃分切面的提取輔助業務代碼時候,會有以下情況
需要在核心業務前執行該輔助業務
需要在核心業務執行之后執行該輔助業務
需要在報錯時候執行該輔助業務
需要在返回結果是執行該輔助業務
需要在方法執行之前之后異常時執行該輔助業務
上面需要搞清的時后置通知和返回通知
返回通知(after-returning
):當核心業務代碼執行完成后執行,發生異常不執行
后置通知(after
):不論目標方法是否發生異常都會執行,若無異常,則執行順序在返回通知之后
動態代理(InvocationHandler
):JDK原生的實現方式,需要被代理的目標類必須實現接口。因為這個技術要求代理對象和目標對象實現同樣的接口(兄弟兩個拜把子模式)。
cglib
:通過繼承被代理的目標類(認干爹模式)實現代理,所以不需要目標類實現接口。
AspectJ
:本質上是靜態代理,將代理邏輯“織入”被代理的目標類編譯得到的字節碼文件,所以最終效果是動態的。weaver就是織入器。Spring只是借用了AspectJ中的注解。
在maven的pom.xml中加入如下代碼
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>Spring-AOP</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.14</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.14</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.14</version> </dependency> <!--在使用這個代碼的時候,我用IDEA沒有代碼提示,并且寫完會爆紅色,直接同步即可,不--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> </project>
因為我們要使用的是AspectJ中的注解,所以需要導入
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency>
springconfig
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <aop:aspectj-autoproxy/> <context:component-scan base-package="com"/> </beans>
測試類
@RunWith(SpringJUnit4ClassRunner.class)//這個需要spring-test依賴,使用后不需要創建IOC容器 @ContextConfiguration(value = "classpath:applicationContext.xml") public class AOPTEST { @Autowired private Calc calc; @Test public void testAnnotationAOP(){ int add=calc.add(10,0); System.out.println("外部 add"+add); } }
這篇文章我們先用有接口的形式來寫切面類
文件結構
前置通知(Before
):在目標方法執行之前執行某段代碼
后置通知(AfterReturning
):在目標方執行完成后執行,如果目標方法異常,則后置通知不再執行某段代碼
異常通知(Afterthrowing
):目標方法拋出異常的時候執行某段代碼
最終通知(After
);不管目標方法是否有異常都會執行,相當于try…catch…finally中的finally。
環繞通知(Around
):可以控制目標方法是否執行
不需要再核心代碼內部添加多余的代碼,而是在調用核心代碼前、后、拋異常、結束時調用某部分代碼。
這里涉及到了反射的知識,因為這些通知的實現底層就是動態代理或cglib。簡單來說,就是在調用核心代碼前,調用的方法會被攔截下來,然后執行切面類中的某段代碼。
首先要知道一點,切面類可以對很多方法或者很多類切面,主要看你想實現怎么樣的功能。比如說我們想在方法執行之前調用日志功能,那么我們要把這些方法在執行之前“切開”,然后在方法內“加入”日志輸出。因為這些事情都是切面類做的,所以才有這樣的名稱。
切面類
@Aspect @Component public class LogAspect { //前置通知 @Before(value = "execution(public int com.Calc.add(int ,int ))") public void printLogBefore(){ System.out.println("[AOP前置通知]方法開始了"); } //后置通知 @AfterReturning(value = "execution(public int com.Calc.add(int ,int ))") //在返回通知中獲取目標方法返回值分為兩步,給returning設置一個名稱,然后使用該名稱在通知方法中聲明一個對應的形參 public void printLogAfterSuccess(){ System.out.println("[AOP返回通知]方法成功返回了"); } //異常通知 @AfterThrowing(value ="execution(public int com.Calc.add(int ,int ))") public void printLogAfterException(){ System.out.println("[AOP異常通知]方法拋出異常"); } //結束通知 @After("execution(public int com.Calc.add(int ,int ))") public void printLogFinish(){ System.out.println("[AOP結束通知]方法結束了"); } }
再來看看測試方法
@Test public void testAnnotationAOP(){ int add=calc.add(10,0);//調用 System.out.println("外部 add"+add); }
結果:
可以看見,切面類成功在Calculator中實現了日志功能
關于Java SpringAOP切面類該如何理解就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。