AspectJ 是一個基于 Java 語言的 AOP 框架,它擴展了 Java 語言。Spring 2.0 以后,新增了對 AspectJ 方式的支持,新版本的 Spring 框架,建議使用 AspectJ 方式開發(fā) AOP。
使用 AspectJ 開發(fā) AOP 通常有兩種方式:
基于 XML 的聲明式。基于 Annotation 的聲明式。
基于XML的聲明式
基于 XML 的聲明式是指通過 Spring 配置文件的方式定義切面、切入點及聲明通知,而所有的切面和通知都必須定義在 aop:config 元素中。
下面通過案例演示 Spring 中如何使用基于 XML 的聲明式實現(xiàn) AOP 的開發(fā)。
1. 導(dǎo)入 JAR 包
使用 AspectJ 除了需要導(dǎo)入 Spring AOP 的 JAR 包以外,還需要導(dǎo)入與 AspectJ 相關(guān)的 JAR 包,具體如下。
spring-aspects-3.2.13.RELEASE.jar:Spring 為 AspectJ 提供的實現(xiàn),在 Spring 的包中已經(jīng)提供。
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar:是 AspectJ 提供的規(guī)范,可以在官方網(wǎng)址 https://repo.spring.io/webapp/#/search/quick/ 中搜索并下載。
2. 創(chuàng)建切面類 MyAspect
在 src 目錄下創(chuàng)建一個名為 com.mengma.aspectj.xml 的包,在該包下創(chuàng)建切面類 MyAspect,編輯后如下所示。
package com
.mengma
.aspectj
.xml
;import org
.aspectj
.lang
.JoinPoint
;
import org
.aspectj
.lang
.ProceedingJoinPoint
;
public class MyAspect {public void myBefore(JoinPoint joinPoint
) {System
.out
.print("前置通知,目標(biāo):");System
.out
.print(joinPoint
.getTarget() + "方法名稱:");System
.out
.println(joinPoint
.getSignature().getName());}public void myAfterReturning(JoinPoint joinPoint
) {System
.out
.print("后置通知,方法名稱:" + joinPoint
.getSignature().getName());}public Object
myAround(ProceedingJoinPoint proceedingJoinPoint
)throws Throwable
{System
.out
.println("環(huán)繞開始"); Object obj
= proceedingJoinPoint
.proceed(); System
.out
.println("環(huán)繞結(jié)束"); return obj
;}public void myAfterThrowing(JoinPoint joinPoint
, Throwable e
) {System
.out
.println("異常通知" + "出錯了" + e
.getMessage());}public void myAfter() {System
.out
.println("最終通知");}
}
上述代碼中,分別定義了幾種不同的通知類型方法,在這些方法中,通過 JoinPoint 參數(shù)可以獲得目標(biāo)對象的類名、目標(biāo)方法名和目標(biāo)方法參數(shù)等。需要注意的是,環(huán)繞通知必須接收一個類型為 ProceedingJoinPoint 的參數(shù),返回值必須是 Object 類型,且必須拋出異常。異常通知中可以傳入 Throwable 類型的參數(shù),用于輸出異常信息。
3. 創(chuàng)建 Spring 配置文件
在 com.mengma.aspectj.xml 包下創(chuàng)建 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"xsi
:schemaLocation
=" http
://www
.springframework
.org
/schema
/beanshttp
://www
.springframework
.org
/schema
/beans
/spring
-beans
-2.5.xsd http
://www
.springframework
.org
/schema
/aophttp
://www
.springframework
.org
/schema
/aop
/spring
-aop
-2.5.xsd"
><!--目標(biāo)類
--><bean id
="customerDao" class="com.mengma.dao.CustomerDaoImpl" /><!--切面類
--><bean id
="myAspect" class="com.mengma.aspectj.xml.MyAspect"></bean
><!--AOP 編程
--><aop
:config
><aop
:aspect ref
="myAspect"><!-- 配置切入點,通知最后增強哪些方法
--><aop
:pointcut expression
="execution ( * com.mengma.dao.*.* (..))"id
="myPointCut" /><!--前置通知,關(guān)聯(lián)通知 Advice和切入點PointCut
--><aop
:before method
="myBefore" pointeut
-ref
="myPointCut" /><!--后置通知,在方法返回之后執(zhí)行,就可以獲得返回值returning 屬性
--><aop
:after
-returning method
="myAfterReturning"pointcut
-ref
="myPointCut" returning
="returnVal" /><!--環(huán)繞通知
--><aop
:around method
="myAround" pointcut
-ref
="myPointCut" /><!--拋出通知:用于處理程序發(fā)生異常,可以接收當(dāng)前方法產(chǎn)生的異常
--><!-- *注意:如果程序沒有異常,則不會執(zhí)行增強
--><!-- * throwing屬性:用于設(shè)置通知第二個參數(shù)的名稱,類型Throwable
--><aop
:after
-throwing method
="myAfterThrowing"pointcut
-ref
="myPointCut" throwing
="e" /><!--最終通知:無論程序發(fā)生任何事情,都將執(zhí)行
--><aop
:after method
="myAfter" pointcut
-ref
="myPointCut" /></aop
:aspect
></aop
:config
>
</beans
>
上述代碼中,首先在第 4、7、8 行代碼中分別導(dǎo)入了 AOP 的命名空間。第 12 行代碼指定了切面類。
第 17、18 行代碼配置了切入點,通知需要增強哪些方法,expression="execution(com.mengma.dao..*(…))的意思是增強 com.mengma.dao 包下所有的方法。
第 20~32 行代碼用于關(guān)聯(lián)通知(Advice)和切入點(PointCut)。以第 20 行代碼前置通知為例,aop:before 標(biāo)簽的 method 屬性用于指定通知,pointcut-ref 屬性用于指定切入點,也就是要增強的方法。
4. 創(chuàng)建測試類
在 com.mengma.aspectj.xml 包下創(chuàng)建測試類 XMLTest,如下所示。
package com
.mengma
.aspectj
.xml
;import org
.junit
.Test
;
import org
.springframework
.context
.ApplicationContext
;
import org
.springframework
.context
.support
.ClassPathXmlApplicationContext
;
import com
.mengma
.dao
.CustomerDao
;public class XMLTest {@Testpublic void test() {String xmlPath
= "com/mengma/aspectj/xml/applicationContext.xml";ApplicationContext applicationContext
= new ClassPathXmlApplicationContext(xmlPath
);CustomerDao customerDao
= (CustomerDao
) applicationContext
.getBean("customerDao");customerDao
.add();}
}
5. 運行項目并查看結(jié)果
使用 JUnit 測試運行 test() 方法,運行成功后,控制臺的輸出結(jié)果如下圖所示。
為了更好地演示異常通知,接下來在 CustomerDaoImpl 類的 add() 方法中添加一行會拋出異常的代碼,如“int i=1/0;”,重新運行 XMLTest 測試類,可以看到異常通知執(zhí)行了。
基于 Annotation 的聲明式
在 Spring 中,盡管使用 XML 配置文件可以實現(xiàn) AOP 開發(fā),但是如果所有的相關(guān)的配置都集中在配置文件中,勢必會導(dǎo)致 XML 配置文件過于臃腫,從而給維護和升級帶來一定的困難。
為此,AspectJ 框架為 AOP 開發(fā)提供了另一種開發(fā)方式——基于 Annotation 的聲明式。AspectJ 允許使用注解定義切面、切入點和增強處理,而 Spring 框架則可以識別并根據(jù)這些注解生成 AOP 代理。
Annotation 注解介紹
名稱說明
| @Aspect | 用于定義一個切面。 |
| @Before | 用于定義前置通知,相當(dāng)于 BeforeAdvice。 |
| @AfterReturning | 用于定義后置通知,相當(dāng)于 AfterReturningAdvice。 |
| @Around | 用于定義環(huán)繞通知,相當(dāng)于MethodInterceptor。 |
| @AfterThrowing | 用于定義拋出通知,相當(dāng)于ThrowAdvice。 |
| @After | 用于定義最終final通知,不管是否異常,該通知都會執(zhí)行。 |
| @DeclareParents | 用于定義引介通知,相當(dāng)于IntroductionInterceptor。 |
下面使用注解的方式重新實現(xiàn)基于XML的聲明式部分的功能。
1. 創(chuàng)建切面類 MyAspect
在 src 目錄下創(chuàng)建一個名為 com.mengma.aspectj.annotation 的包,在該包下創(chuàng)建一個切面類 MyAspect,如下所示。
package com
.mengma
.aspectj
.annotation
;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
.stereotype
.Component
;
@Aspect
@Component
public class MyAspect {@Pointcut("execution(*com.mengma.dao..*.*(..))")private void myPointCut() {}@Before("myPointCut()")public void myBefore(JoinPoint joinPoint
) {System
.out
.print("前置通知,目標(biāo):");System
.out
.print(joinPoint
.getTarget() + "方法名稱:");System
.out
.println(joinPoint
.getSignature().getName());}@AfterReturning(value
= "myPointCut()")public void myAfterReturning(JoinPoint joinPoint
) {System
.out
.print("后置通知,方法名稱:" + joinPoint
.getSignature().getName());}@Around("myPointCut()")public Object
myAround(ProceedingJoinPoint proceedingJoinPoint
)throws Throwable
{System
.out
.println("環(huán)繞開始"); Object obj
= proceedingJoinPoint
.proceed(); System
.out
.println("環(huán)繞結(jié)束"); return obj
;}@AfterThrowing(value
= "myPointCut()", throwing
= "e")public void myAfterThrowing(JoinPoint joinPoint
, Throwable e
) {System
.out
.println("異常通知" + "出錯了" + e
.getMessage());}@After("myPointCut()")public void myAfter() {System
.out
.println("最終通知");}
}
上述代碼中,第 13 行 @Aspect 注解用于聲明這是一個切面類,該類作為組件使用,所以要添加 @Component 注解才能生效。第 19 行中 @Poincut 注解用于配置切入點,取代 XML 文件中配置切入點的代碼。
在每個通知相應(yīng)的方法上都添加了注解聲明,并且將切入點方法名“myPointCut”作為參數(shù)傳遞給要執(zhí)行的方法,如需其他參數(shù)(如異常通知的異常參數(shù)),可以根據(jù)代碼提示傳遞相應(yīng)的屬性值。
2. 為目標(biāo)類添加注解
在 com.mengma.dao.CustomerDaoImpl 目標(biāo)類中添加注解 @Repository(“customerDao”)。
3. 創(chuàng)建Spring配置文件
在 com.mengma.aspectj.annotation 包下創(chuàng)建 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
/beanshttp
://www
.springframework
.org
/schema
/beans
/spring
-beans
-2.5.xsdhttp
://www
.springframework
.org
/schema
/aophttp
://www
.springframework
.org
/schema
/aop
/spring
-aop
-2.5.xsdhttp
://www
.springframework
.org
/schema
/contexthttp
://www
.springframework
.org
/schema
/context
/spring
-context
.xsd"
><!--掃描含com
.mengma包下的所有注解
--><context
:component
-scan base
-package="com.mengma"/><!-- 使切面開啟自動代理
--><aop
:aspectj
-autoproxy
></aop
:aspectj
-autoproxy
>
</beans
>
上述代碼中,首先導(dǎo)入了 AOP 命名空間及其配套的約束,使切面類中的 @AspectJ 注解能夠正常工作;第 13 行代碼添加了掃描包,使注解生效。需要注意的是,這里還包括目標(biāo)類 com.mengma.dao.CustomerDaoImpl 的注解,所以 base-package 的值為 com.mengma;第 15 行代碼的作用是切面開啟自動代理。
4. 創(chuàng)建測試類
在 com.mengma.aspectj.annotation 包下創(chuàng)建一個名為 AnnotationTest 的測試類,如下所示。
package com
.mengma
.aspectj
.annotation
;import org
.junit
.Test
;
import org
.springframework
.context
.ApplicationContext
;
import org
.springframework
.context
.support
.ClassPathXmlApplicationContext
;import com
.mengma
.dao
.CustomerDao
;public class AnnotationTest {@Testpublic void test() {String xmlPath
= "com/mengma/aspectj/xml/applicationContext.xml";ApplicationContext applicationContext
= new ClassPathXmlApplicationContext(xmlPath
);CustomerDao customerDao
= (CustomerDao
) applicationContext
.getBean("customerDao");customerDao
.add();}
}
5. 運行項目并查看結(jié)果
使用 JUnit 測試運行 test() 方法,運行成功后,控制臺的輸出結(jié)果如下圖所示。
刪除 add() 方法中的“int i=1/0;”,重新運行 test() 方法,此時控制臺的輸出結(jié)果如下圖所示。
從輸出結(jié)果中可以看出,已成功使用 Annotation 的方式實現(xiàn)了 AOP 開發(fā)。與其他方式相比,基于 Annotation 方式實現(xiàn) AOP 的效果是最方便的方式。
總結(jié)
以上是生活随笔為你收集整理的Spring使用AspectJ开发AOP的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。