溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringAOP中AspectJ怎么用

發(fā)布時間:2021-09-23 14:23:14 來源:億速云 閱讀:162 作者:小新 欄目:編程語言

這篇文章給大家分享的是有關SpringAOP中AspectJ怎么用的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

AspectJ是一個基于Java語言的AOP框架,Spring2.0以后新增了對AspectJ切點表達式支持。因為Spring1.0的時候Aspectj還未出現(xiàn);

AspectJ1.5中新增了對注解的支持,允許直接在Bean類中定義切面。新版本的Spring框架建議我們都使用AspectJ方式來開發(fā)AOP,并提供了非常靈活且強大的切點表達式 ;

當然無論使用Spring自己的AOP還是AspectJ相關的概念都是相同的;

注解配置

依賴導入:

<dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-aspects</artifactId>  <version>5.2.2.RELEASE</version></dependency>

通知類型

@AspectJ提供的通知類型:  @Before 前置通知 在原始方法執(zhí)行前執(zhí)行  @AfterReturning 后置通知 在原始方法執(zhí)行前執(zhí)行  @Around 環(huán)繞通知 徹底攔截原始方法的執(zhí)行,執(zhí)行前后都可以增加邏輯,也可以不執(zhí)行原始方法  @AfterThrowing拋出通知,執(zhí)行原始方法出現(xiàn)異常時執(zhí)行  @After 最終final通知,不管是否異常,原始方法調(diào)用后都會執(zhí)行  @DeclareParents 引介通知,相當于IntroductionInterceptor (了解即可)

定義切點

通過execution函數(shù)來定義切點

語法:execution(訪問修飾符 返回類型 方法名 參數(shù) 異常)

表達式示例:

匹配所有類public方法:execution(public * *(..))第一個*表示返回值 ..表示任意個任意類型參數(shù)  匹配指定包下所有方法: execution(* cn.xxx.dao.*(..)) 第一個想*表示忽略權限和返回值類型  匹配指定包下所有方法:execution(* cn.xxx.dao..*(..))包含子包  匹配指定類所有方法: execution(* cn.xxx.service.UserService.*(..))  匹配實現(xiàn)特定接口所有類方法 : execution(* cn.xxx.dao.GenericDAO+.*(..))  匹配所有save開頭的方法: execution(* save*(..))

前置通知

pom依賴:

<dependencies>    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-context</artifactId>      <version>5.2.2.RELEASE</version>    </dependency>    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-aspects</artifactId>      <version>5.2.2.RELEASE</version>    </dependency>    <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-test</artifactId>      <version>5.2.2.RELEASE</version>      <scope>test</scope>    </dependency>    <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-test</artifactId>      <version>5.2.2.RELEASE</version>      <scope>test</scope>    </dependency>    <!-- https://mvnrepository.com/artifact/junit/junit -->    <dependency>      <groupId>junit</groupId>      <artifactId>junit</artifactId>      <version>4.13</version>      <scope>test</scope>    </dependency>  </dependencies></project>

xml需要添加aop名稱空間及xsd:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:aop="http://www.springframework.org/schema/aop"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    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"><!--  啟用aspectj  -->  <aop:aspectj-autoproxy/><!--  目標-->  <bean id="personDao" class="com.yh.demo1.PersonDao"/><!--  切面-->  <bean class="com.yh.demo1.MyAspect"/></beans>

test:

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class Test1 {  @Autowired  PersonDao personDao;  @Test  public void test(){    personDao.delete();    personDao.update();  }}

切面類:

import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class MyAspect {   //表示PersonDao下所有方法都作為切點  @Before(value = "execution(* com.yh.demo1.PersonDao.*(..))")  public void beforeAdvice(){    System.out.println("before code run.....");  }}

當我們需要獲取切點信息(被增強的代碼)時,可以在通知添加參數(shù),想下面這樣

@Aspectpublic class MyAspect {  @Before(value = "execution(* com.yh.demo1.PersonDao.*(..))")  public void beforeAdvice2(JoinPoint point){    System.out.println("before code run2....." + point);  }}

后置通知:

//當需要獲取原始方法的返回值時可以在注解中添加returning參數(shù)來指定參數(shù)名 Aspectj會自動將返回值放到參數(shù)中@AfterReturning(value = "execution(* com.yh.demo1.PersonDao.delete(..))",returning = "result")public void afterAdvice(Object result){  System.out.println("刪除方法執(zhí)行后 .....  返回值為:"+ result);}

后置通知可以獲取目標方法的返回值

環(huán)繞通知:

@Around(value = "execution(* com.yh.demo1.PersonDao.insert(..))")public void aroundAdvice(ProceedingJoinPoint point) throws Throwable {  //code............  System.out.println("環(huán)繞前置..");  //執(zhí)行原始方法 __當需要獲取返回值時可以聲明變量接收  Object result = point.proceed();  System.out.println("原始方法返回值: "+result);  //code............  System.out.println("環(huán)繞后置..");}

環(huán)繞通知與其他通知最大的區(qū)別在于環(huán)繞通知可以控制是否調(diào)用原始方法

注意:參數(shù)類型必須為ProceedingJoinPoint,否則 無法執(zhí)行原始方法,

異常通知

@AfterThrowing(value = "execution(* com.yh.demo1.PersonDao.save(..))",throwing = "e")public void exceptionHandler(JoinPoint point,Exception e){  System.out.println(point + " 方法出現(xiàn)"+e.getMessage()+"異常");}

當方法中出現(xiàn)時才會執(zhí)行該通知,若需要獲取異常信息,可在注解中添加throwing指定參數(shù)名稱

我們可以使用環(huán)繞+異常通知來處理數(shù)據(jù)庫事務,在環(huán)繞中開啟事務以及提交事務,異常通知中回滾事務,當然Spring已經(jīng)對事務進行了封裝不需要自己寫

最終通知

@After(value = "execution(* *delete(..))")public void afterRun(){  System.out.println("最終");}

最終通知叫做after 即調(diào)用原始方法之后執(zhí)行無論原始方法中是否出現(xiàn)異常

而后置叫做afterReturning表示在成功返回后才會執(zhí)行執(zhí)行

帶有邏輯符的表達式:在表達式中可以使用戶邏輯操運算符,與&& 或|| 非!

示例:

/*execution(* cn.xxx.service.UserDao.insert(..))||execution(* cn.xxx.service.UserDao.delete(..))execution(* cn.xxx.service.UserDao.*nsert(..))&&execution(* cn.xxx.service.UserDao.inser*(..))!execution(* cn.xxx.service.UserDao.insert(..))*/2|4切點命名

切點命名

假設有多個通知應用在同一個切點上時,我們需要重復編寫execution表達式,且后續(xù)要修改切點時則多個通知都需要修改,維護起來非常麻煩,我們可以通過給切點指定名稱從而完成對切點的重復使用和統(tǒng)一操作,以提高開發(fā)維護效率;

//定義命名切點 方法名稱即切點名稱@Pointcut(value = "execution(* com.yh.demo1.PersonDao.save(..))")private void savePointcut(){}@Pointcut(value = "execution(* com.yh.demo1.PersonDao.delete(..))")private void deletePointcut(){}

多個通知應用到同一個切點:

//使用命名切點@Before(value = "savePointcut()")public void beforeAdvice(){  System.out.println("before code run.....");}//使用命名切點@Around(value = "savePointcut()")public void beforeAdvice2(ProceedingJoinPoint point) throws Throwable {  System.out.println("環(huán)繞前");  point.proceed();  System.out.println("環(huán)繞后");

一個通知應用到多個切點

//同一個通知對應多個切點@After(value = "savePointcut()||deletePointcut()")public void afterAdvice(){  System.out.println("after code run.....");}

XML配置

XML配置所需的jar 以及各個對象之間的依賴關以及表達式的寫法都是一樣的,僅僅是換種方式來寫而已;

xml:

<!--目標-->  <bean id="studentDao" class="com.yh.demo2.StudentDao"/>    <!--通知-->  <bean id="advices" class="com.yh.demo2.XMLAdvice"/>    <!--織入信息-->  <aop:config>        <!--切點定義-->    <aop:pointcut id="select" expression="execution(* com.yh.demo2.StudentDao.select(..))"/>        <!--切面定義-->    <aop:aspect ref="advices">      <aop:before method="before" pointcut-ref="select"/>      <aop:after-returning method="afterReturning" pointcut-ref="select" returning="result"/>      <aop:after method="after" pointcut-ref="select" />      <aop:after-throwing method="exception" pointcut-ref="select" throwing="e"/>      <aop:around method="around" pointcut-ref="select"/>    </aop:aspect>       <!--入侵式通知 即通知需要實現(xiàn)指定接口 兩種不能同時使用 -->    <aop:advisor advice-ref="advice2" pointcut-ref="select"/>  </aop:config> <!--入侵式通知Bean--> <bean id="advice2" class="com.yh.demo2.XMLAdvice2"/>

通知類:

import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.JoinPoint;public class XMLAdvice {  public void before(JoinPoint pointcut){ System.out.println("前置通知 切點:"+pointcut); }  public void afterReturning(JoinPoint point,Object result){    System.out.println("后置通知 切點:"+point);  }  public void after(JoinPoint point){ System.out.println("最終通知 切點:"+point); }  public void exception(JoinPoint point,Throwable e){    System.out.println("異常通知: " + e+"切點:"+point);  }  public void around(ProceedingJoinPoint point) throws Throwable {    System.out.println("環(huán)繞前");    point.proceed();    System.out.println("環(huán)繞后");  }}

你會發(fā)現(xiàn) ,無論是XML還是注解都不需要手動指定代理,以及目標對象,Aspectj會從切點中獲取目標對象信息并自動創(chuàng)建代理;

AspectJ是目前更流行的方式,具體采用XML還是注解需要根據(jù)項目具體情況,小組協(xié)作開發(fā)推薦xml;

感謝各位的閱讀!關于“SpringAOP中AspectJ怎么用”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI