1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        終于有人把 Spring 循環(huán)依賴講清楚了!

        共 4915字,需瀏覽 10分鐘

         ·

        2020-10-14 06:05

        Java技術(shù)棧

        www.javastack.cn

        關(guān)注閱讀更多優(yōu)質(zhì)文章



        來(lái)源:CodeBear的園子
        地址:www.cnblogs.com/CodeBear/p/13327899.html

        網(wǎng)上關(guān)于Spring循環(huán)依賴的博客太多了,有很多都分析的很深入,寫的很用心,甚至還畫了時(shí)序圖、流程圖幫助讀者理解,我看了后,感覺(jué)自己是懂了,但是閉上眼睛,總覺(jué)得還沒(méi)有完全理解,總覺(jué)得還有一兩個(gè)坎過(guò)不去,對(duì)我這種有點(diǎn)笨的人來(lái)說(shuō),真的好難。

        當(dāng)時(shí),我就在想,如果哪一天,我理解了Spring循環(huán)依賴,一定要用自己的方式寫篇博客,幫助大家更好的理解,等我理解后,一直在構(gòu)思,到底怎么應(yīng)該寫,才能更通俗易懂,就在前幾天,我想通了,這么寫應(yīng)該更通俗易懂。在寫本篇博客之前,我翻閱了好多關(guān)于Spring循環(huán)依賴的博客,網(wǎng)上應(yīng)該還沒(méi)有像我這樣講解的,現(xiàn)在就讓我們開始把。

        什么是循環(huán)依賴

        一言以蔽之:兩者相互依賴。

        在開發(fā)中,可能經(jīng)常出現(xiàn)這種情況,只是我們平時(shí)并沒(méi)有注意到原來(lái)我們寫的兩個(gè)類、甚至多個(gè)類相互依賴了,為什么注意不到呢?

        當(dāng)然是因?yàn)闆](méi)有報(bào)錯(cuò),而且一點(diǎn)問(wèn)題都木有,如果報(bào)錯(cuò)了,或者產(chǎn)生了問(wèn)題,我們還會(huì)注意不到嗎?

        這一切都是Spring的功勞,它在后面默默的為我們解決了循環(huán)依賴的問(wèn)題。大家可以關(guān)注公眾號(hào)Java技術(shù)?;貜?fù)spring閱讀系列Spring教程。

        如下所示:

        @Configuration
        @ComponentScan
        public?class?AppConfig?{
        }
        @Service
        public?class?AuthorService?{
        ????@Autowired
        ????BookService?bookService;
        }
        @Service
        public?class?BookService?{
        ????@Autowired
        ????AuthorService?authorService;
        }
        public?class?Main?{
        ????public?static?void?main(String[]?args)?{
        ????????ApplicationContext?annotationConfigApplicationContext?=?new?AnnotationConfigApplicationContext(AppConfig.class);

        ????????BookService?bookService?=?(BookService)?annotationConfigApplicationContext.getBean("bookService");
        ????????System.out.println(bookService.authorService);

        ????????AuthorService?authorService?=?(AuthorService)?annotationConfigApplicationContext.getBean("authorService");
        ????????System.out.println(authorService.bookService);
        ????}
        }

        運(yùn)行結(jié)果:

        com.codebear.springcycle.AuthorService@63376bed
        com.codebear.springcycle.BookService@4145bad8

        可以看到BookService中需要AuthorService,AuthorService中需要BookService,類似于這樣的就叫循環(huán)依賴,但是神奇的是竟然一點(diǎn)問(wèn)題沒(méi)有。

        當(dāng)然有些小伙伴可能get不到它的神奇之處,至于它的神奇之處在哪里,我們放到后面再說(shuō)。

        任何循環(huán)依賴,Spring都能解決嗎

        不行。

        如果是原型 bean的循環(huán)依賴,Spring無(wú)法解決:

        @Service
        @Scope(BeanDefinition.SCOPE_PROTOTYPE)
        public?class?BookService?{????
        ????@Autowired
        ????AuthorService?authorService;
        }
        @Service
        @Scope(BeanDefinition.SCOPE_PROTOTYPE)
        public?class?AuthorService?{????
        ????@Autowired
        ????BookService?bookService;
        }

        啟動(dòng)后,令人恐懼的紅色字體在控制臺(tái)出現(xiàn)了:

        如果是構(gòu)造參數(shù)注入的循環(huán)依賴,Spring無(wú)法解決:

        @Service
        public?class?AuthorService?{
        ????BookService?bookService;

        ????public?AuthorService(BookService?bookService)?{
        ????????this.bookService?=?bookService;
        ????}
        }
        @Service
        public?class?BookService?{

        ????AuthorService?authorService;

        ????public?BookService(AuthorService?authorService)?{
        ????????this.authorService?=?authorService;
        ????}
        }

        還是討厭的紅色字體:

        循環(huán)依賴可以關(guān)閉嗎

        可以,Spring提供了這個(gè)功能,我們需要這么寫:

        public?class?Main?{
        ????public?static?void?main(String[]?args)?{
        ????????AnnotationConfigApplicationContext?applicationContext?=?new?AnnotationConfigApplicationContext();
        ????????applicationContext.setAllowCircularReferences(false);
        ????????applicationContext.register(AppConfig.class);
        ????????applicationContext.refresh();
        ????}
        }

        再次運(yùn)行,就報(bào)錯(cuò)了:

        需要注意的是,我們不能這么寫:

        AnnotationConfigApplicationContext?applicationContext?=?new?AnnotationConfigApplicationContext(AppConfig.class);
        applicationContext.setAllowCircularReferences(false);

        如果你這么寫,程序執(zhí)行完第一行代碼,整個(gè)Spring容器已經(jīng)初始化完成了,你再設(shè)置不允許循環(huán)依賴,也于事無(wú)補(bǔ)了。bean 實(shí)例化原理,推薦看下。關(guān)注公眾號(hào)Java技術(shù)棧回復(fù)spring閱讀系列Spring教程。

        可以循環(huán)依賴的神奇之處在哪

        有很多小伙伴可能并不覺(jué)得可以循環(huán)依賴有多么神奇,那是因?yàn)椴恢烂茳c(diǎn)在哪,接下來(lái)就來(lái)說(shuō)說(shuō)這個(gè)問(wèn)題:
        當(dāng)beanA,beanB循環(huán)依賴:

        1. 創(chuàng)建beanA,發(fā)現(xiàn)依賴beanB;

        2. 創(chuàng)建beanB,發(fā)現(xiàn)依賴beanA;

        3. 創(chuàng)建beanA,發(fā)現(xiàn)依賴beanB;

        4. 創(chuàng)建beanB,發(fā)現(xiàn)依賴beanA。

        ...

        好了,死循環(huán)了。

        循環(huán)依賴的矛盾點(diǎn)就在于要?jiǎng)?chuàng)建beanA,它需要beanB,而創(chuàng)建beanB,又需要beanA,然后兩個(gè)bean都創(chuàng)建不出來(lái)。

        如何簡(jiǎn)單的解決循環(huán)依賴

        如果你曾經(jīng)看過(guò)Spring循環(huán)依賴依賴的博客(比如這篇:圖解Spring循環(huán)依賴),應(yīng)該知道它其中有好幾個(gè)Map,一個(gè)Map放的是最完整的對(duì)象,稱為singletonObjects,一個(gè)Map放的是提前暴露出來(lái)的對(duì)象,稱為earlySingletonObjects。

        在這里,先要解釋下這兩個(gè)東西:

        • singletonObjects:?jiǎn)卫?,其中存放的是?jīng)歷了Spring完整生命周期的bean,這里面的bean的依賴都已經(jīng)填充完畢了。

        • earlySingletonObjects:提前暴露出來(lái)的對(duì)象的map,其中存放的是剛剛創(chuàng)建出來(lái)的對(duì)象,沒(méi)有經(jīng)歷Spring完整生命周期的bean,這里面的bean的依賴還未填充完畢。

        我們可以這么做:

        1. 當(dāng)我們創(chuàng)建完beanA,就把自己放到earlySingletonObjects,發(fā)現(xiàn)自己需要beanB,然后就去屁顛屁顛創(chuàng)建beanB;

        2. 當(dāng)我們創(chuàng)建完beanB,就把自己放到earlySingletonObjects,發(fā)現(xiàn)自己需要beanA,然后就去屁顛屁顛創(chuàng)建beanA;

        3. 創(chuàng)建beanA前,先去earlySingletonObjects看一下,發(fā)現(xiàn)自己已經(jīng)被創(chuàng)建出來(lái)了,把自己返回出去;

        4. beanB拿到了beanA,beanB創(chuàng)建完畢,把自己放入singletonObjects;

        5. beanA可以去singletonObjects拿到beanB了,beanA也創(chuàng)建完畢,把自己放到singletonObjects。
          整個(gè)過(guò)程結(jié)束。

        下面讓我們來(lái)實(shí)現(xiàn)這個(gè)功能:
        首先,自定義一個(gè)注解,字段上打上這個(gè)注解的,說(shuō)明需要被Autowired:

        @Retention(RetentionPolicy.RUNTIME)
        public?@interface?CodeBearAutowired?{
        }

        再創(chuàng)建兩個(gè)循環(huán)依賴的類:

        public?class?OrderService?{
        ????@CodeBearAutowired
        ????public?UserService?userService;
        }
        public?class?UserService?{
        ????@CodeBearAutowired
        ????public?OrderService?orderService;
        }

        然后就是核心,創(chuàng)建對(duì)象,填充屬性,并解決Spring循環(huán)依賴的問(wèn)題:

        public?class?Cycle?{
        ????//?單例池,里面放的是完整的bean,已完成填充屬性
        ????private?final?Map?singletonObjects?=?new?ConcurrentHashMap<>();

        ????//?存放的是提前暴露出來(lái)的bean,沒(méi)有經(jīng)歷過(guò)spring完整的生命周期,沒(méi)有填充屬性
        ????private?final?Map?earlySingletonObjects?=?new?HashMap<>();

        ????//?在Spring中,這個(gè)map存放的是beanNam和beanDefinition的映射關(guān)系
        ????static?Map>?map?=?new?HashMap<>();
        ????static?{
        ????????map.put("orderService",?OrderService.class);
        ????????map.put("userService",?UserService.class);
        ????}
        ????//?如果先調(diào)用init方法,就是預(yù)加載,如果直接調(diào)用getBean就是懶加載,兩者的循環(huán)依賴問(wèn)題都解決了
        ????public?void?init()?{
        ????????for?(Map.Entry>?stringClassEntry?:?map.entrySet())?{
        ????????????createBean(stringClassEntry.getKey());
        ????????}
        ????}

        ????public?Object?getBean(String?beanName)?{
        ????????//?嘗試從singletonObjects中取,
        ????????Object?singletonObject?=?this.singletonObjects.get(beanName);
        ????????if?(singletonObject?!=?null)?{
        ????????????return?singletonObject;
        ????????}

        ????????//?嘗試從earlySingletonObjects取
        ????????singletonObject?=?this.earlySingletonObjects.get(beanName);
        ????????if?(singletonObject?!=?null)?{
        ????????????return?singletonObject;
        ????????}

        ????????return?createBean(beanName);
        ????}

        ????private?Object?createBean(String?beanName)?{
        ????????Object?singletonObject;

        ????????try?{
        ????????????//?創(chuàng)建對(duì)象
        ????????????singletonObject?=?map.get(beanName).getConstructor().newInstance();

        ????????????//?把沒(méi)有完成填充屬性的半成品?bean?放入earlySingletonObjects
        ????????????earlySingletonObjects.put(beanName,?singletonObject);

        ????????????//?填充屬性
        ????????????populateBean(singletonObject);

        ????????????//?bean創(chuàng)建成功,放入singletonObjects
        ????????????this.singletonObjects.put(beanName,?singletonObject);

        ????????????return?singletonObject;
        ????????}?catch?(Exception?ignore)?{
        ????????}
        ????????return?null;
        ????}

        ????private?void?populateBean(Object?object)?{
        ????????Field[]?fields?=?object.getClass().getDeclaredFields();
        ????????for?(Field?field?:?fields)?{
        ????????????if?(field.getAnnotation(CodeBearAutowired.class)?!=?null)?{
        ????????????????Object?value?=?getBean(field.getName());
        ????????????????try?{
        ????????????????????field.setAccessible(true);
        ????????????????????field.set(object,?value);
        ????????????????}?catch?(IllegalAccessException?ignored)?{
        ????????????????}
        ????????????}
        ????????}
        ????}
        }

        預(yù)加載調(diào)用:

        public?class?Main?{
        ????public?static?void?main(String[]?args)?{
        ????????Cycle?cycle?=?new?Cycle();
        ????????cycle.init();
        ????????UserService?userService?=?(UserService)?cycle.getBean("userService");
        ????????OrderService?orderService?=?(OrderService)?cycle.getBean("orderService");
        ????????System.out.println(userService.orderService);
        ????????System.out.println(orderService.userService);
        ????}
        }

        運(yùn)行結(jié)果:

        com.codebear.cycleeasy.OrderService@61baa894
        com.codebear.cycleeasy.UserService@b065c63

        懶加載調(diào)用:

        public?class?Main?{
        ????public?static?void?main(String[]?args)?{
        ????????Cycle?cycle?=?new?Cycle();
        ????????UserService?userService?=?(UserService)?cycle.getBean("userService");
        ????????OrderService?orderService?=?(OrderService)?cycle.getBean("orderService");
        ????????System.out.println(userService.orderService);
        ????????System.out.println(orderService.userService);
        ????}
        }

        運(yùn)行結(jié)果:

        com.codebear.cycleeasy.OrderService@61baa894
        com.codebear.cycleeasy.UserService@b065c63

        為什么無(wú)法解決原型、構(gòu)造方法注入的循環(huán)依賴

        在上面,我們自己手寫了解決循環(huán)依賴的代碼,可以看到,核心是利用一個(gè)map,來(lái)解決這個(gè)問(wèn)題的,這個(gè)map就相當(dāng)于緩存。

        為什么可以這么做,因?yàn)槲覀兊腷ean是單例的,而且是字段注入(setter注入)的,單例意味著只需要?jiǎng)?chuàng)建一次對(duì)象,后面就可以從緩存中取出來(lái),字段注入,意味著我們無(wú)需調(diào)用構(gòu)造方法進(jìn)行注入。

        • 如果是原型bean,那么就意味著每次都要去創(chuàng)建對(duì)象,無(wú)法利用緩存;

        • 如果是構(gòu)造方法注入,那么就意味著需要調(diào)用構(gòu)造方法注入,也無(wú)法利用緩存。

        需要aop怎么辦?

        我們上面的方案看起來(lái)很美好,但是還有一個(gè)問(wèn)題,如果我們的bean創(chuàng)建出來(lái),還要做一點(diǎn)加工,怎么辦?也許,你沒(méi)有理解這句話的意思,再說(shuō)的明白點(diǎn),如果beanA和【beanB的代理對(duì)象】循環(huán)依賴,或者【beanA的代理對(duì)象】和beanB循環(huán)依賴,再或者【beanA的代理對(duì)象】和【beanB的代理對(duì)象】循環(huán)依賴,怎么辦?

        這里說(shuō)的創(chuàng)建代理對(duì)象僅僅是“加工”的其中一種可能。

        遇到這種情況,我們總不能把創(chuàng)建完的對(duì)象直接扔到緩存把?我們這么做的話,如果【beanA的代理對(duì)象】和【beanB的代理對(duì)象】循環(huán)依賴,我們最終獲取的beanA中的beanB還是beanB,并非是beanB的代理對(duì)象。

        聰明的你,一定在想,這還不簡(jiǎn)單嗎:
        我們創(chuàng)建完對(duì)象后,判斷這個(gè)對(duì)象是否需要代理,如果需要代理,創(chuàng)建代理對(duì)象,然后把代理對(duì)象放到earlySingletonObjects不就OJ8K了?
        就像這樣:

        private?Object?createBean(String?beanName)?{
        ????Object?singletonObject;

        ????try?{
        ????????//?創(chuàng)建對(duì)象
        ????????singletonObject?=?map.get(beanName).getConstructor().newInstance();

        ????????//?創(chuàng)建bean的代理對(duì)象
        ????????/**
        ?????????*?if(?需要代理){
        ?????????*?????singletonObject=創(chuàng)建代理對(duì)象;
        ?????????*
        ?????????*?}
        ?????????*/

        ????????//?把沒(méi)有完成填充屬性的半成品?bean?放入earlySingletonObjects
        ????????earlySingletonObjects.put(beanName,?singletonObject);

        ????????//?填充屬性
        ????????populateBean(singletonObject);

        ????????//?bean創(chuàng)建成功,放入singletonObjects
        ????????this.singletonObjects.put(beanName,?singletonObject);

        ????????return?singletonObject;
        ????}?catch?(Exception?ignore)?{
        ????
        ????}
        ????return?null;
        }

        這確實(shí)可以,但是,這違反了Spring的初衷,Spring的初衷是希望在bean生命周期的最后幾步才去aop,如果像上面說(shuō)的這么做,就意味著一旦創(chuàng)建完對(duì)象,Spring就會(huì)去aop了,這就違反了Spring的初衷,所以Spring并沒(méi)有這么做。

        但是如果真的出現(xiàn)了aop bean循環(huán)依賴,就沒(méi)辦法了,只能先去aop,但是如果沒(méi)有出現(xiàn)循環(huán)依賴,Spring并不希望在這里就進(jìn)行aop,所以Spring引入了Map>,ObjectFactory是一個(gè)函數(shù)式接口,可以理解為工廠方法,當(dāng)創(chuàng)建完對(duì)象后,把【獲得這個(gè)對(duì)象的工廠方法】放入這個(gè)map,等真的發(fā)生循環(huán)依賴,就去執(zhí)行這個(gè)【獲得這個(gè)對(duì)象的工廠方法】,獲取加工完成的對(duì)象。

        下面直接放出代碼:

        public?class?Cycle?{
        ????//?單例池,里面放的是完整的bean,已完成填充屬性
        ????private?final?Map?singletonObjects?=?new?ConcurrentHashMap<>();

        ????//?存放的是?加工bean的工廠方法
        ????private?final?Map>?singletonFactories?=?new?HashMap<>();

        ????//?存放的是提前暴露出來(lái)的bean,沒(méi)有經(jīng)歷過(guò)spring完整的生命周期,沒(méi)有填充屬性
        ????private?final?Map?earlySingletonObjects?=?new?HashMap<>();

        ????private?final?Set?singletonsCurrentlyInCreation?=?new?HashSet<>();

        ????static?Map>?map?=?new?HashMap<>();

        ????static?{
        ????????map.put("orderService",?OrderService.class);
        ????????map.put("userService",?UserService.class);
        ????}

        ????public?void?init()?{
        ????????for?(Map.Entry>?stringClassEntry?:?map.entrySet())?{
        ????????????createBean(stringClassEntry.getKey());
        ????????}
        ????}

        ????private?Object?createBean(String?beanName)?{
        ????????Object?instance?=?null;
        ????????try?{
        ????????????instance?=?map.get(beanName).getConstructor().newInstance();
        ????????}?catch?(Exception?ex)?{
        ????????}


        ????????Object?finalInstance?=?instance;
        ????????this.singletonFactories.put(beanName,?()?->?{
        ????????????//?創(chuàng)建代理對(duì)象
        ????????????return?finalInstance;
        ????????});

        ????????populateBean(instance);

        ????????this.singletonObjects.put(beanName,?instance);
        ????????return?instance;
        ????}

        ????public?Object?getBean(String?beanName)?{
        ????????//?嘗試從singletonObjects中取,
        ????????Object?singletonObject?=?this.singletonObjects.get(beanName);
        ????????if?(singletonObject?!=?null)?{
        ????????????return?singletonObject;
        ????????}

        ????????//?嘗試從earlySingletonObjects取
        ????????singletonObject?=?this.earlySingletonObjects.get(beanName);
        ????????if?(singletonObject?!=?null)?{
        ????????????return?singletonObject;
        ????????}

        ????????//?嘗試從singletonFactories取出工廠方法
        ????????ObjectFactory?objectFactory?=?this.singletonFactories.get(beanName);
        ????????if?(objectFactory?!=?null)?{
        ????????????singletonObject?=?objectFactory.getObject();
        ????????????this.earlySingletonObjects.put(beanName,?singletonObject);
        ????????????return?singletonObject;
        ????????}

        ????????return?createBean(beanName);
        ????}

        ????private?void?populateBean(Object?object)?{
        ????????Field[]?fields?=?object.getClass().getDeclaredFields();
        ????????for?(Field?field?:?fields)?{
        ????????????if?(field.getAnnotation(CodeBearAutowired.class)?!=?null)?{
        ????????????????Object?value?=?getBean(field.getName());
        ????????????????try?{
        ????????????????????field.setAccessible(true);
        ????????????????????field.set(object,?value);
        ????????????????}?catch?(IllegalAccessException?ignored)?{
        ????????????????}
        ????????????}
        ????????}
        ????}
        }

        調(diào)用方法:

        ?public?static?void?main(String[]?args)?{
        ????Cycle?cycle?=?new?Cycle();
        ????cycle.init();
        ????System.out.println(((UserService)?cycle.getBean("userService")).orderService);
        ????System.out.println(((OrderService)?cycle.getBean("orderService")).userService);
        }

        運(yùn)行結(jié)果:

        com.codebear.cycles.OrderService@49e4cb85
        com.codebear.cycles.UserService@2133c8f8

        二級(jí)緩存能不能解決循環(huán)依賴,三級(jí)循環(huán)到底有什么用?

        我的觀點(diǎn)可能和網(wǎng)上的主流觀點(diǎn)有很大的出入,至于我的觀點(diǎn)是對(duì)是錯(cuò),請(qǐng)各位自行判斷。

        二級(jí)緩存可以解決循環(huán)依賴,哪怕aop bean循環(huán)依賴,上面我們已經(jīng)提到了,我們可以創(chuàng)建完對(duì)象,直接創(chuàng)建代理對(duì)象,把代理對(duì)象放入二級(jí)緩存,這樣我們從二級(jí)緩存獲得的一定是aop bean,并非是bean本身。

        三級(jí)緩存有什么用?網(wǎng)上的主流觀點(diǎn)是為了解決循環(huán)依賴,還有就是為了效率,為了解決循環(huán)依賴,我們上面已經(jīng)討論過(guò)了,我的觀點(diǎn)是二級(jí)緩存已經(jīng)可以解決循環(huán)依賴了,下面就讓我們想想,和效率是否有關(guān)系?

        我的觀點(diǎn)是沒(méi)有關(guān)系,理由如下:
        我們把【獲得對(duì)象的工廠方法】放入了map

        • 如果沒(méi)有循環(huán)依賴,這個(gè)map根本沒(méi)有用到,和效率沒(méi)有關(guān)系;

        • 如果是普通bean循環(huán)依賴,三級(jí)緩存直接返回了bean,和效率還是沒(méi)有關(guān)系;

        • 如果是aop bean循環(huán)依賴,如果沒(méi)有三級(jí)緩存,直接創(chuàng)建代理對(duì)象,放入二級(jí)緩存,如果有三級(jí)緩存,還是需要?jiǎng)?chuàng)建代理對(duì)象,只是兩者的時(shí)機(jī)不同,和效率還是沒(méi)有關(guān)系。

        有了這篇博客的基礎(chǔ),當(dāng)你再看其他關(guān)于Spring循環(huán)依賴的博客,應(yīng)該會(huì)輕松的多,因?yàn)槲覀儺吘棺约航鉀Q了循環(huán)依賴,Spring的循環(huán)依賴只是在我們之上做了進(jìn)一步的封裝與改進(jìn)。





        關(guān)注Java技術(shù)棧看更多干貨



        戳原文,獲取精選面試題!
        瀏覽 41
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            黄色片短视频 | 精品蜜桃秘 一区二区三区观看 | 少妇又黄又爽 | 国产一区在线免费观看 | 黄色国产在线观看 | 国产三级电影排行 | 破处日逼网址 | 车上性姿势108式大全图 | 国产色片在线 | 国产AV 无码 乱噜噜 |