javascript
Spring AOP 五大通知类型
1.前置通知
在目標方法執行之前執行執行的通知。
前置通知方法,可以沒有參數,也可以額外接收一個JoinPoint,Spring會自動將該對象傳入,代表當前的連接點,通過該對象可以獲取目標對象 和 目標方法相關的信息。
注意,如果接收JoinPoint,必須保證其為方法的第一個參數,否則報錯。
配置方式:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"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-3.2.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.2.xsd"><context:annotation-config></context:annotation-config><context:component-scan base-package="cn.tedu.service,cn.tedu.aop"></context:component-scan><aop:config proxy-target-class="true"><!-- 配置切入點 --><aop:pointcut expression="execution(* cn.tedu.service.UserServiceImpl.addUser(..))" id="pc01"/><!-- 配置切面 --><aop:aspect ref="firstAspect"><<!-- 前置通知 --><aop:before method="before" pointcut-ref="pc01"/></aop:aspect></aop:config> </beans> package cn.tedu.service;import org.springframework.stereotype.Service; /*** UserServiceImple:目標對象*/ @Service("userService") public class UserServiceImple implements UserService {@Overridepublic void addUser(String name) {System.out.println("增加用戶。。");}@Overridepublic void updateUser() {System.out.println("修改用戶。。");}@Overridepublic void deleteUser() {System.out.println("刪除用戶。。");}@Overridepublic void query() {System.out.println("查詢用戶。。");} } package cn.tedu.aop;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.springframework.stereotype.Component; /*** FirstAspect:切面代碼*/ @Component public class FirstAspect {public void before(JoinPoint jp){ // 可以選擇額外的傳入一個JoinPoint連接點對象,必須用方法的第一個參數接收。Class clz = jp.getTarget().getClass();Signature signature = jp.getSignature(); // 通過JoinPoint對象獲取更多信息String name = signature.getName();System.out.println("1 -- before...["+clz+"]...["+name+"]...");} } package cn.tedu.test;import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;import cn.tedu.service.UserService; /*** AOPTest:測試代碼*/ public class AOPTest {@Testpublic void test01(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) context.getBean("userService");userService.addUser("cjj"); // 一個連接點 } }
執行結果:
1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]... 增加用戶。。2.環繞通知
在目標方法執行之前和之后都可以執行額外代碼的通知。
在環繞通知中必須顯式的調用目標方法,目標方法才會執行,這個顯式調用時通過ProceedingJoinPoint來實現的,可以在環繞通知中接收一個此類型的形參,spring容器會自動將該對象傳入,注意這個參數必須處在環繞通知的第一個形參位置。
**要注意,只有環繞通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。
環繞通知需要返回返回值,否則真正調用者將拿不到返回值,只能得到一個null。
環繞通知有控制目標方法是否執行、有控制是否返回值、有改變返回值的能力。
環繞通知雖然有這樣的能力,但一定要慎用,不是技術上不可行,而是要小心不要破壞了軟件分層的“高內聚 低耦合”的目標。
配置方式:
<!-- 環繞通知 --><aop:around method="around" pointcut-ref="pc1"/> public Object around(ProceedingJoinPoint jp) throws Throwable{System.out.println("1 -- around before...");Object obj = jp.proceed(); //--顯式的調用目標方法System.out.println("1 -- around after...");return obj;}運行結果:
1 -- around before... 增加用戶。。 1 -- around after...3.后置通知
在目標方法執行之后執行的通知。
在后置通知中也可以選擇性的接收一個JoinPoint來獲取連接點的額外信息,但是這個參數必須處在參數列表的第一個。
?配置方式:
<!-- 后置通知 --> <aop:after-returning method="afterReturn" pointcut-ref="pc1"/> public void afterReturn(JoinPoint jp){Class clz = jp.getTarget().getClass();Signature signature = jp.getSignature(); String name = signature.getName();System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...");}執行結果:
1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]... 1 -- around before... 增加用戶。。 1 -- around after... 1 -- afterReturn...[class cn.tedu.service.UserServiceImple]...[addUser]...在后置通知中,還可以通過配置獲取返回值
一定要保證JoinPoint處在參數列表的第一位,否則拋異常
配置方式:
<!-- 后置通知 --> <aop:after-returning method="afterReturn" pointcut-ref="pc1" returning="msg"/> public void afterReturn(JoinPoint jp, Object msg){Class clz = jp.getTarget().getClass();Signature signature = jp.getSignature(); String name = signature.getName();System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...["+msg+"]...");}執行結果:
1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]... 1 -- around before... 增加用戶。。 1 -- around after... 1 -- afterReturn...[class cn.tedu.service.UserServiceImple]...[addUser]...[cjj]...4.異常通知
在目標方法拋出異常時執行的通知
可以配置傳入JoinPoint獲取目標對象和目標方法相關信息,但必須處在參數列表第一位
另外,還可以配置參數,讓異常通知可以接收到目標方法拋出的異常對象。
配置方法:
<!-- 異常通知 --> <aop:after-throwing method="afterThrow" pointcut-ref="pc1" throwing="e"/> public void afterThrow(JoinPoint jp,Throwable e){Class clz = jp.getTarget().getClass();String name = jp.getSignature().getName();System.out.println("1afterThrow..["+clz+"]..["+name+"].."+e.getMessage());}代碼報異常后
執行結果:
1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]... 1 -- around before... 1 -- afterThrow..[class cn.tedu.service.UserServiceImple]..[addUser]../ by zero5.最終通知
是在目標方法執行之后執行的通知。
和后置通知不同之處在于,后置通知是在方法正常返回后執行的通知,如果方法沒有正常返-例如拋出異常,則后置通知不會執行。
而最終通知無論如何都會在目標方法調用過后執行,即使目標方法沒有正常的執行完成。
另外,后置通知可以通過配置得到返回值,而最終通知無法得到。
最終通知也可以額外接收一個JoinPoint參數,來獲取目標對象和目標方法相關信息,但一定要保證必須是第一個參數。
配置方式:
<!-- 最終通知 --> <aop:after method="after" pointcut-ref="pc1" /> public void after(JoinPoint jp){Class clz = jp.getTarget().getClass();String name = jp.getSignature().getName();System.out.println("1 -- after..["+clz+"]..["+name+"]...");}執行結果:
1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]... 1 -- around before... 增加用戶。。 1 -- around after... 1 -- afterReturn...[class cn.tedu.service.UserServiceImple]...[addUser]...[cjj]... 1 -- after..[class cn.tedu.service.UserServiceImple]..[addUser]... cjj源碼
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"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-3.2.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd "><context:annotation-config></context:annotation-config><context:component-scan base-package="cn.tedu.service,cn.tedu.aop"></context:component-scan><!-- proxy-target-class屬性值決定是基于接口的還是基于類的代理被創建 --><aop:config proxy-target-class="true"> <!-- 配置切入點 --><aop:pointcut expression="execution(* cn.tedu.service.UserServiceImple.addUser(..))" id="pc1"/> <!-- 配置切入面 --><aop:aspect ref="firstAspect"><!-- 前置通知 --> <aop:before method="before" pointcut-ref="pc1"/><!-- 環繞通知 --><aop:around method="around" pointcut-ref="pc1"/><!-- 后置通知 --><!-- <aop:after-returning method="afterReturn" pointcut-ref="pc1"/> --><aop:after-returning method="afterReturn" pointcut-ref="pc1" returning="msg"/><!-- 異常通知 --><aop:after-throwing method="afterThrow" pointcut-ref="pc1" throwing="e"/><!-- 最終通知 --><aop:after method="after" pointcut-ref="pc1" /></aop:aspect></aop:config></beans> applicationContext.xml package cn.tedu.service; /*** 接口*/ public interface UserService {public String addUser(String name);public void updateUser();public void deleteUser();public void query(); } UserService.java package cn.tedu.service;import org.springframework.stereotype.Service; /*** UserServiceImple:目標對象*/ @Service("userService") public class UserServiceImple implements UserService {@Overridepublic String addUser(String name) {// int i = 1/0;System.out.println("增加用戶。。");return "cjj";}@Overridepublic void updateUser() {System.out.println("修改用戶。。");}@Overridepublic void deleteUser() {System.out.println("刪除用戶。。");}@Overridepublic void query() {System.out.println("查詢用戶。。");} } UserServiceImple.java package cn.tedu.aop;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.springframework.stereotype.Component; /*** FirstAspect:切面代碼*/ @Component public class FirstAspect {public void before(JoinPoint jp){ // 可以選擇額外的傳入一個JoinPoint連接點對象,必須用方法的第一個參數接收。Class clz = jp.getTarget().getClass();Signature signature = jp.getSignature(); // 通過JoinPoint對象獲取更多信息String name = signature.getName();System.out.println("1 -- before...["+clz+"]...["+name+"]...");}public Object around(ProceedingJoinPoint jp) throws Throwable{System.out.println("1 -- around before...");Object obj = jp.proceed(); //--顯式的調用目標方法System.out.println("1 -- around after...");return obj;}public void afterReturn(JoinPoint jp, Object msg){Class clz = jp.getTarget().getClass();Signature signature = jp.getSignature(); String name = signature.getName();System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...["+msg+"]...");}public void afterThrow(JoinPoint jp,Throwable e){Class clz = jp.getTarget().getClass();String name = jp.getSignature().getName();System.out.println("1 -- afterThrow..["+clz+"]..["+name+"].."+e.getMessage());}public void after(JoinPoint jp){Class clz = jp.getTarget().getClass();String name = jp.getSignature().getName();System.out.println("1 -- after..["+clz+"]..["+name+"]...");} } FirstAspect.java package cn.tedu.test;import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;import cn.tedu.service.UserService; /*** AOPTest:測試代碼*/ public class AOPTest {@Testpublic void test01(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) context.getBean("userService");String result = userService.addUser("cjj"); // 一個連接點 System.out.println(result);} } AOPTes.java五種通知的執行順序
1.在目標方法沒有拋出異常的情況下
前置通知
環繞通知的調用目標方法之前的代碼
目標方法
環繞通知的調用目標方法之后的代碼
后置通知
最終通知
2.在目標方法拋出異常的情況下
前置通知
環繞通知的調用目標方法之前的代碼
目標方法 拋出異常 異常通知
最終通知
3.如果存在多個切面
多切面執行時,采用了責任鏈設計模式。
切面的配置順序決定了切面的執行順序,多個切面執行的過程,類似于方法調用的過程,在環繞通知的proceed()執行時,去執行下一個切面或如果沒有下一個切面執行目標方法,從而達成了如下的執行過程:
?
如果目標方法拋出異常:
五種通知的常見使用場景
| 環繞通知 | 控制事務 權限控制 |
| 后置通知 | 記錄日志(方法已經成功調用) |
| 異常通知 | 異常處理 控制事務 |
| 最終通知 | 記錄日志(方法已經調用,但不一定成功) |
轉載于:https://www.cnblogs.com/chuijingjing/p/9806651.html
總結
以上是生活随笔為你收集整理的Spring AOP 五大通知类型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux 安装配置JDK 、 MySQ
- 下一篇: 【JS】js打开新窗口与页面跳转