Spring系列面試題

1、springmvc用到的注解,作用是什么,原理。

@Controller注解

是在Spring的org.springframework.stereotype包下,org.springframework.stereotype.Controller注解類型用于指示Spring類的實例是一個控制器,使用@Controller注解的類不需要繼承特定的父類或者實現(xiàn)特定的接口,相對之前的版本實現(xiàn)Controller接口變的更加簡單。而Controller接口的實現(xiàn)類只能處理一個單一的請求動作,而@Controller注解注解的控制器可以同時支持處理多個請求動作,使程序開發(fā)變的更加靈活。

@Controller用戶標記一個類,使用它標記的類就是一個Spring MVC Controller對象,即:一個控制器類。Spring使用掃描機制查找應用程序中所有基于注解的控制器類,分發(fā)處理器會掃描使用了該注解的方法,并檢測該方法是否使用了@RequestMapping注解,而使用@RequestMapping注解的方法才是真正處理請求的處理器。為了保證Spring能找到控制器,我們需要完成兩件事:

1、在Spring MVC的配置文件中的頭部引入spring-context;

@RequestMapping注解

Spring MVC中用于參數(shù)綁定的注解有很多,都在org.springframework.web.bind.annotation包中,根據(jù)它們處理的request的不同內(nèi)容可以分為四類(常用的類型)。

第一類:處理request body部分的注解有:@RequestParam和@RequestBody

第二類:處理requet uri部分的注解有:@PathVaribale

第三類:處理request header部分的注解有:@RequestHeader和@CookieValue

第四類:處理attribute類型的注解有:@SessionAttributes和@MoelAttribute

@RequestParam注解

下面來說org.springframework.web.bind.annotation包下的第三個注解,即:@RequestParam注解,該注解類型用于將指定的請求參數(shù)賦值給方法中的形參。那么@RequestParam注解有什么屬性呢?它有4種屬性,下面將逐一介紹這四種屬性:

1、name屬性

該屬性的類型是String類型,它可以指定請求頭綁定的名稱;

2、value屬性

該屬性的類型是String類型,它可以設(shè)置是name屬性的別名;

3、required屬性

該屬性的類型是boolean類型,它可以設(shè)置指定參數(shù)是否必須綁定;

4、defalutValue屬性

該屬性的類型是String類型,它可以設(shè)置如果沒有傳遞參數(shù)可以使用默認值。

@PathVaribale注解

下面來說org.springframework.web.bind.annotation包下的第四個注解,即:@PathVaribale注解,該注解類型可以非常方便的獲得請求url中的動態(tài)參數(shù)。@PathVaribale注解只支持一個屬性value,類型String,表示綁定的名稱,如果省略則默認綁定同名參數(shù)。

常用的就是以上幾個,如需學習更多可以參考下面的鏈接:

https://blog.csdn.net/qian_ch/article/details/73826663

2、springmvc controller方法中為什么不能定義局部變量?。

因為controller是默認單例模式,高并發(fā)下全局變量會出現(xiàn)線程安全問題

現(xiàn)這種問題如何解決呢?

第一種方式: 既然是全局變量惹的禍,那就將全局變量都編程局部變量,通過方法參數(shù)來傳遞。

第二種方式: jdk提供了java.lang.ThreadLocal,它為多線程并發(fā)提供了新思路。

第三種:使用@Scope("session"),會話級別

@Controller//把這個bean 的范圍設(shè)置成session,表示這bean是會話級別的, @Scope("session")publicclassXxxController{privateList list ;//@PostConstruct當bean加載完之后,就會執(zhí)行init方法,并且將list實例化; @PostConstructpublicvoidinit(){? list =newArrayList();? }? }

第四種:將控制器的作用域從單例改為原型,即在spring配置文件Controller中聲明 scope="prototype",每次都創(chuàng)建新的controller

3、Springmvc 中DispatcherServlet初始化過程。

4、SpringMVC執(zhí)行流程和原理

SpringMVC流程:

01、用戶發(fā)送出請求到前端控制器DispatcherServlet。

02、DispatcherServlet收到請求調(diào)用HandlerMapping(處理器映射器)。

03、HandlerMapping找到具體的處理器(可查找xml配置或注解配置),生成處理器對象及處理器攔截器(如果有),再一起返回給DispatcherServlet。

04、DispatcherServlet調(diào)用HandlerAdapter(處理器適配器)。

05、HandlerAdapter經(jīng)過適配調(diào)用具體的處理器(Handler/Controller)。

06、Controller執(zhí)行完成返回ModelAndView對象。

07、HandlerAdapter將Controller執(zhí)行結(jié)果ModelAndView返回給DispatcherServlet。

08、DispatcherServlet將ModelAndView傳給ViewReslover(視圖解析器)。

09、ViewReslover解析后返回具體View(視圖)。

10、DispatcherServlet根據(jù)View進行渲染視圖(即將模型數(shù)據(jù)填充至視圖中)。

11、DispatcherServlet響應用戶。

5、@autowire和@resource區(qū)別

對比項@Autowire@Resource注解來源Spring注解JDK注解(JSR-250標準注解,屬于J2EE)裝配方式優(yōu)先按類型優(yōu)先按名稱屬性requiredname、type作用范圍字段、setter方法、構(gòu)造器字段、setter方法

6、SpringMVC中的攔截器和Servlet中的filter有什么區(qū)別?

首先最核心的一點他們的攔截側(cè)重點是不同的,SpringMVC中的攔截器是依賴JDK的反射實現(xiàn)的,SpringMVC的攔截器主要是進行攔截請求,通過對Handler進行處理的時候進行攔截,先聲明的攔截器中的preHandle方法會先執(zhí)行,然而它的postHandle方法(他是介于處理完業(yè)務(wù)之后和返回結(jié)果之前)和afterCompletion方法卻會后執(zhí)行。并且Spring的攔截器是按照配置的先后順序進行攔截的。

而Servlet的filter是基于函數(shù)回調(diào)實現(xiàn)的過濾器,F(xiàn)ilter主要是針對URL地址做一個編碼的事情、過濾掉沒用的參數(shù)、安全校驗(比較泛的,比如登錄不登錄之類)

7、講講Spring加載流程。

初始化環(huán)境—>加載配置文件—>實例化Bean—>調(diào)用Bean顯示信息

首先從大的幾個核心步驟來去說明,因為Spring中的具體加載過程和用到的類實在是太多了。

(1)、首先是先從AbstractBeanFactory中去調(diào)用doGetBean(name, requiredType, final Object[] args, boolean typeCheckOnly【這個是判斷進行創(chuàng)建bean還是僅僅用來做類型檢查】)方法,然后第一步要做的就是先去對傳入的參數(shù)name進行做轉(zhuǎn)換,因為有可能傳進來的name=“&XXX”之類,需要去除&符號

(2)、然后接著是去調(diào)用getSingleton()方法,其實在上一個面試題中已經(jīng)提到了這個方法,這個方法就是利用“三級緩存” 來去避免循環(huán)依賴問題的出現(xiàn)的。【這里補充一下,只有在是單例的情況下才會去解決循環(huán)依賴問題】

(3)、對從緩存中拿到的bean其實是最原始的bean,還未長大,所以這里還需要調(diào)用getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)方法去進行實例化。

(4)、然后會解決單例情況下嘗試去解決循環(huán)依賴,如果isPrototypeCurrentlyInCreation(beanName)返回為true的話,會繼續(xù)下一步,否則throw new BeanCurrentlyInCreationException(beanName);

(5)、因為第三步中緩存中如果沒有數(shù)據(jù)的話,就直接去parentBeanFactory中去獲取bean,然后判斷containsBeanDefinition(beanName)中去檢查已加載的XML文件中是否包含有這樣的bean存在,不存在的話遞歸去getBean()獲取,如果沒有繼續(xù)下一步

(6)、這一步是吧存儲在XML配置文件中的GernericBeanDifinition轉(zhuǎn)換為RootBeanDifinition對象。這里主要進行一個轉(zhuǎn)換,如果父類的bean不為空的話,會一并合并父類的屬性

(7)、這一步核心就是需要跟這個Bean有關(guān)的所有依賴的bean都要被加載進來,通過剛剛的那個RootBeanDifinition對象去拿到所有的beanName,然后通過registerDependentBean(dependsOnBean, beanName)注冊bean的依賴

(8)、然后這一步就是會根據(jù)我們在定義bean的作用域的時候定義的作用域是什么,然后進行判斷在進行不同的策略進行創(chuàng)建(比如isSingleton、isPrototype)

(9)、這個是最后一步的類型裝換,會去檢查根據(jù)需要的類型是否符合bean的實際類型去做一個類型轉(zhuǎn)換。Spring中提供了許多的類型轉(zhuǎn)換器

8、Spring AOP的實現(xiàn)原理。

AOP(Aspect-OrientedProgramming,面向方面編程):是OOP的補充和完善。OOP引入了封裝、繼承、多態(tài)性等建立一種對象層次結(jié)構(gòu)(從上到下的關(guān)系)。當需要為分散的對象引入公共行為的時候(從左到右的關(guān)系),OOP就顯得無能為力。例如:日志功能。日志代碼往往水平的散步所有對象層次中,與對象的核心功能毫無關(guān)系。這種代碼被稱為橫切(cross-cutting)代碼還有像安全性、異常處理、透明的持續(xù)性等都稱為橫切代碼。在OOP設(shè)計中,它們導致了大量代碼的重復,不利于模塊的重用。

AOP與OOP相反,利用“橫切”技術(shù)將影響多個類的公共行為封裝到一個可重用模塊,稱為Aspect。簡單點,就是將那些與業(yè)務(wù)無關(guān),卻被業(yè)務(wù)模塊所共同調(diào)用的邏輯封裝起來,便于減少系統(tǒng)的重復代碼,降低模塊間的耦合度,并有利于未來的可操作性和可維護性。AOP的核心思想就是“將應用程序中的商業(yè)邏輯同對其提供支持的通用服務(wù)進行分離。”

Spring提供了兩種方式生成代理對象:JDKProxy和Cglib具體使用哪種方式生成由AopProxyFactory根據(jù)AdvisedSupport對象的配置來決定。默認的策略是如果目標類是接口,則使用JDK動態(tài)代理技術(shù),否則使用Cglib來生成代理。

9、講講Spring事務(wù)的傳播屬性。

1)、PROPAGATION_REQUIRED —— 支持當前事務(wù),如果當前沒有事務(wù),就新建一個事務(wù)。(常見的選擇)比如ServiceB.methodB的事務(wù)級別定義為PROPAGATION_REQUIRED,那么由于執(zhí)行ServiceA.methodA的時候,ServiceA.methodA已經(jīng)起了事務(wù),這時調(diào)用ServiceB.methodB,ServiceB.methodB看到自己已經(jīng)運行在ServiceA.methodA的事務(wù)內(nèi)部,就不再起新的事務(wù)。而假如ServiceA.methodA運行的時候發(fā)現(xiàn)自己沒有在事務(wù)中,他就會為自己分配一個事務(wù)。這樣,在ServiceA.methodA或者在ServiceB.methodB內(nèi)的任何地方出現(xiàn)異常,事務(wù)都會被回滾。即使ServiceB.methodB的事務(wù)已經(jīng)被提交,但是ServiceA.methodA在接下來fail要回滾,ServiceB.methodB也要回滾。

2)、PROPAGATION_SUPPORTS —— 支持當前事務(wù),如果當前沒有事務(wù),就以非事務(wù)方式執(zhí)行。

3)、PROPAGATION_MANDATORY ——支持當前事務(wù),如果當前沒有事務(wù),就拋出異常。

4)、PROPAGATION_REQUIRES_NEW ——支持當前事務(wù),如果當前沒有事務(wù),就將當前事務(wù)掛起。如ServiceA.methodA的事務(wù)級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務(wù)級別為PROPAGATION_REQUIRES_NEW,那么當執(zhí)行到ServiceB.methodB的時候,ServiceA.methodA所在的事務(wù)就會掛起,ServiceB.methodB會起一個新的事務(wù),等待ServiceB.methodB的事務(wù)完成以后,A才繼續(xù)執(zhí)行。他與PROPAGATION_REQUIRED的事務(wù)區(qū)別在于事務(wù)的回滾程度了。因為ServiceB.methodB是新起一個事務(wù),那么就是存在兩個不同的事務(wù)。如果ServiceB.methodB已經(jīng)提交,那么ServiceA.methodA失敗回滾,ServiceB.methodB是不會回滾的。如果ServiceB.methodB失敗回滾,如果他拋出的異常被ServiceA.methodA捕獲,ServiceA.methodA事務(wù)仍然可能提交。

5)、PROPAGATION_NOT_SUPPORTED —— 以非事務(wù)方式執(zhí)行當前操作,如果當前存在事務(wù),就把事務(wù)掛起來。

6)、PROPAGATION_NEVER —— 以非事務(wù)方式執(zhí)行,如果當前存在事務(wù),則拋異常。

7)、PROPAGATION_NESTED—— 如果當前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行,關(guān)鍵是savepoint。如果當前沒有事務(wù),則進行與PROPAGATION_REQUIRED類似的操作。與PROPAGATION_REQUIRES_NEW的區(qū)別是NESTED的事務(wù)和他的父事務(wù)是相依的,它的提交是要等父事務(wù)一塊提交。也就是說,如果父事務(wù)最后回滾,它也要回滾。

10、Spring如何管理事務(wù)的。

Spring事務(wù)管理主要包括3個接口,Spring事務(wù)主要由以下三個共同完成的:

1)、PlatformTransactionManager:事務(wù)管理器,主要用于平臺相關(guān)事務(wù)的管理。主要包括三個方法:①、commit:事務(wù)提交。②、rollback:事務(wù)回滾。③、getTransaction:獲取事務(wù)狀態(tài)。

2)、TransacitonDefinition:事務(wù)定義信息,用來定義事務(wù)相關(guān)屬性,給事務(wù)管理器PlatformTransactionManager使用這個接口有下面四個主要方法:①、getIsolationLevel:獲取隔離級別。②、getPropagationBehavior:獲取傳播行為。③、getTimeout獲取超時時間。④、isReadOnly:是否只讀(保存、更新、刪除時屬性變?yōu)閒alse--可讀寫,查詢時為true--只讀)事務(wù)管理器能夠根據(jù)這個返回值進行優(yōu)化,這些事務(wù)的配置信息,都可以通過配置文件進行配置。

3)、TransationStatus:事務(wù)具體運行狀態(tài),事務(wù)管理過程中,每個時間點事務(wù)的狀態(tài)信息。例如:①、hasSavepoint():返回這個事務(wù)內(nèi)部是否包含一個保存點。②、isCompleted():返回該事務(wù)是否已完成,也就是說,是否已經(jīng)提交或回滾。③、isNewTransaction():判斷當前事務(wù)是否是一個新事務(wù)。

11、Spring怎么配置事務(wù)(具體說出一些關(guān)鍵的xml 元素)。

配置事務(wù)的方法有兩種:

1)、基于XML的事務(wù)配置。

<?xml version="1.0"encoding="UTF-8"?><!-- from the file 'context.xml' -->

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"><!-- 數(shù)據(jù)元信息 --><!-- 管理事務(wù)的類,指定我們用誰來管理我們的事務(wù)--><!-- 首先我們要把服務(wù)對象聲明成一個bean 例如HelloService --><!-- 然后是聲明一個事物建議tx:advice,spring為我們提供了事物的封裝,這個就是封裝在了<tx:advice/>中 --><!-- <tx:advice/>有一個transaction-manager屬性,我們可以用它來指定我們的事物由誰來管理。

默認:事務(wù)傳播設(shè)置是 REQUIRED,隔離級別是DEFAULT --><!-- 配置這個事務(wù)建議的屬性 --><!-- 指定所有g(shù)et開頭的方法執(zhí)行在只讀事務(wù)上下文中 --><!-- 其余方法執(zhí)行在默認的讀寫上下文中 --><!-- 我們定義一個切面,它匹配FooService接口定義的所有操作 --><!-- <aop:pointcut/>元素定義AspectJ的切面表示法,這里是表示com.yintong.service.helloService包下的任意方法。 --><!-- 然后我們用一個通知器:<aop:advisor/>把這個切面和tx:advice綁定在一起,表示當這個切面:fooServiceOperation執(zhí)行時tx:advice定義的通知邏輯將被執(zhí)行 -->

2)、基于注解方式的事務(wù)配置。

@Transactional:直接在Java源代碼中聲明事務(wù)的做法讓事務(wù)聲明和將受其影響的代碼距離更近了,而且一般來說不會有不恰當?shù)鸟詈系娘L險,因為,使用事務(wù)性的代碼幾乎總是被部署在事務(wù)環(huán)境中。

<?xml version="1.0"encoding="UTF-8"?>

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"><!-- 配置注解事務(wù) -->

主要在類中定義事務(wù)注解@Transactional,如下:

//@Transactional 注解可以聲明在類上,也可以聲明在方法上。在大多數(shù)情況下,方法上的事務(wù)會首先執(zhí)行@Transactional(readOnly = true) public class HelloService{publicFoogetFoo(String fooName) {? }//@Transactional 注解的事務(wù)設(shè)置將優(yōu)先于類級別注解的事務(wù)設(shè)置 propagation:可選的傳播性設(shè)置 @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)? public void updateFoo(Hel hel) { }}

12、說說你對Spring的理解

1)、Spring是一個開源框架,主要是為簡化企業(yè)級應用開發(fā)而生??梢詫崿F(xiàn)EJB可以實現(xiàn)的功能,Spring是一個IOC和AOP容器框架。

? 控制反轉(zhuǎn)(IOC):Spring容器使用了工廠模式為我們創(chuàng)建了所需要的對象,我們使用時不需要自己去創(chuàng)建,直接調(diào)用Spring為我們提供的對象即可,這就是控制反轉(zhuǎn)的思想。

? 依賴注入(DI):Spring使用Java Bean對象的Set方法或者帶參數(shù)的構(gòu)造方法為我們在創(chuàng)建所需對象時將其屬性自動設(shè)置所需要的值的過程就是依賴注入的基本思想。

? 面向切面編程(AOP):在面向?qū)ο缶幊?OOP)思想中,我們將事物縱向抽象成一個個的對象。而在面向切面編程中,我們將一個個對象某些類似的方面橫向抽象成一個切面,對這個切面進行一些如權(quán)限驗證,事物管理,記錄日志等公用操作處理的過程就是面向切面編程的思想。

2)、在Spring中,所有管理的都是JavaBean對象,而BeanFactory和ApplicationContext就是Spring框架的那個IOC容器,現(xiàn)在一般使用ApplicationContext,其不但包括了BeanFactory的作用,同時還進行了更多的擴展。

13、Spring非單例注入的原理

在大部分情況下,容器中的bean都是singleton類型的。如果一個singleton bean要引用另外一個singleton bean或者一個非singleton bean要引用另外一個非singleton,通常情況下將一個bean定義為另一個bean的property值就可以了。不過對于具有不同生命周期的bean來說這樣做就會有問題了,比如在調(diào)用一個singleton類型bean A的某個方法時,需要引用另一個非singleton(prototype)類型的bean B,對于bean A來說,容器只會創(chuàng)建一次,這樣就沒法在需要的時候每次讓容器為bean A提供一個新的的bean B實例。

14、Spring依賴注入原理

我們知道Spring的依賴注入有四種方式,各自是get/set方法注入、構(gòu)造器注入、靜態(tài)工廠方法注入、實例工廠方法注入。

https://www.cnblogs.com/mthou...

15、Springbean的生命周期

通過這張圖能大致看懂spring的生命周期,詳解:

首先會先進行實例化bean對象

然后是進行對bean的一個屬性進行設(shè)置

接著是對BeanNameAware(其實就是為了讓Spring容器來獲取bean的名稱)、BeanFactoryAware(讓bean的BeanFactory調(diào)用容器的服務(wù))、ApplicationContextAware(讓bean當前的applicationContext可以來取調(diào)用Spring容器的服務(wù))

然后是實現(xiàn)BeanPostProcessor 這個接口中的兩個方法,主要是對調(diào)用接口的前置初始化postProcessBeforeInitialization

這里是主要是對xml中自己定義的初始化方法 init-method = “xxxx”進行調(diào)用

然后是繼續(xù)對BeanPostProcessor 這個接口中的后置初始化方法進行一個調(diào)用postProcessAfterInitialization()

其實到這一步,基本上這個bean的初始化基本已經(jīng)完成,就處于就緒狀態(tài)

然后就是當Spring容器中如果使用完畢的話,就會調(diào)用destory()方法

最后會去執(zhí)行我們自己定義的銷毀方法來進行銷毀,然后結(jié)束生命周期。

16、Spring中bean的循環(huán)依賴怎么解決?

Spring的循環(huán)依賴其實就是在進行g(shù)etBean的時候,A對象中去依賴B對象,而B對象又依賴C對象,但是對象C又去依賴A對象,結(jié)果就造成A、B、C三個對象都不能完成實例化,出現(xiàn)了循環(huán)依賴。就會出現(xiàn)死循環(huán),最終導致內(nèi)存溢出的錯誤。

.如何去解決Spring的循環(huán)依賴呢?

1.先知道什么是Spring的“三級緩存”:就是下面的三個大的Map對象,因為Spring中的循環(huán)依賴的理論基礎(chǔ)其實是基于java中的值傳遞的,然后其實Spring中的單例對象的創(chuàng)建是分為三個步驟的:

createBeanInstance,其實第一步就是通過構(gòu)造方法去進行實例化對象。但是這一步只是實例對象而已,并沒有把對象的屬性也給注入進去

然后這一步就是進行注入實例對象的屬性,也就是從這步對spring xml中指定的property進行populate

最后一步其實是初始化XML中的init方法,來進行最終完成實例對象的創(chuàng)建。但是AfterPropertiesSet方法會發(fā)生循環(huán)依賴的步驟集中在第一步和第二步。

singletonObjects指單例對象的cache(一級緩存)privatefinalMap singletonObjects =newConcurrentHashMap(256);singletonFactories指單例對象工廠的cache(三級緩存)privatefinalMap> singletonFactories =newHashMap>(16);earlySingletonObjects指提前曝光的單例對象的cache(二級緩存)privatefinalMap earlySingletonObjects =newHashMap(16);

然后是怎么具體使用到這個三級緩存的呢,或者說三級緩存的思路?

首先第一步是在Spring中會先去調(diào)用getSingleton(String beanName, boolean allowEarlyReference)來獲取想要的單例對象。

然后第一步會先進行通過singletonObjects這個一級緩存的集合中去獲取對象,如果沒有獲取成功的話并且使用isSingletonCurrentlyInCreation(beanName)去判斷對應的單例對象是否正在創(chuàng)建中(也就是說當單例對象沒有被初始化完全,走到初始化的第一步或者第二的時候),如果是正在創(chuàng)建中的話,會繼續(xù)走到下一步

然后會去從earlySingletonObjects中繼續(xù)獲取這個對象,如果又沒有獲取到這個單例對象的話,并且通過參數(shù)傳進來的allowEarlyReference標志,看是不是允許singletonFactories(三級緩存集合)去拿到該實例對象,如果allowEarlyReference為Ture的話,那么繼續(xù)下一步

此時上一步中并沒有從earlySingletonObjects二級緩存集合中拿到想要的實例對象,最后只能從三級緩存singletonFactories (單例工廠集合中)去獲取實例對象,

然后把獲取的對象通過Put(beanName, singletonObject)放到earlySingletonObjects(二級緩存中),然后在再從singletonFactories(三級緩存)對象中的集合中把該對象給remove(beanName)出去。

附上核心代碼

protectedObject getSingleton(String beanName, boolean allowEarlyReference) { 從一級緩存獲取 Object singletonObject =this.singletonObjects.get(beanName);if(singletonObject ==null&& isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { 從二級緩存獲取 singletonObject =this.earlySingletonObjects.get(beanName);if(singletonObject ==null&& allowEarlyReference) { 從三級緩存獲取 ObjectFactory singletonFactory =this.singletonFactories.get(beanName);if(singletonFactory !=null) { singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName); } } } }return(singletonObject != NULL_OBJECT ? singletonObject :null);}

總結(jié)一下為什么這么做就能解決Spring中的循環(huán)依賴問題。

其實在沒有真正創(chuàng)建出來一個實例對象的時候,這個對象已經(jīng)被生產(chǎn)出來了,雖然還不完美(還沒有進行初始化的第二步和第三步),但是已經(jīng)能被人認出來了(根據(jù)對象引用能定位到堆中的對象),所以Spring此時將這個對象提前曝光出來讓大家認識,讓大家使用

A首先完成了初始化的第一步,并且將自己提前曝光到singletonFactories中,此時進行初始化的第二步,發(fā)現(xiàn)自己依賴對象B,此時就嘗試去get(B),發(fā)現(xiàn)B還沒有被create,所以走create流程,B在初始化第一步的時候發(fā)現(xiàn)自己依賴了對象A,于是嘗試get(A),嘗試一級緩存singletonObjects(肯定沒有,因為A還沒初始化完全),嘗試二級緩存earlySingletonObjects(也沒有),嘗試三級緩存singletonFactories,由于A通過ObjectFactory將自己提前曝光了,所以B能夠通過ObjectFactory.getObject拿到A對象(雖然A還沒有初始化完全,但是總比沒有好呀),B拿到A對象后順利完成了初始化階段1、2、3,完全初始化之后將自己放入到一級緩存singletonObjects中。此時返回A中,A此時能拿到B的對象順利完成自己的初始化階段2、3,最終A也完成了初始化,長大成人,進去了一級緩存singletonObjects中,而且更加幸運的是,由于B拿到了A的對象引用,所以B現(xiàn)在hold住的A對象也蛻變完美了!一切都是這么神奇??!

總結(jié):Spring通過三級緩存加上“提前曝光”機制,配合Java的對象引用原理,比較完美地解決了某些情況下的循環(huán)依賴問題!

https://blog.csdn.net/itmrchen/article/details/90201279

https://blog.csdn.net/qq_36520235/article/details/88257749

17、spring容器的bean什么時候被實例化?

(1)如果你使用BeanFactory作為Spring Bean的工廠類,則所有的bean都是在第一次使用該Bean的時候?qū)嵗?/p>

(2)如果你使用ApplicationContext作為Spring Bean的工廠類,則又分為以下幾種情況:

如果bean的scope是singleton的,并且lazy-init為false(默認是false,所以可以不用設(shè)置),則 ApplicationContext啟動的時候就實例化該Bean,并且將實例化的Bean放在一個map結(jié)構(gòu)的緩存中,下次再使 用該 Bean的時候,直接從這個緩存中取

如果bean的scope是singleton的,并且lazy-init為true,則該Bean的實例化是在第一次使用該Bean的時候進行實例化。

如果bean的scope是prototype的,則該Bean的實例化是在第一次使用該Bean的時候進行實例化。

18、Spring中AOP的底層是怎么實現(xiàn)的?

Spring中AOP底層的實現(xiàn)其實是基于JDK的動態(tài)代理和cglib動態(tài)創(chuàng)建類進行動態(tài)代理來實現(xiàn)的:

第一種基于JDK的動態(tài)代理的原理是:

需要用到的幾個關(guān)鍵成員

InvocationHandler (你想要通過動態(tài)代理生成的對象都必須實現(xiàn)這個接口)

真實的需要代理的對象(幫你代理的對象)

Proxy對象(是JDK中java.lang.reflect包下的)

下面是具體如何動態(tài)利用這三個組件生成代理對象

(1)首先你的真是要代理的對象必須要實現(xiàn)InvocationHandler 這個接口,并且覆蓋這個接口的invoke(Object proxyObject, Method method, Object[] args)方法,這個Invoker中方法的參數(shù)的proxyObject就是你要代理的真實目標對象,方法調(diào)用會被轉(zhuǎn)發(fā)到該類的invoke()方法, method是真實對象中調(diào)用方法的Method類,Object[] args是真實對象中調(diào)用方法的參數(shù)

(2)然后通過Proxy類去調(diào)用newProxyInstance(classLoader, interfaces, handler)方法,classLoader是指真實代理對象的類加載器,interfaces是指真實代理對象需要實現(xiàn)的接口,還可以同時指定多個接口,handler方法調(diào)用的實際處理者(其實就是幫你代理的那個對象),代理對象的方法調(diào)用都會轉(zhuǎn)發(fā)到這里,然后直接就能生成你想要的對象類了。

需要java學習路線圖的關(guān)注gzh“程序員小x”私信領(lǐng)取哦!另外喜歡這篇文章的可以給筆者點個贊,關(guān)注一下,每天都會分享Java相關(guān)文章!還有不定時的福利贈送,包括整理的學習資料,面試題,源碼等~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • IOC和DI是什么? Spring IOC 的理解,其初始化過程? BeanFactory 和 FactoryBe...
    justlpf閱讀 3,595評論 1 21
  • 不足的地方請大家多多指正,如有其它沒有想到的常問面試題請大家多多評論,一起成長,感謝!~ String可以被繼承嗎...
    啟示錄是真的閱讀 3,073評論 3 3
  • http://liuxing.info/2017/06/30/Spring%20AMQP%E4%B8%AD%E6%...
    sherlock_6981閱讀 16,208評論 2 11
  • 人間紛亂舞,千里蒼茫, 踏雪尋詩,竟為清歌凝咽; 焙爐煮老茶,徐云氤氳, 染了年華,終究滄桑笑破。 ——...
    Luciahuang閱讀 127評論 0 1
  • 你就是一個點 但你不是一點喔 這一個點讓我思緒萬千千萬 這么看來 你也有點兒討厭阿
    dew清澈閱讀 299評論 0 0

友情鏈接更多精彩內(nèi)容