精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Spring aop聊點(diǎn)不一樣的東西

開發(fā) 前端
關(guān)于spring aop的文章網(wǎng)上一搜一大堆,但我想寫點(diǎn)不一樣的東西,嘗試一種全新的寫作風(fēng)格,希望您會(huì)喜歡。

[[379631]]

 前言

前幾篇文章本打算寫spring aop的,但是強(qiáng)忍著沒有寫(旁白:也有可能是沒想好該怎么寫😝),就是為了今天整個(gè)專題,因?yàn)樗莝pring中最核心的技術(shù)之一,實(shí)在太重要了。

關(guān)于spring aop的文章網(wǎng)上一搜一大堆,但我想寫點(diǎn)不一樣的東西,嘗試一種全新的寫作風(fēng)格,希望您會(huì)喜歡。


從實(shí)戰(zhàn)出發(fā)

很多文章講spring aop的時(shí)候,一開始就整一堆概念,等我們看得差不多要暈的時(shí)候,才真正進(jìn)入主題。。。

我卻相反,沒錯(cuò),先從實(shí)戰(zhàn)出發(fā)。

在spring aop還沒出現(xiàn)之前,想要在目標(biāo)方法之前先后加上日志打印的功能,我們一般是這樣做的:

  1. @Service 
  2. public  class TestService { 
  3.  
  4.     public void doSomething1() { 
  5.         beforeLog(); 
  6.         System.out.println("==doSomething1=="); 
  7.         afterLog(); 
  8.     } 
  9.  
  10.     public void doSomething2() { 
  11.         beforeLog(); 
  12.         System.out.println("==doSomething1=="); 
  13.         afterLog(); 
  14.     } 
  15.  
  16.     public void doSomething3() { 
  17.         beforeLog(); 
  18.         System.out.println("==doSomething1=="); 
  19.         afterLog(); 
  20.     } 
  21.  
  22.     public void beforeLog() { 
  23.         System.out.println("打印請(qǐng)求日志"); 
  24.     } 
  25.  
  26.     public void afterLog() { 
  27.         System.out.println("打印響應(yīng)日志"); 
  28.     } 

如果加了新doSomethingXXX方法,就需要在新方法前后手動(dòng)加beforeLog和afterLog方法。

原本相安無事的,但長此以往,總有會(huì)出現(xiàn)幾個(gè)刺頭青。

刺頭青A說:每加一個(gè)新方法,都需要加兩行重復(fù)的代碼,是不是很麻煩?

刺頭青B說:業(yè)務(wù)代碼和公共代碼是不是耦合在一起了?

刺頭青C說:如果有幾千個(gè)類中加了公共代碼,而有一天我需要?jiǎng)h除,是不是要瘋了?

spring大師們說:我們提供一套spring的aop機(jī)制,你們可以閉嘴了。

下面看看用spring aop(偷偷說一句,還用了aspectj)是如何打印日志的:

  1. @Service 
  2. public  class TestService { 
  3.  
  4.     public void doSomething1() { 
  5.         System.out.println("==doSomething1=="); 
  6.     } 
  7.  
  8.     public void doSomething2() { 
  9.         System.out.println("==doSomething1=="); 
  10.     } 
  11.  
  12.     public void doSomething3() { 
  13.         System.out.println("==doSomething1=="); 
  14.     } 
  15. @Component 
  16. @Aspect 
  17. public  class LogAspect { 
  18.  
  19.     @Pointcut("execution(public * com.sue.cache.service.*.*(..))"
  20.     public void pointcut() { 
  21.     } 
  22.  
  23.     @Before("pointcut()"
  24.     public void beforeLog() { 
  25.         System.out.println("打印請(qǐng)求日志"); 
  26.     } 
  27.  
  28.     @After("pointcut()"
  29.     public void afterLog() { 
  30.         System.out.println("打印響應(yīng)日志"); 
  31.     } 

增加了LogAspect類,在類上加了@Aspect注解。先在類中使用@Pointcut注解定義了pointcut方法,然后將beforeLog和afterLog方法移到這個(gè)類中,分別加上@Before和@After注解。

改造后,業(yè)務(wù)方法在TestService類中,而公共方法在LogAspect類中,是分離的。如果要新加一個(gè)業(yè)務(wù)方法,直接加就好,LogAspect類不用改任何代碼,新加的業(yè)務(wù)方法就自動(dòng)擁有打印日志的功能,是不是很神奇?


spring aop其實(shí)是一種橫切的思想,通過動(dòng)態(tài)代理技術(shù)將公共代碼織入到業(yè)務(wù)方法中。

這里出于5毛錢的友情,有必要溫馨提醒一下。aop是一種思想,不是spring獨(dú)有的,目前市面上比較出名的有:

  • aspectj
  • spring aop
  • jboss aop

我們現(xiàn)在主流的做法是將spring aop和aspectj結(jié)合使用,spring借鑒了AspectJ的切面,以提供注解驅(qū)動(dòng)的AOP。

此時(shí),一個(gè)黑影一閃而過。

刺頭青D問:你說的“橫切”,“動(dòng)態(tài)代理”,“織入” 是什么東東?

幾個(gè)重要的概念

根據(jù)上面spring aop的代碼,用一張圖聊聊幾個(gè)重要的概念:


  • 連接點(diǎn)(Joinpoint) 程序執(zhí)行的某個(gè)特定位置,如某個(gè)方法調(diào)用前,調(diào)用后,方法拋出異常后,這些代碼中的特定點(diǎn)稱為連接點(diǎn)。
  • 切點(diǎn)(Pointcut) 每個(gè)程序的連接點(diǎn)有多個(gè),如何定位到某個(gè)感興趣的連接點(diǎn),就需要通過切點(diǎn)來定位。
  • 通知(Advice) 增強(qiáng)是織入到目標(biāo)類連接點(diǎn)上的一段程序代碼。
  • 切面(Aspect) 切面由切點(diǎn)和通知組成,它既包括了橫切邏輯的定義,也包括了連接點(diǎn)的定義,SpringAOP就是將切面所定義的橫切邏輯織入到切面所制定的連接點(diǎn)中。
  • 目標(biāo)對(duì)象(Target) 需要被增強(qiáng)的業(yè)務(wù)對(duì)象
  • 代理類(Proxy) 一個(gè)類被AOP織入增強(qiáng)后,就產(chǎn)生了一個(gè)代理類。
  • 織入(Weaving) 織入就是將增強(qiáng)添加到對(duì)目標(biāo)類具體連接點(diǎn)上的過程。

還是那個(gè)刺頭青D說(旁邊:這位仁兄比較好學(xué)):spring aop概念弄明白了,挺簡單的。@Pointcut注解的execution表達(dá)式剛剛看得我一臉懵逼,可以再說說嗎,我請(qǐng)你吃飯?

切入點(diǎn)表達(dá)式

@Pointcut注解的execution切入點(diǎn)表達(dá),看似簡單,里面還是有些內(nèi)容的。為了更直觀一些,還是用張圖來總結(jié)一下:

該表達(dá)式的含義是:匹配訪問權(quán)限是public,任意返回值,包名為:com.sue.cache.service,下面的所有類所有方法和所有參數(shù)類型。圖中所有用*表示,比如圖中類名用.*表示的是所有類。如果具體匹配某個(gè)類,比如:TestService,則表達(dá)式可以換成:

  1. @Pointcut("execution(public * com.sue.cache.service.TestService.*(..))"

其實(shí)spring支持9種表達(dá)式,execution只是其中一種。


有哪些入口?

先說說我為什么會(huì)問這樣一個(gè)問題?

spring aop有哪些入口?說人話就是在問:spring中有哪些場(chǎng)景需要調(diào)用aop生成代理對(duì)象,難道你不好奇嗎?

入口1

AbstractAutowireCapableBeanFactory類的createBean方法中,有這樣一段代碼:

它通過BeanPostProcessor提供了一個(gè)生成代理對(duì)象的機(jī)會(huì)。具體邏輯在AbstractAutoProxyCreator類的postProcessBeforeInstantiation方法中:


說白了,需要實(shí)現(xiàn)TargetSource才有可能會(huì)生成代理對(duì)象。該接口是對(duì)Target目標(biāo)對(duì)象的封裝,通過該接口可以獲取到目標(biāo)對(duì)象的實(shí)例。

不出意外,這時(shí),又會(huì)冒出一個(gè)黑影。

刺頭青F說:這里生成代理對(duì)象有什么用呢?

有時(shí)我們想自己控制bean的創(chuàng)建和初始化,而不需要通過spring容器,這時(shí)就可以通過實(shí)現(xiàn)TargetSource滿足要求。只是創(chuàng)建單純的實(shí)例還好,如果我們想使用代理該怎么辦呢?這時(shí)候,入口1的作用就體現(xiàn)出來了。

入口2

AbstractAutowireCapableBeanFactory類的doCreateBean方法中,有這樣一段代碼:


它主要作用是為了解決對(duì)象的循環(huán)依賴問題,核心思路是提前暴露singletonFactory到緩存中。

通過getEarlyBeanReference方法生成代理對(duì)象:


它又會(huì)調(diào)用wrapIfNecessary方法:


這里有你想看到的生成代理的邏輯。

這時(shí)。。。。,你猜錯(cuò)了,黑影去吃飯了。。。

入口3

AbstractAutowireCapableBeanFactory類的initializeBean方法中,有這樣一段代碼:


它會(huì)調(diào)用到AbstractAutoProxyCreator類postProcessAfterInitialization方法:

該方法中能看到我們熟悉的面孔:wrapIfNecessary方法。從上面得知該方法里面包含了真正生成代理對(duì)象的邏輯。

這個(gè)入口,是為了給普通bean能夠生成代理用的,是spring最常見并且使用最多的入口。

下面為了加深印象,用一張圖總結(jié)一下:


jdk動(dòng)態(tài)代理 vs cglib

我猜你們對(duì)jdk動(dòng)態(tài)代理和cglib是知道的(即使猜錯(cuò)了也不會(huì)少塊肉😃),但為了照顧一下新朋友,還是有必要把這兩種生成代理的方式拿出來說說。

jdk動(dòng)態(tài)代理

jdk動(dòng)態(tài)代理是通過反射技術(shù)實(shí)現(xiàn)的,生成代理的代碼如下:

  1. public  interface IUser { 
  2.     void add(); 
  3.  
  4. public  class User implements IUser{ 
  5.     @Override 
  6.     public void add() { 
  7.         System.out.println("===add==="); 
  8.     } 
  9.  
  10. public  class JdkProxy implements InvocationHandler { 
  11.  
  12.     private Object target; 
  13.  
  14.     public Object getProxy(Object target) { 
  15.         this.target = target; 
  16.         return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); 
  17.     } 
  18.  
  19.     @Override 
  20.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  21.         before(); 
  22.         Object result = method.invoke(target,args); 
  23.         after(); 
  24.         return result; 
  25.     } 
  26.  
  27.     private void before() { 
  28.         System.out.println("===before==="); 
  29.     } 
  30.  
  31.     private void after() { 
  32.         System.out.println("===after==="); 
  33.     } 
  34.  
  35. public  class Test { 
  36.     public static void main(String[] args) { 
  37.         User user = new User(); 
  38.         JdkProxy jdkProxy = new JdkProxy(); 
  39.         IUser proxy = (IUser)jdkProxy.getProxy(user); 
  40.         proxy.add(); 
  41.     } 

首先要定義一個(gè)接口IUser,然后定義接口實(shí)現(xiàn)類User,再定義類JdkProxy實(shí)現(xiàn)InvocationHandler接口,重寫invoke方法,該方法中實(shí)現(xiàn)額外的邏輯。當(dāng)然,別忘了在getProxy方法中,用Proxy.newProxyInstance方法創(chuàng)建一個(gè)代理對(duì)象。

jdk動(dòng)態(tài)代理三個(gè)要素:

  • 定義一個(gè)接口
  • 實(shí)現(xiàn)InvocationHandler接口
  • 使用Proxy創(chuàng)建代理對(duì)象

cglib

cglib底層是通過asm字節(jié)碼技術(shù)實(shí)現(xiàn)的,生成代理的代碼如下:

  1. public  class User { 
  2.     public void add() { 
  3.         System.out.println("===add==="); 
  4.     } 
  5.  
  6. public  class CglibProxy implements MethodInterceptor { 
  7.  
  8.     private Object target; 
  9.  
  10.     public Object getProxy(Object target) { 
  11.         this.target = target; 
  12.         Enhancer enhancer = new Enhancer(); 
  13.         enhancer.setSuperclass(target.getClass()); 
  14.         enhancer.setCallback(this); 
  15.         return enhancer.create(); 
  16.     } 
  17.  
  18.     @Override 
  19.     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 
  20.         before(); 
  21.         Object result = method.invoke(target,objects); 
  22.         after(); 
  23.         return result; 
  24.     } 
  25.  
  26.     private void before() { 
  27.         System.out.println("===before==="); 
  28.     } 
  29.  
  30.     private void after() { 
  31.         System.out.println("===after==="); 
  32.     } 
  33.  
  34. public  class Test { 
  35.     public static void main(String[] args) { 
  36.         User user = new User(); 
  37.         CglibProxy cglibProxy = new CglibProxy(); 
  38.         IUser proxy = (IUser)cglibProxy.getProxy(user); 
  39.         proxy.add(); 
  40.     } 

這里不需要定義接口,直接定義目標(biāo)類User,然后實(shí)現(xiàn)MethodInterceptor接口,重寫intercept方法,該方法中實(shí)現(xiàn)額外的邏輯。當(dāng)然,別忘了在getProxy方法中,通過Enhancer創(chuàng)建代理對(duì)象。

cglib兩個(gè)要素:

  • 實(shí)現(xiàn)MethodInterceptor接口
  • 使用Enhancer創(chuàng)建代理對(duì)象

spring中如何用的?

DefaultAopProxyFactory類的createAopProxy方法中,有這樣一段代碼:


它里面包含:

  • JdkDynamicAopProxy jdk動(dòng)態(tài)代理生成類
  • ObjenesisCglibAopProxy cglib代理生成類

JdkDynamicAopProxy類的invoke方法生成的代理對(duì)象。而ObjenesisCglibAopProxy類的父類:CglibAopProxy,它的getProxy方法生成的代理對(duì)象。

哪個(gè)更好?

我猜,不是刺頭青,是你,可能會(huì)來自靈魂深處的一問:jdk動(dòng)態(tài)代理和cglib哪個(gè)更好?

其實(shí)這個(gè)問題沒有標(biāo)準(zhǔn)答案,要看具體的業(yè)務(wù)場(chǎng)景:

沒有定義接口,只能使用cglib,不說它好不行。

定義了接口,需要?jiǎng)?chuàng)建單例或少量對(duì)象,調(diào)用多次時(shí),可以使用jdk動(dòng)態(tài)代理,因?yàn)樗鼊?chuàng)建時(shí)更耗時(shí),但調(diào)用時(shí)速度更快。

定義了接口,需要?jiǎng)?chuàng)建多個(gè)對(duì)象時(shí),可以使用cglib,因?yàn)樗鼊?chuàng)建速度更快。

  • 隨著jdk版本不斷迭代更新,jdk動(dòng)態(tài)代理創(chuàng)建耗時(shí)不斷被優(yōu)化,8以上的版本中,跟cglib已經(jīng)差不多。所以spring官方默認(rèn)推薦使用jdk動(dòng)態(tài)代理,因?yàn)樗{(diào)用速度更快。

出于人道主義關(guān)懷,免費(fèi)贈(zèng)送一條有用經(jīng)驗(yàn):如果要強(qiáng)制使用cglib,可以通過以下兩種方式:

  • spring.aop.proxy-target-class=true
  • @EnableAspectJAutoProxy(proxyTargetClass = true)

五種通知

spring默認(rèn)提供了五種通知:


按照國際慣例,不,按照我個(gè)人習(xí)慣,先看看他們是怎么用的。

前置通知

該通知在方法執(zhí)行之前執(zhí)行,只需在公共方法上加@Before注解,就能定義前置通知:

  1. @Before("pointcut()"
  2. public void beforeLog(JoinPoint joinPoint) { 
  3.     System.out.println("打印請(qǐng)求日志"); 

后置通知

該通知在方法執(zhí)行之后執(zhí)行,只需在公共方法上加@After注解,就能定義后置通知:

  1. @After("pointcut()"
  2. public void afterLog(JoinPoint joinPoint) { 
  3.     System.out.println("打印響應(yīng)日志"); 

環(huán)繞通知

該通知在方法執(zhí)行前后執(zhí)行,只需在公共方法上加@Round注解,就能定義環(huán)繞通知:

  1. @Around("pointcut()"
  2. public Object around(ProceedingJoinPoint joinPoint) throws Throwable { 
  3.     System.out.println("打印請(qǐng)求日志"); 
  4.     Object result = joinPoint.proceed(); 
  5.     System.out.println("打印響應(yīng)日志"); 
  6.     return result; 

結(jié)果通知

該通知在方法結(jié)束后執(zhí)行,能夠獲取方法返回結(jié)果,只需在公共方法上加@AfterReturning注解,就能定義結(jié)果通知:

  1. @AfterReturning(pointcut = "pointcut()",returning = "retVal"
  2. public void afterReturning(JoinPoint joinPoint, Object retVal) { 
  3.     System.out.println("獲取結(jié)果:"+retVal); 

異常通知

該通知在方法拋出異常之后執(zhí)行,只需在公共方法上加@AfterThrowing注解,就能定義異常通知:

  1. @AfterThrowing(pointcut = "pointcut()", throwing = "e"
  2. public void afterThrowing(JoinPoint joinPoint, Throwable e) { 
  3.     System.out.println("異常:"+e); 

spring aop給這五種通知,分別分配了一個(gè)xxxAdvice類。在ReflectiveAspectJAdvisorFactory類的getAdvice方法中可以看得到:


下面用一張圖總結(jié)一下對(duì)應(yīng)關(guān)系:


這五種xxxAdvice類都實(shí)現(xiàn)了Advice接口,但是有些差異。

下面三個(gè)xxxAdvice類實(shí)現(xiàn)了MethodInterceptor接口:


而另外兩個(gè)類:AspectJMethodBeforeAdvice 和 AspectJAfterReturningAdvice 沒有實(shí)現(xiàn)上面的接口,這是為什么?

(這里留點(diǎn)懸念,后面的文章會(huì)揭曉謎題,敬請(qǐng)期待。)

一個(gè)猝不及防,依然是那個(gè)刺頭青D,放下碗沖過來問了句:這五種通知的執(zhí)行順序是怎么樣的?

單個(gè)切面正常情況


單個(gè)切面異常情況


多個(gè)切面正常情況


多個(gè)切面異常情況


  • 當(dāng)有多有切面時(shí),按照可以通過@Order(n)指定執(zhí)行順序,n值越小越先執(zhí)行。

為什么使用鏈?zhǔn)秸{(diào)用?

這個(gè)問題沒人問,是我自己想聊聊(旁白:因?yàn)槲议L得帥,有點(diǎn)自戀了)。

先看看spring是如何使用鏈?zhǔn)秸{(diào)用的,在ReflectiveMethodInvocation的proceed方法中,有這樣一段代碼:

下面用一張圖捋一捋上面的邏輯:


圖中包含了一個(gè)遞歸的鏈?zhǔn)秸{(diào)用,為什么要這樣設(shè)計(jì)呢?

假如不這樣設(shè)計(jì),我們代碼中是不是需要寫很多if...else,根據(jù)不同的切面和通知單獨(dú)處理?

而spring巧妙的使用責(zé)任鏈模式消除了原本需要大量的if...else判斷,讓代碼的擴(kuò)展性更好,很好的體現(xiàn)了開閉原則:對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。

緩存中存的原始還是代理對(duì)象?

我們知道spring中為了性能考慮是有緩存的,通常說包含了三級(jí)緩存:


說時(shí)遲那時(shí)快,刺頭青D的兄弟,刺頭青F忍不住趕過來問了句:緩存中存的原始還是代理對(duì)象?

我竟然被問得一時(shí)語塞,仔細(xì)捋了捋,要從三個(gè)方面回答:

singletonFactories(三級(jí)緩存)

AbstractAutowireCapableBeanFactory類的doCreateBean方法中,有這樣一段代碼:


其實(shí)之前已經(jīng)說過,它是為了解決循環(huán)依賴問題。這次要說的是addSingletonFactory方法:


它里面保存的是singletonFactory對(duì)象,所以是原始對(duì)象。

earlySingletonObjects(二級(jí)緩存)

AbstractBeanFactory類的doGetBean方法中,有這樣一段代碼:

在調(diào)用getBean方法獲取bean實(shí)例時(shí),會(huì)調(diào)用getSingleton嘗試先從緩存中看能否獲取到,如果能獲取到則直接返回。

這段代碼會(huì)先從一級(jí)緩存中獲取bean,如果沒有再從二級(jí)緩存中獲取,如果還是沒有則從三級(jí)緩存中獲取singletonFactory,通過getObject方法獲取實(shí)例,將該實(shí)例放入到二級(jí)緩存中。

答案的謎底就聚焦在getObject方法中,而這個(gè)方法又是在哪來定義的呢?

其實(shí)就是上面的getEarlyBeanReference方法,我們知道這個(gè)方法生成的是代理對(duì)象,所以二級(jí)緩存中存的是代理對(duì)象。

singletonObjects(一級(jí)緩存)

DefaultSingletonBeanRegistry類的getSingleton方法中,有這樣一段代碼:

此時(shí)的bean創(chuàng)建、注入和初始化完成了,判斷是如果新的單例對(duì)象,則會(huì)加入到一級(jí)緩存中,具體代碼如下:


出于一塊錢的友誼,有必要溫馨提醒一下:這里是DefaultSingletonBeanRegistry類的getSingleton方法,跟上面說的AbstractBeanFactory類getSingleton方法不一樣。

幾個(gè)常見的坑

我是一個(gè)樂于分享的人,雖說有時(shí)話比較少(旁邊:屬于人狠話不多的角色,別惹我)。為了表現(xiàn)我的share精神,給大家總結(jié)幾個(gè)我之前使用spring aop遇過的坑。

我們幾乎每天都在用spring aop。

“什么?我怎么不知道?” 你可能會(huì)問。

如果你每天在用spring事務(wù)的話,就是每天在用spring aop,因?yàn)閟pring事務(wù)的底層就用到了spring aop。

坑1:直接方法調(diào)用

使用spring事務(wù)時(shí),直接方法調(diào)用:

  1. @Service 
  2. public  class UserService { 
  3.  
  4.     @Autowired 
  5.     private UserMapper userMapper; 
  6.  
  7.     public void add(UserModel userModel) { 
  8.         userMapper.queryUser(userModel); 
  9.         save(userModel); 
  10.     } 
  11.  
  12.     @Transactional 
  13.     public void save(UserModel userModel) { 
  14.         System.out.println("保存數(shù)據(jù)"); 
  15.     } 

這種情況直接方法調(diào)用spring aop無法生成代理對(duì)象,事務(wù)會(huì)失效。這個(gè)問題的解決辦法有很多:

  1. 使用TransactionTemplate手動(dòng)開啟事務(wù)
  2. 將事務(wù)方法save放到新加的類UserSaveService中,通過userSaveService.save調(diào)用事務(wù)方法。
  3. UserService類中@Autowired注入自己的實(shí)例userService,通過userService.save調(diào)用事務(wù)方法。
  4. 通過AopContext類獲取代理對(duì)象:((UserService)AopContext.currentProxy()).save(user);

坑2:訪問權(quán)限錯(cuò)誤

  1. @Service 
  2. public  class UserService { 
  3.     @Autowired 
  4.     private UserService userService; 
  5.     @Autowired 
  6.     private UserMapper userMapper; 
  7.  
  8.     public void add(UserModel userModel) { 
  9.         userMapper.queryUser(userModel); 
  10.         userService.save(userModel); 
  11.     } 
  12.  
  13.     @Transactional 
  14.     private void save(UserModel userModel) { 
  15.         System.out.println("保存數(shù)據(jù)"); 
  16.     } 

上面用 UserService類中@Autowired注入自己的實(shí)例userService的方式解決事務(wù)失效問題,如果不出意外的話,是可以的。

但是恰恰出現(xiàn)了意外,save方法被定義成了private的,這時(shí)也無法生成代理對(duì)象,事務(wù)同樣會(huì)失效。

所以,我們應(yīng)該拿個(gè)小本本記一下,目標(biāo)方法一定不能定義成private的。

坑3:目標(biāo)類用final修飾

  1. @Service 
  2. public  class UserService { 
  3.     @Autowired 
  4.     private UserService userService; 
  5.     @Autowired 
  6.     private UserMapper userMapper; 
  7.  
  8.     public void add(UserModel userModel) { 
  9.         userMapper.queryUser(userModel); 
  10.         userService.save(userModel); 
  11.     } 
  12.  
  13.     @Transactional 
  14.     public final void save(UserModel userModel) { 
  15.         System.out.println("保存數(shù)據(jù)"); 
  16.     } 

這種情況spring aop生成代理對(duì)象,重寫save方法時(shí),發(fā)現(xiàn)的final的,重寫不了,也會(huì)導(dǎo)致事務(wù)失效。

小本本需要再加一條,目標(biāo)方法一定不能定義成final的。

坑4:循環(huán)依賴問題

在使用@Async注解開啟異步功能的場(chǎng)景,它會(huì)通過AOP自動(dòng)生成代理對(duì)象。

  1. @Service 
  2. public  class TestService1 { 
  3.  
  4.     @Autowired 
  5.     private TestService2 testService2; 
  6.  
  7.     @Async 
  8.     public void test1() { 
  9.     } 
  10.  
  11. @Service 
  12. public  class TestService2 { 
  13.  
  14.     @Autowired 
  15.     private TestService1 testService1; 
  16.  
  17.     public void test2() { 
  18.     } 

啟動(dòng)服務(wù)會(huì)報(bào)錯(cuò):

  1. org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testService1': Bean with name 'testService1' has been injected into other beans [testService2] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned offfor example. 

至于為什么會(huì)報(bào)錯(cuò),我在這里不過多解釋了,在我的《spring:我是如何解決循環(huán)依賴的?》這篇文章中寫的很詳細(xì)。

 

責(zé)任編輯:姜華 來源: 蘇三說技術(shù)
相關(guān)推薦

2015-08-28 09:20:07

app推廣方法

2012-03-07 17:24:10

戴爾咨詢

2012-12-20 10:17:32

IT運(yùn)維

2017-05-25 15:02:46

聯(lián)宇益通SD-WAN

2015-10-19 12:33:01

華三/新IT

2016-05-09 18:40:26

VIP客戶緝拿

2018-05-09 15:42:24

新零售

2009-12-01 16:42:27

Gentoo Linu

2009-02-04 15:43:45

敏捷開發(fā)PHPFleaPHP

2022-09-26 08:06:24

Go語言

2011-02-28 10:38:13

Windows 8

2012-05-29 15:54:40

天天聊華為

2009-06-12 15:26:02

2016-03-24 18:51:40

2019-01-03 14:39:08

Oracle甲骨文ORACLE

2022-05-05 21:47:32

Linuxls 命令

2009-07-07 10:44:14

多態(tài)

2015-08-04 14:49:54

Discover

2015-08-25 09:52:36

云計(jì)算云計(jì)算產(chǎn)業(yè)云計(jì)算政策

2013-01-11 18:10:56

軟件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

精品激情国产视频| 在线观看日韩av先锋影音电影院| 99re视频在线观看| 日本一级黄色大片| 日韩在线二区| 亚洲精品一区二区三区福利| 精品久久久久久无码国产| 精品美女在线观看视频在线观看| 不卡的av在线| 91久久精品美女| 国产精品99精品无码视| 成人亚洲一区| 日韩精品视频在线观看网址 | 黄色在线论坛| 91女人视频在线观看| 91精品在线观| 日韩xxx视频| 国产欧美另类| 欧美激情综合色综合啪啪五月| 少妇无套高潮一二三区| 99精品国产高清一区二区麻豆| 在线视频国产一区| 国产精品50p| 在线中文字幕视频观看| 国产精品天干天干在观线| 久久精品国产第一区二区三区最新章节 | 国产精品18毛片一区二区| 中文字幕在线一| 亚洲免费网站| 久久人人97超碰精品888| 麻豆精品国产免费| 久久精品国产大片免费观看| 日韩国产精品视频| 精品影片一区二区入口| 免费看日产一区二区三区| 欧美色爱综合网| 国产精品-区区久久久狼| 岛国av在线播放| 亚洲宅男天堂在线观看无病毒| 在线无限看免费粉色视频| h网站在线免费观看| 久久亚洲精精品中文字幕早川悠里 | 精品久久ai| 欧美www视频| xxxx国产视频| 欧美9999| 日韩一区二区三免费高清| 午夜大片在线观看| 国产精品久久久久久av公交车| 欧美日韩亚洲综合在线 欧美亚洲特黄一级| 无码人妻丰满熟妇区96| 精品人人视频| 欧美日韩国产中字| 丰满人妻中伦妇伦精品app| 日韩伦理在线一区| 欧美日韩国产一区在线| 亚洲熟妇av一区二区三区漫画| 久热在线观看视频| 色琪琪一区二区三区亚洲区| 国产真实乱子伦| 日韩电影免费观看高清完整版| 日韩人在线观看| 黄色片一级视频| 国产一区一一区高清不卡| 欧美性猛片aaaaaaa做受| 日韩一级免费片| 国产在线一区不卡| 欧美xxxxxxxx| 国精品无码人妻一区二区三区| 久草成人资源| 中文字幕日韩电影| 我家有个日本女人| 亚洲国产专区校园欧美| 国产91久久婷婷一区二区| 波多野结衣视频网址| 黄色日韩网站视频| 国产经典一区二区三区 | 日韩有吗在线观看| 亚洲国产毛片完整版| 天天躁日日躁aaaxxⅹ| 日韩aaaa| 久久久久久国产精品久久| 久久夜色精品亚洲| 免费高清视频精品| av在线不卡观看| 五月婷婷久久久| 国产精品美女久久久久aⅴ国产馆| 超碰免费在线公开| 91九色porn在线资源| 欧美亚洲一区二区在线观看| www.桃色.com| 亚洲精品亚洲人成在线| 久久精品视频播放| 久久青青草视频| 久久av中文字幕片| 激情小说综合网| 一本一道波多野毛片中文在线 | 国产视频aaa| 91麻豆精品在线观看| 中文字幕中文字幕在线中一区高清 | 亚洲综合图片区| 爱情岛论坛vip永久入口| 亚洲国产欧美国产第一区| 亚洲欧美激情视频| 免费一级全黄少妇性色生活片| 久久欧美肥婆一二区| 51午夜精品| 国产www.大片在线| 亚洲成人在线观看视频| 欧美性猛交xxxx乱大交91| 亚洲三级网页| 欧美激情一区二区久久久| 中文字幕在线观看你懂的| 成+人+亚洲+综合天堂| 中文字幕在线亚洲三区| 天天免费亚洲黑人免费| 亚洲爱爱爱爱爱| 亚洲综合久久av一区二区三区| 国产深夜精品| 国产高清精品一区二区三区| 国产二区三区在线| 欧美性感一区二区三区| 国产黄色网址在线观看| 激情综合中文娱乐网| 5g影院天天爽成人免费下载| 午夜看片在线免费| 欧美午夜视频网站| 日本xxxxxxxxx18| 国产欧美欧美| 久久精品magnetxturnbtih| 国产乱码在线| 日韩欧美国产三级电影视频| 国产精品99久久久久久成人| 美腿丝袜一区二区三区| 日韩在线电影一区| 午夜精品成人av| 亚洲欧美日韩国产中文| 久久精品视频1| 91原创在线视频| 内射国产内射夫妻免费频道| 久久男人av| 51精品在线观看| 神马久久久久久久久久| 亚洲福利视频导航| 无码人妻精品一区二区三| 欧美日本精品| 高清视频一区| av成人福利| 精品动漫一区二区三区在线观看| 欧美三级免费看| 国产激情一区二区三区四区 | 久热re这里精品视频在线6| 欧美午夜视频在线| **欧美日韩在线观看| 亚洲视频电影图片偷拍一区| 中国a一片一级一片| 中文字幕第一页久久| 天天综合天天添夜夜添狠狠添| 99热国内精品| 99re6热在线精品视频播放速度| 在线中文字幕-区二区三区四区| 欧美成人一级视频| 91国产丝袜播放在线| 久久香蕉国产线看观看99| 天天操天天爽天天射| 久久精品影视| 国产精品日韩一区二区免费视频| 97人人在线视频| 亚洲精品小视频| 中文字幕+乱码+中文字幕明步 | 天堂网av在线播放| 国产精品免费aⅴ片在线观看| wwww.国产| 欧美在线亚洲综合一区| 国产精品久久久对白| 欧美男人天堂| 中文国产成人精品| 国产av一区二区三区精品| 日韩欧美在线观看强乱免费| 国产成人自拍网站| 国产激情91久久精品导航| 国产精品视频二| 欧美网色网址| 国产精品精品一区二区三区午夜版 | 国产在线免费av| 国产精品一卡二卡在线观看| 鲁一鲁一鲁一鲁一色| 久久一本综合| 国产伦精品一区二区三区免费视频| 欧美电影免费观看高清完整| 日韩日本欧美亚洲| 五月天婷婷在线播放| 欧美视频中文字幕| 日韩精品成人在线| 国产精品护士白丝一区av| 国产精品入口麻豆| 久久66热偷产精品| 国产麻花豆剧传媒精品mv在线| 羞羞答答成人影院www| 久草精品电影| 国产一区二区在线观| 热99在线视频| 日本片在线看| 中文字幕亚洲国产| 人妻少妇精品无码专区久久| 欧美日韩国产首页在线观看| 国产微拍精品一区| 一级精品视频在线观看宜春院| 亚洲色成人网站www永久四虎| 国产v日产∨综合v精品视频| 色婷婷成人在线| 麻豆精品网站| 给我免费播放片在线观看| 99久久99久久精品国产片桃花| 九九九九久久久久| 国产精品17p| 亚洲一区精品电影| 狠狠久久综合| 国产mv久久久| 末成年女av片一区二区下载| 久久最新资源网| av网站在线播放| 亚洲欧美激情一区| 无套内谢的新婚少妇国语播放| 欧美一区二区三区人| 亚洲一区二区色| 欧洲av一区二区嗯嗯嗯啊| 特一级黄色大片| 夜夜嗨av一区二区三区网页| 91禁男男在线观看| 日本一区二区三区dvd视频在线| 精品黑人一区二区三区观看时间| 国产成人综合视频| 中文字幕1区2区| 国产乱码精品一区二区三区五月婷 | 免费国产一区| 九九免费精品视频在线观看| 九九九九九精品| 日韩理论电影中文字幕| 国产有色视频色综合| 风间由美一区二区av101| 国产一区自拍视频| 亚洲综合福利| 奇米精品在线| 精品美女久久| 亚洲欧美日韩不卡一区二区三区| 欧州一区二区| 亚洲一区三区视频在线观看| 国产精品91一区二区三区| 亚洲欧洲中文| 91成人看片| 成人在线视频一区二区三区| 影音先锋久久久| 欧美老熟妇喷水| 久久尤物视频| 丝袜制服一区二区三区| 蜜桃av一区二区三区电影| 国产三级精品三级在线| 国产高清在线精品| 一起草在线视频| 久久色成人在线| 国产视频123区| 成人欧美一区二区三区在线播放| 国产午夜手机精彩视频| 亚洲韩国精品一区| 国产午夜性春猛交ⅹxxx| 欧美影院一区二区三区| 国产女人18毛片18精品| 精品国产一区二区三区忘忧草| 无码精品黑人一区二区三区 | 欧美男男video| 91精品国产高清久久久久久91| 九九精品调教| 国产91在线播放九色快色| 曰本一区二区| 黄色一区三区| 97人人精品| 久久国产午夜精品理论片最新版本| 欧美亚洲视频| 亚洲综合在线一区二区| 91看片淫黄大片一级| 免费成人深夜夜行网站| 午夜国产精品影院在线观看| 国产在线一级片| 精品国产免费一区二区三区四区 | 日韩中文字幕二区| 国产主播一区二区三区| 亚洲香蕉中文网| 国产精品美女一区二区在线观看| 久久中文字幕无码| 欧美视频精品在线观看| 神马一区二区三区| 精品国产拍在线观看| 九色porny自拍视频在线观看| 国产精品普通话| 精品欧美午夜寂寞影院| 熟女熟妇伦久久影院毛片一区二区| 一级成人国产| 下面一进一出好爽视频| 国产色综合一区| 午夜影院在线看| 欧美一区二区三区免费在线看 | 久久躁狠狠躁夜夜爽| 亚洲黄色免费看| 97超级在线观看免费高清完整版电视剧| 亚洲精品456| 国产精品久久久久久久乖乖| 久久99精品久久久久| 三级网站在线免费观看| 亚洲午夜一区二区| 国产乱码一区二区| 综合国产在线视频| 国模冰冰炮一区二区| 国产精品久久精品视| 欧美 亚欧 日韩视频在线| 五月天婷婷激情视频| 久久夜色精品一区| 成年人免费高清视频| 精品剧情在线观看| 国产cdts系列另类在线观看| 国产精品亚洲аv天堂网| 亚洲精品国模| 1024av视频| 99国产精品国产精品毛片| 激情综合网五月天| 日韩一二三区视频| 老司机免费在线视频| 国产精品久久电影观看| 美日韩中文字幕| 337p粉嫩大胆噜噜噜鲁| 成人av网在线| 在线观看精品国产| 日韩精品免费在线视频观看| 国产va在线视频| 国产美女99p| 伊人久久久大香线蕉综合直播| 日批视频免费看| 亚洲一二三区在线观看| 亚洲女人18毛片水真多| 欧美裸身视频免费观看| 视频免费一区二区| 老子影院午夜伦不卡大全| 粉嫩av一区二区三区粉嫩| 久久在线视频精品| 亚洲国产精品99| 一个人看的www视频在线免费观看 一个人www视频在线免费观看 | 中文字幕欧美日韩精品| 国产精品亲子伦av一区二区三区 | 亚洲激情亚洲| 无码人妻精品一区二区三区温州| 欧美日韩国产麻豆| 免费资源在线观看| 国产精品视频自在线| 91精品精品| 久久久无码人妻精品无码| 亚洲电影在线免费观看| 天堂av网在线| 国产精品免费在线免费 | 久久99精品国产自在现线| ww国产内射精品后入国产| 久久久久久一二三区| 亚洲中文一区二区三区| 超碰日本道色综合久久综合| 激情视频极品美女日韩| 欧美极品欧美精品欧美图片| 欧美国产日韩精品免费观看| 6—12呦国产精品| 欧美激情视频一区二区| 欧美黄色录像| 最新天堂中文在线| 亚洲一区二区三区三| 激情小说 在线视频| 91久久久精品| 国产精品日韩精品欧美精品| 中字幕一区二区三区乱码| 日韩一区二区视频在线观看| 国产精品一二三产区| 亚洲 国产 日韩 综合一区| 激情五月婷婷综合网| 日本三级一区二区| 久久精品亚洲精品| 天天久久夜夜| www.国产福利| 欧美日韩国产影院| 在线播放麻豆| 国产一区二区自拍| 久久精品99国产精品| 日韩精品――中文字幕| 中文字幕欧美精品在线| 全球av集中精品导航福利| 亚洲欧美日韩精品一区| 精品国产精品自拍| 粗大黑人巨茎大战欧美成人| 免费av在线一区二区| 国产精品99久久久久久宅男| 日韩一级在线视频| 久久久久一本一区二区青青蜜月 | 免费不卡av在线| 中国av一区二区三区| 天天干在线观看|