您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何在Spring AOP中使用AspectJ,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
AspectJ是一個基于Java語言的AOP框架,Spring2.0以后新增了對AspectJ切點表達式支持。因為Spring1.0的時候Aspectj還未出現;
AspectJ1.5中新增了對注解的支持,允許直接在Bean類中定義切面。新版本的Spring框架建
議我們都使用AspectJ方式來開發AOP,并提供了非常靈活且強大的切點表達式 ;
當然無論使用Spring自己的AOP還是AspectJ相關的概念都是相同的;
注解配置
依賴導入:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.2.RELEASE</version> </dependency>
通知類型
@AspectJ提供的通知類型:
@Before 前置通知 在原始方法執行前執行
@AfterReturning 后置通知 在原始方法執行前執行
@Around 環繞通知 徹底攔截原始方法的執行,執行前后都可以增加邏輯,也可以不執行原始方法
@AfterThrowing拋出通知,執行原始方法出現異常時執行
@After 最終final通知,不管是否異常,原始方法調用后都會執行
@DeclareParents 引介通知,相當于IntroductionInterceptor (了解即可)
定義切點
通過execution函數來定義切點
語法:execution(訪問修飾符 返回類型 方法名 參數 異常)
表達式示例:
匹配所有類public方法:execution(public * *(..))第一個*表示返回值 ..表示任意個任意類型參數
匹配指定包下所有方法: execution(* cn.xxx.dao.*(..)) 第一個想*表示忽略權限和返回值類型
匹配指定包下所有方法:execution(* cn.xxx.dao..*(..))包含子包
匹配指定類所有方法: execution(* cn.xxx.service.UserService.*(..))
匹配實現特定接口所有類方法 : 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; @Aspect public class MyAspect { //表示PersonDao下所有方法都作為切點 @Before(value = "execution(* com.yh.demo1.PersonDao.*(..))") public void beforeAdvice(){ System.out.println("before code run....."); } }
當我們需要獲取切點信息(被增強的代碼)時,可以在通知添加參數,想下面這樣
@Aspect public class MyAspect { @Before(value = "execution(* com.yh.demo1.PersonDao.*(..))") public void beforeAdvice2(JoinPoint point){ System.out.println("before code run2....." + point); } }
后置通知:
//當需要獲取原始方法的返回值時可以在注解中添加returning參數來指定參數名 Aspectj會自動將返回值放到參數中 @AfterReturning(value = "execution(* com.yh.demo1.PersonDao.delete(..))",returning = "result") public void afterAdvice(Object result){ System.out.println("刪除方法執行后 ..... 返回值為:"+ result); }
后置通知可以獲取目標方法的返回值
環繞通知:
@Around(value = "execution(* com.yh.demo1.PersonDao.insert(..))") public void aroundAdvice(ProceedingJoinPoint point) throws Throwable { //code............ System.out.println("環繞前置.."); //執行原始方法 __當需要獲取返回值時可以聲明變量接收 Object result = point.proceed(); System.out.println("原始方法返回值: "+result); //code............ System.out.println("環繞后置.."); }
環繞通知與其他通知最大的區別在于環繞通知可以控制是否調用原始方法
注意:參數類型必須為ProceedingJoinPoint,否則 無法執行原始方法,
異常通知
@AfterThrowing(value = "execution(* com.yh.demo1.PersonDao.save(..))",throwing = "e") public void exceptionHandler(JoinPoint point,Exception e){ System.out.println(point + " 方法出現"+e.getMessage()+"異常"); }
當方法中出現時才會執行該通知,若需要獲取異常信息,可在注解中添加throwing指定參數名稱
我們可以使用環繞+異常通知來處理數據庫事務,在環繞中開啟事務以及提交事務,異常通知中回滾事務,當然Spring已經對事務進行了封裝不需要自己寫
最終通知
@After(value = "execution(* *delete(..))") public void afterRun(){ System.out.println("最終"); }
最終通知叫做after 即調用原始方法之后執行無論原始方法中是否出現異常
而后置叫做afterReturning表示在成功返回后才會執行執行
帶有邏輯符的表達式:
在表達式中可以使用戶邏輯操運算符,與&& 或|| 非!
示例:
/* 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表達式,且后續要修改切點時則多個通知都需要修改,維護起來非常麻煩,我們可以通過給切點指定名稱從而完成對切點的重復使用和統一操作,以提高開發維護效率;
//定義命名切點 方法名稱即切點名稱 @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("環繞前"); point.proceed(); System.out.println("環繞后");
一個通知應用到多個切點
//同一個通知對應多個切點 @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> <!--入侵式通知 即通知需要實現指定接口 兩種不能同時使用 --> <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("環繞前"); point.proceed(); System.out.println("環繞后"); } }
關于如何在Spring AOP中使用AspectJ就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。