<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
當一個方法的執行被多個切面共同切的時候,環繞通知隻影響當前切面的通知順序,例如建立兩個切面logUtil,validateUtil兩個切面共同監視計算器類的加法運算,add(int a,int b);測試中,看切面工具類的名稱首字母,預設情況下a-z執行順序,所以這個時候logUtil切面通知比validateUtil先執行通知;
所以順序是:L的前置通知 -->v的前置通知–>執行add方法,然後v的後置通知–>V的後置返回–>L的後置通知–>L的後置返回。
但是當logUtil中加入了環繞通知,所以環繞通知要比logUtil的普通通知先執行,環繞通知功能很強大,在通過反射執行方法的前面我們可以更改這個方法的引數,但是普通通知不能這麼做。雖然在logUtil加了環繞通知,但是這個環繞通知只是比logUtil的普通通知先執行無論是進入切面前還是出切面時,他並不影響validateUtil這個切面的普通通知的執行順序,所以加了環繞通知執行順序是
環繞前置–> log前置–>va前置–>目標方法–>va後置–>va返回–>環繞返回通知–>環繞後置–>log後置–>log返回。
圖:
這裡的validate切面就是圖中的VaAspect;
對啦,可以更改預設的切面順序,要在將要更改的切面類上加入@order(int value)註解,value預設值很大,超級大,越大執行的優先順序越低,所以如果把它調成1就是先執行這個切面的通知。
回憶基於註解的AOC設定
不使用註解實現AOP設定。
1.切面類
public class LogUtil { public void performance(){} public void logStart(JoinPoint joinPoint) { //獲取方法上的參數列 Object[] args = joinPoint.getArgs(); //獲取方法簽名 Signature signature = joinPoint.getSignature(); String name = signature.getName();//獲取方法名 System.out.println("前置通知:"+name+" 方法開始執行了....引數是:"+ Arrays.asList(args) +""); } public void logReturn(JoinPoint point,Object result) { String name = point.getSignature().getName(); Object[] args = point.getArgs(); System.out.println("返回通知: "+name+"方法正常執行,返回結果是:"+result+""); } public void logException(JoinPoint point,Exception e) { String name = point.getSignature().getName(); System.out.println("異常通知:"+name+" 方法出現了異常,異常是 "+e+"..."); } public void logEnd(JoinPoint joinPoint) { String name = joinPoint.getSignature().getName(); System.out.println("後置通知:"+name+"方法結束了"); } //環繞通知 public Object myAround(ProceedingJoinPoint proceedingJoinPoint){ Object proceed = null; //獲取方法名 String name = proceedingJoinPoint.getSignature().getName(); //獲取執行方法的參數列 Object[] args = proceedingJoinPoint.getArgs(); try { System.out.println("環繞前置通知:"+name+"方法開始執行了,引數是"+Arrays.asList(args)+""); //等於 method.invoke();通過反射執行指定方法 proceed = proceedingJoinPoint.proceed(); System.out.println("環繞返回通知:"+name+"方法返回結果是"+proceed+";"); } catch (Throwable throwable) { System.out.println("環繞異常通知:異常是"+throwable+""); throwable.printStackTrace(); }finally { System.out.println("環繞後置通知:"+name+"方法結束了"); } return proceed; }
2.被切入的類(這裡是一個計算器類)
package main.java.cn.zixue.domain;public class MyCalculator { public int add(int a,int b) { return a+b; } public int sub(int a,int b) { return a-b; } public int mul(int a,int b) { return a*b; } public int dev(int a,int b) { return a/b; } public double add(double a,float b,int c) { return a+b+c; } }
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: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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="main.java.cn"></context:component-scan> <bean id="myCalculator" class="main.java.cn.zixue.domain.MyCalculator"></bean> <bean id="logUtil" class="main.java.cn.zixue.utils.LogUtil"></bean> <!--AOP名稱空間--> <aop:config> <!-- 制定切面的方法--> <aop:pointcut id="performance" expression="execution(public * main.java.cn.zixue.domain.MyCalculator.*(..))"></aop:pointcut> <!--指定切面--> <aop:aspect ref="logUtil"> <aop:after method="logEnd" pointcut-ref="performance"></aop:after> <aop:before method="logStart" pointcut-ref="performance"></aop:before> <aop:after-returning method="logReturn" pointcut-ref="performance" returning="result"></aop:after-returning> <aop:after-throwing method="logException" pointcut-ref="performance" throwing="e"></aop:after-throwing> <aop:around method="myAround" pointcut-ref="performance"></aop:around> </aop:aspect> </aop:config> </beans>
4.測試結果
@Test public void Test02() { MyCalculator myCalculator = (MyCalculator) context.getBean("myCalculator"); myCalculator.add(1,10); System.out.println("========================"); }
前置通知:add 方法開始執行了…引數是:[1, 10]
環繞前置通知:add方法開始執行了,引數是[1, 10]
環繞返回通知:add方法返回結果是11;
環繞後置通知:add方法結束了
返回通知: add方法正常執行,返回結果是:11
後置通知:add方法結束了
====================**
普通前置通知->環繞通知->環繞返回->環繞後置->普通返回->普通後置
註解和組態檔在什麼時候使用?該如何選擇?
註解的優點:設定快速簡潔。
組態檔的優點:功能豐富,註解有的他都可以實現,註解沒有的他也有。
當遇到重要的切面時,用組態檔寫,例如許可權驗證及管理。對於常用的普通的切面就用註解。
public Object aop(Method method,Object object) { try { try { /*doAround start*/ doBefore(); method.invoke(object); /*doAround end*/ } finally { doAfter(); } doAfterReturning(); } catch (Exception e) { doAfterThrowing(); } }
切面之間使用older註解,區分呼叫順序,Order值越小,那麼切面越先執行(越後結束).
不指定Order,那麼Order是預設值->Integer.MAX_VALUE. 如果Order相同,則是按照切面字母的順序來執行切面.比如@Transactional和@Cacheable->對應的切面是TransactionInterceptor和CacheInterceptor,則先執行@Cacheable的切面
@Transactional也是通過切面實現,Order值是Integer.MAX_VALUE。(如果在service方法上同時新增帶older的紀錄檔註解,在紀錄檔切面after裡面報錯,不會回滾事務)
1.方法A呼叫同類中的方法B,方法B上的切面不會生效
問題範例:
@Component public class StrategyService extends BaseStrategyService { public PricingResponse getFactor(Map<String, String> pricingParams) { // 做一些引數校驗,以及異常捕獲相關的事情 return this.loadFactor(tieredPricingParams); } @Override @StrategyCache(keyName = "key0001", expireTime = 60 * 60 * 2) private PricingResponse loadFactor(Map<String, String> pricingParams) { //程式碼執行 } }
原因:Spring的AOP是通過代理物件呼叫,只有這種呼叫方式,才能夠在真正的物件的執行前後,能夠讓代理物件也執行相關程式碼,才能起到切面的作用。而對於上面使用this的方式呼叫,這種只是自呼叫,並不會使用代理物件進行呼叫,也就無法執行切面類。
public static void main(String[] args) { ProxyFactory factory = new ProxyFactory(new SimplePojo()); factory.addInterface(Pojo.class); factory.addAdvice(new RetryAdvice()); Pojo pojo = (Pojo) factory.getProxy(); // this is a method call on the proxy! pojo.foo(); }
解決方法:使用AopContext.currentProxy()獲取到代理物件,然後通過代理物件呼叫對應的方法。還有個地方需要注意,以上方式還需要將Aspect的expose-proxy設定成true。
@Component public class StrategyService{ public PricingResponse getFactor(Map<String, String> pricingParams) { // 做一些引數校驗,以及異常捕獲相關的事情 // 這裡不使用this.loadFactor而是使用AopContext.currentProxy()呼叫,目的是解決AOP代理不支援方法自呼叫的問題 if (AopContext.currentProxy() instanceof StrategyService) { return ((StrategyService)AopContext.currentProxy()).loadFactor(tieredPricingParams); } else { // 部分實現沒有被代理過,則直接進行自呼叫即可 return loadFactor(tieredPricingParams); } } @Override @StrategyCache(keyName = "key0001", expireTime = 60 * 60 * 2) private PricingResponse loadFactor(Map<String, String> oricingParams) { //程式碼執行 } } //還有個地方需要注意,以上方式還需要將Aspect的expose-proxy設定成true。如果是組態檔修改: <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/> //如果是SpringBoot,則修改應用啟動入口類的註解: @EnableAspectJAutoProxy(exposeProxy = true) public class Application { }
以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45