目錄
5.2、切面類:編寫多個通知,採用aspectj 通知名稱任意(方法名任意)
5.4、aop編程:將通知應用到目標類(增強)(配置文件)和測試結果
1、介紹
- AspectJ是一個基於Java語言的AOP框架
- Spring2.0以後新增加了對AspectJ切入點表達式支持
- @Aspect是AspectJ1.5新增功能,通過JDK5註解技術,允許直接在Bean類中定義切面
- 新版本Spring框架,建議使用AspectJ方式來開發AOP
- 主要用途:自定義開發
2、切入點表達式【掌握】
2.1、execution()
用於描述方法
語法:execution(修飾符 返回值 包.方法(參數) throws異常)
- 修飾符:一般省略
- public 公共方法
- * 任意
- 返回值
- void 沒有返回值
- String 返回字符串
- * 任意
- 包 [可以省略 ]
- com.itheima.crm 固定包
- com.itheima.crm.*.service crm包下面子包任意(例如:com.itheima.crm.staff.service)
- com.itheima.crm.. crm包下面的所有子包(含自己的)
- com.itheima.*.service.. crm包下面任意的子包,固定目錄service,service目錄下的任意包
- 類 [可以省略]
- UserServiceImpl 指定類
- *Impl 以Impl結尾的所有類
- User* 以User開頭的所有類
- * 任意
- 方法 [不能省略]
- addUser 指定方法
- add* 以add開頭
- *User 以User結尾
- * 任意
- (參數)
- () 無參
- (int) 一個整型
- (int,int) 兩個整型
- (..) 參數任意
- throws [可以省略] 一般情況下不寫
綜合案例:
1、execution(*com.itheima.crm.*.service..*.*(..))返回值任意,com.itheima.crm下的子包任意,固定目錄service下的任意子包,任意實現類,任意方法名,任意參數
2、<aop:pointcut expression="execution(省略)" || execution(省略) id = "myPointCut"/>
匹配任意一個表達式都可以
2.2、within(瞭解)
匹配包或者子包中的方法
within(com.itheima.aop..*)
2.3、this(瞭解)
匹配實現接口的代理對象中的方法
this(com.itheima.aop.user.UserDAO)
2.4、target(瞭解)
匹配實現接口的目標對象中的方法
target(com.itheima.aop.user.UserDAO)
2.5、args(瞭解)
匹配參數格式符合標準的方法
args(int,int)
2.6、bean(id)(瞭解)
對指定的bean中的所有方法
bean("UserServiceId")
3、AspectJ通知類型
- aop聯盟定義的通知類型,具有特定的接口,必須實現,從而確定方法名稱
- aspectj通知類型,只定義類型名稱,以及方法格式
- 個數:6種,知道5種,掌握一種
3.1、前置通知(before)
- 在方法執行前執行,如果通知拋出異常,通知就無法執行
- 應用:各種校驗
3.1.1、方法聲明
import org.aspectj.lang.JoinPoint; public void myBefore(JoinPoint joinPoint)
- JoinPoint:用於描述連接點(目標方法),獲得當前目標方法的方法名等信息
3.1.2、配置文件
在<aop:config>標籤中配置:
<aop:before method="" pointcut-ref=""></aop:before>
- method:通知方法名
- pointcut-ref:切入點的引用,可以與其他通知共享切入點
3.2、後置通知(afterReturning)
- 在方法正常返回後執行,如果方法中拋出異常,通知無法執行。
- 可以獲得方法的返回值
3.2.1、方法聲明
import org.aspectj.lang.JoinPoint; public void myAfterReturning(JoinPoint joinPoint,Object obj)
- JoinPoint:用於描述連接點(目標方法)
- Object obj:目標方法的返回值
3.2.2、配置文件
<aop:after-returning method="" pointcut-ref="" returning=""></aop:after-returning>
- method:聲明的後置方法名
- pointcut-ref:切入點引用
- returning:聲明的後置方法中返回值名,即returning = "obj"
3.3、環繞通知(around)
- 在目標方法執行前後分別執行,可以阻止目標方法的執行,可以獲得目標方法的返回值。
- 必須手動執行目標方法
- 必須拋出
3.3.1、方法聲明
import org.aspectj.lang.ProceedingJoinPoint; public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable
- ProceedingJoinPoint :用來描述目標方法
- 返回值:Object,目標方法的返回值
3.3.2、配置文件
<aop:around method="" pointcut-ref=""></aop:around>
- method:通知方法名
- pointcut-ref:切入點的引用
3.4、拋出異常通知
- 在目標方法拋出異常後執行,如果方法沒有拋出異常,該通知就不會執行
3.4.1、方法聲明
import org.aspectj.lang.JoinPoint; public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable)
- Throwable throwable:捕獲的異常
- JoinPoint joinPoint:描述目標方法
3.4.2、配置文件
<aop:after-throwing method="" pointcut-ref="" throwing=""></aop:after-throwing>
- method:通知方法名
- pointcut-ref:切入點的引用
- throwing:與通知方法中的參數異常名相同,即爲throwable
3.5、最終通知
- 方法執行完畢後執行,無論方法中是否出現異常,最終都會執行
3.5.1、方法聲明
public void myAfter()
一般情況下沒有參數
3.5.2、配置文件
<aop:after method="" pointcut-ref=""></aop:after>
4、導入jar包
- 四個jar包:aop聯盟、spring aop實現、aspect規範、spring aspect的實現
5、基於xml
5.1、目標類:接口+實現
public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); } public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("add a user"); } @Override public String updateUser() { System.out.println("update a user"); return "測試後置方法獲得該目標方法的返回值"; } @Override public void deleteUser() { System.out.println("delete a user"); } }
5.2、切面類:編寫多個通知,採用aspectj 通知名稱任意(方法名任意)
public class MyAspect { public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知:" + joinPoint.getSignature().getName()); } public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("後置通知:" + joinPoint.getSignature().getName() + ",--->" + ret); } //環繞通知的參數必須是JoinPoint的子接口,必須要進行拋出異常 public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("********************"); System.out.println("前"); //手動執行目標方法 Object obj = proceedingJoinPoint.proceed();//這個返回值就是目標方法的返回值 System.out.println("後:--->" + obj); return obj; } public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("拋出異常通知:" + e.getMessage()); } public void myAfter(){ System.out.println("最終通知"); } }
5.3、測試類
public class TestAspectXml { @Test public void demo(){ String xmlPath = "test_d_aspect_a_xml/applicationContext.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); UserService userService = (UserService) applicationContext.getBean("userServiceId"); userService.addUser(); userService.updateUser(); userService.deleteUser(); } }
5.4、aop編程:將通知應用到目標類(增強)(配置文件)和測試結果
5.4.1、前置通知測試
<?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/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"> <!-- 1、目標類 --> <bean id = "userServiceId" class = "test_d_aspect_a_xml.UserServiceImpl"></bean> <!-- 2、切面類 (通知)--> <bean id = "myAspectId" class = "test_d_aspect_a_xml.MyAspect"></bean> <!-- 3、aop編程 <aop:aspect>:將切面類聲明成"切面",從而獲得通知(方法) ref:切面類的引用 <aop:pointcut:聲明一個切入點表達式 expression:切入點表達式 id:名稱,用於其他通知引用 --> <aop:config> <aop:aspect ref = "myAspectId"> <aop:pointcut id="myPointCut" expression="execution(* test_d_aspect_a_xml.UserServiceImpl.*(..))"/> <!-- 3.1 前置通知 <aop:before method="" pointcut="" pointcut-ref=""/> method:通知,就是方法名 pointcut:切入點表達式,此表達式只能當前通知使用 pointcut-ref:切入點的引用,可以與其他通知共享切入點 pointcut和pointcut-ref二選一即可 通知方法格式:public void myBefore(JoinPoint joinPoint) 參數1:org.aspectj.lang.JoinPoint:用於去描述連接點(目標方法),獲得當前目標方法的方法名字 --> <aop:before method="myBefore" pointcut-ref="myPointCut"></aop:before> </aop:aspect> </aop:config> </beans>
測試結果:
5.4.2、後置通知測試
配置文件:
<?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/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"> <!-- 1、目標類 --> <bean id = "userServiceId" class = "test_d_aspect_a_xml.UserServiceImpl"></bean> <!-- 2、切面類 (通知)--> <bean id = "myAspectId" class = "test_d_aspect_a_xml.MyAspect"></bean> <!-- 3、aop編程 <aop:aspect>:將切面類聲明成"切面",從而獲得通知(方法) ref:切面類的引用 <aop:pointcut:聲明一個切入點表達式 expression:切入點表達式 id:名稱,用於其他通知引用 --> <aop:config> <aop:aspect ref = "myAspectId"> <aop:pointcut id="myPointCut" expression="execution(* test_d_aspect_a_xml.UserServiceImpl.*(..))"/> <!--3.2 後置通知 <aop:after-returning method="" pointcut-ref="" returning=""></aop:after-returning> returning:通知方法第二個參數的名稱 通知方法的格式:public void myAfterReturning(JoinPoint joinPoint,Object ret) 參數1:連接點描述 參數2:Object,參數名字是由returing配置的 --> <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret"></aop:after-returning> </aop:aspect> </aop:config> </beans>
測試結果:
5.4.3、環繞通知測試
配置文件:
<?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/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"> <!-- 1、目標類 --> <bean id = "userServiceId" class = "test_d_aspect_a_xml.UserServiceImpl"></bean> <!-- 2、切面類 (通知)--> <bean id = "myAspectId" class = "test_d_aspect_a_xml.MyAspect"></bean> <!-- 3、aop編程 <aop:aspect>:將切面類聲明成"切面",從而獲得通知(方法) ref:切面類的引用 <aop:pointcut:聲明一個切入點表達式 expression:切入點表達式 id:名稱,用於其他通知引用 --> <aop:config> <aop:aspect ref = "myAspectId"> <aop:pointcut id="myPointCut" expression="execution(* test_d_aspect_a_xml.UserServiceImpl.*(..))"/> <!--3.3環繞通知 <aop:around method="" pointcut-ref=""></aop:around> 方法通知的格式:public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable 返回值:Object 參數:ProceedingJoinPoint 必須拋出異常 必須手動執行目標方法 --> <aop:around method="myAround" pointcut-ref="myPointCut"></aop:around> </aop:aspect> </aop:config> </beans>
測試結果:
5.4.4、拋出異常通知
配置文件:
<?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/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"> <!-- 1、目標類 --> <bean id = "userServiceId" class = "test_d_aspect_a_xml.UserServiceImpl"></bean> <!-- 2、切面類 (通知)--> <bean id = "myAspectId" class = "test_d_aspect_a_xml.MyAspect"></bean> <!-- 3、aop編程 <aop:aspect>:將切面類聲明成"切面",從而獲得通知(方法) ref:切面類的引用 <aop:pointcut:聲明一個切入點表達式 expression:切入點表達式 id:名稱,用於其他通知引用 --> <aop:config> <aop:aspect ref = "myAspectId"> <aop:pointcut id="myPointCut" expression="execution(* test_d_aspect_a_xml.UserServiceImpl.*(..))"/> <!-- 3.4拋出異常通知 <aop:after-throwing method="" pointcut-ref="" throwing=""></aop:after-throwing> throwing:通知方法的第二個參數的名稱 通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e) 參數1 :連接點的描述對象 參數2 :獲得異常信息,類型Throwable 參數名由throwing設置 --> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"></aop:after-throwing> </aop:aspect> </aop:config> </beans>
測試結果:
5.4.5、最終通知測試
配置文件:
<?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/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"> <!-- 1、目標類 --> <bean id = "userServiceId" class = "test_d_aspect_a_xml.UserServiceImpl"></bean> <!-- 2、切面類 (通知)--> <bean id = "myAspectId" class = "test_d_aspect_a_xml.MyAspect"></bean> <!-- 3、aop編程 <aop:aspect>:將切面類聲明成"切面",從而獲得通知(方法) ref:切面類的引用 <aop:pointcut:聲明一個切入點表達式 expression:切入點表達式 id:名稱,用於其他通知引用 --> <aop:config> <aop:aspect ref = "myAspectId"> <aop:pointcut id="myPointCut" expression="execution(* test_d_aspect_a_xml.UserServiceImpl.*(..))"/> <!--3.5最終通知 <aop:after method="" pointcut-ref=""></aop:after> --> <aop:after method="myAfter" pointcut-ref="myPointCut"></aop:after> </aop:aspect> </aop:config> </beans>
測試結果:
6、基於註解
此部分與上部分的測試相同,是將xml配置aop換成註解配置aop,僅僅給出修改後的切面類和配置文件,目標類、測試類以及測試結果與上面相同。
6.1、aop註解總結
切面:
- @Aapect:聲明切面,修飾切面類,從而獲得通知
通知
- @Before(value="切入點表達式||切入點引用"):前置通知
- @AfterReturning(value = "切入點表達式||切入點引用",returning="返回值"):後置通知
- @Around(value = ''切入點表達式||切入點引用"):環繞
- @AfterThrowing(value = "切入點表達式||切入點引用",throwing="異常名"):拋出異常通知
- @After(value = "切入點表達式||切入點引用")
注意,當參數只有一個的時候,“value”可以省略,例如:@After("切入點表達式")。
切入點:
- @PointCut("切入點表達式") 修飾方法private void xxx(){} 之後通過方法名來獲得切入點引用
6.2、配置文件
相比於xml配置aop,註解需要添加命名空間
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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"> <!--1 掃描所有的註解類--> <context:component-scan base-package="test_d_aspect_b_anno"></context:component-scan> <!--2 確定aop註解生效--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
6.3、切面類
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component @Aspect public class MyAspect { //切入點當前有效(在配置別的方法的時候,需要重新寫切入點表達式) //@Before("execution(* test_d_aspect_b_anno.UserServiceImpl.*(..))") public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知:" + joinPoint.getSignature().getName() ); } //聲明公共的切入點表達式 @Pointcut("execution(* test_d_aspect_b_anno.UserServiceImpl.*(..))") public void myPointcut(){ } /* @AfterReturning(value = "",returning = "") value: returning:返回值 */ //@AfterReturning(value = "myPointcut()",returning = "obj") public void myAfterReturning(JoinPoint joinPoint,Object obj){ System.out.println("後置方法:--->"+"後置方法獲得的目標方法的返回值:" + obj); } /* @Around(value = "") 注意:1、如果只有一個參數,value是可以省略的, 2、value如果是一個數組,使用{},若數組中只有一個值,{}可以省略 */ //@Around("myPointcut()") public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("****************"); System.out.println("前"); Object obj = proceedingJoinPoint.proceed(); System.out.println("後"); return obj; } //@AfterThrowing(value = "myPointcut()",throwing = "throwable") public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable){ System.out.println("拋出異常通知:" + throwable.getMessage()); } @After("myPointcut()") public void myAfter(){ System.out.println("最終通知"); } }