SpringBean依賴和三級(jí)緩存的案例講解
spring中的bean依賴有大體上可以分為兩類,共3中形式,下面簡(jiǎn)單介紹一下。
第一類是構(gòu)造方法中的循環(huán)依賴,這種會(huì)報(bào)錯(cuò)
@Service
public class ServiceA {
private ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
public void methodA(){
System.out.println("a");
}
}
@Service
public class ServiceB {
private ServiceA serviceA;
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void methodB(){
System.out.println("b");
}
}
//錯(cuò)誤提示
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| serviceA defined in file [C:\demo\target\classes\com\example\demo\ServiceA.class]
↑ ↓
| serviceB defined in file [C:\demo\target\classes\com\example\demo\ServiceB.class]
└─────┘
第二類是field循環(huán)依賴,它分為兩種,第一類循環(huán)依賴的作用域scope默認(rèn)是singleton,啟動(dòng)不會(huì)報(bào)錯(cuò)
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
public void methodA(){
System.out.println("a");
}
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
public void methodB(){
System.out.println("b");
}
}
第二種作用域scope為prototype,在這種情況下bean是多例的,按理說(shuō)這種啟動(dòng)也會(huì)報(bào)錯(cuò),但是它成功了。。我也不知道為啥
@Service
@Scope("prototype")
public class ServiceA {
@Autowired
private ServiceB serviceB;
public void methodA(){
System.out.println("a");
}
}
@Service
@Scope("prototype")
public class ServiceB {
@Autowired
private ServiceA serviceA;
public void methodB(){
System.out.println("b");
}
}
據(jù)我在網(wǎng)上查找的資料,spring可以幫我們處理bean的scope為singleton的field循環(huán)依賴,個(gè)人感覺(jué)應(yīng)該也是這樣,下面說(shuō)一下它的處理過(guò)程。
簡(jiǎn)單說(shuō)一下bean的加載過(guò)程,當(dāng)spring啟動(dòng)的時(shí)候,首先加載進(jìn)beanFactory的是beanDefinition,之后會(huì)根據(jù)beanDefinition判斷其是否為sington并且為非抽象類非懶加載,那么之后會(huì)去創(chuàng)建bean,
bean的創(chuàng)建分為三步:
1.調(diào)用構(gòu)造方法創(chuàng)建對(duì)象實(shí)例(這一步完成之后其它對(duì)象實(shí)例就可以引用它)
2.填充實(shí)例內(nèi)部屬性(會(huì)依次從三級(jí)緩存中獲取依賴的bean,如果沒(méi)有找到,則會(huì)先去創(chuàng)建依賴的bean,之后再返回繼續(xù)填充屬性)
3.執(zhí)行initializeBean方法,進(jìn)行初始化
當(dāng)bean進(jìn)行創(chuàng)建時(shí),會(huì)先調(diào)用getbean方法->執(zhí)行doGetBean方法,在doGetBean方法中會(huì)調(diào)用getSingleton方法,這一步就是從三級(jí)緩存中獲取對(duì)象緩存,因?yàn)槭莿傞_(kāi)始創(chuàng)建bean所以緩存中肯定沒(méi)有,之后會(huì)調(diào)用createBean方法,在createBean方法中會(huì)調(diào)用doCreateBean執(zhí)行bean的創(chuàng)建過(guò)程就是上面的那三步,當(dāng)bean創(chuàng)建成功之后會(huì)將其放入一級(jí)緩存之中,此時(shí)會(huì)將它從三級(jí)和二級(jí)緩存中刪除。
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//從緩存中獲取實(shí)例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//省略代碼
} else {
//省略代碼
createBean(beanName, mbd, args);
}
//將創(chuàng)建完成的bean放入一級(jí)緩存
addSingleton(beanname,object)
}
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
//省略代碼
doCreateBean()
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
//根據(jù)構(gòu)造方法創(chuàng)建對(duì)象實(shí)例
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
//將對(duì)象實(shí)例放入第三級(jí)緩存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
//實(shí)例內(nèi)部屬性填充
populateBean(beanName, mbd, instanceWrapper);
//初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
其中我們可以看到當(dāng)?shù)谝徊綀?zhí)行完畢后會(huì)將剛剛創(chuàng)建的實(shí)例放入singletonFactories(第三級(jí)緩存)中,那么我們下面了解下到底什么是spring的三級(jí)緩存。處于最上層的緩存是singletonObjects,它其中存儲(chǔ)的對(duì)象是完成創(chuàng)建好,可以正常使用的bean,二級(jí)緩存叫做earlySingletonObjects,它其中存儲(chǔ)的bean是僅執(zhí)行了第一步通過(guò)構(gòu)造方法實(shí)例化,并沒(méi)有填充屬性和初始化,第三級(jí)緩存singletonFactories是一個(gè)工場(chǎng)。
/** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
其實(shí)在getSingleton方法中會(huì)首先從一級(jí)緩存中獲取bean,一級(jí)緩存中沒(méi)有再?gòu)亩?jí)緩存中獲取,二級(jí)也沒(méi)有就會(huì)從三級(jí)中獲取factory當(dāng)factory不為null時(shí),則會(huì)調(diào)用getObject方法獲取bean,并將bean放入二級(jí)緩存,之后再?gòu)娜?jí)緩存中刪除該key-value對(duì),代碼如下:
protected Object 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;
}
那么到此處我們就可以看出為什么不能在構(gòu)造方法中存在循環(huán)依賴了,假如現(xiàn)在有a、b兩個(gè)service,它們兩個(gè)互相在構(gòu)造方法中循環(huán)依賴,當(dāng)項(xiàng)目啟動(dòng),創(chuàng)建a的bean時(shí)執(zhí)行第一步通過(guò)構(gòu)造方法創(chuàng)建實(shí)例,但是發(fā)現(xiàn)依賴b的bean,所以就從三級(jí)緩存中獲取,但是沒(méi)發(fā)現(xiàn),那么就先掛起a的創(chuàng)建過(guò)程,先去創(chuàng)建b,在b創(chuàng)建過(guò)程中,又依賴于a,但是三級(jí)緩存中也沒(méi)有a的bean,這樣就進(jìn)入了一個(gè)循環(huán)創(chuàng)建的過(guò)程,自然是不可取的。
而內(nèi)部field scope為prototype為何也會(huì)報(bào)錯(cuò)呢,當(dāng)scope為prototype每次引用它時(shí)都會(huì)創(chuàng)建一個(gè)新的對(duì)象,所以也會(huì)存在循環(huán)創(chuàng)建的過(guò)程。
而默認(rèn)情況下bean的scope為singleton,整個(gè)容器中僅有整個(gè)service的一個(gè)bean,還是假如a、b兩service存在field循環(huán)依賴,當(dāng)創(chuàng)建a的bean時(shí),執(zhí)行完構(gòu)造方法后,a的實(shí)例已生成,將其factory對(duì)象存入第三級(jí)緩存singletonFactories中,在填充屬性時(shí),發(fā)現(xiàn)依賴b的bean,但是在緩存中沒(méi)有b的bean;因此轉(zhuǎn)而去創(chuàng)建b,在此過(guò)程中執(zhí)行完b的構(gòu)造方法后將其factory也放入三級(jí)緩存,此時(shí)執(zhí)行b的屬性填充,發(fā)現(xiàn)依賴a,從三級(jí)緩存中獲取a的對(duì)象,并將a放入二級(jí)緩存中,之后執(zhí)行intialize初始化,最后將b的bean轉(zhuǎn)入一級(jí)緩存;再繼續(xù)回來(lái)創(chuàng)建a,這個(gè)時(shí)候發(fā)現(xiàn)在一級(jí)緩存中已經(jīng)有了b,那么屬性填充成功,進(jìn)行初始化,最后a也放入一級(jí)緩存,至此執(zhí)行完畢。
那么大家可能會(huì)感到疑惑,為什么要使用三級(jí)緩存呢,感覺(jué)沒(méi)有singletonFactories使用二級(jí)緩存也可以呀?
從前面的代碼里可以看到向第三級(jí)緩存中放置的是一個(gè)objectFactory的匿名實(shí)現(xiàn)類,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)),當(dāng)我們從singletonFactories中獲取objectFctory然后調(diào)用getObject方法獲取bean的時(shí)候,實(shí)際就是通過(guò)getEarlyBeanReference獲取的object,那么進(jìn)入這個(gè)方法看一下。
它在這里會(huì)獲取所有的beanPostProcessor實(shí)現(xiàn)類,然后從中找出實(shí)現(xiàn)了SmartInstantiationAwareBeanPostProcessor的beanPostProcessor,然后調(diào)用它的getEarlyBeanReference(obgect,beanName)方法,對(duì)bean進(jìn)行處理,然后進(jìn)行返回,這些實(shí)現(xiàn)類中就有aop的核心AbstractAutoProxyCreator,從這里我們就可以看出來(lái),從第三級(jí)緩存objectFactory中獲取的obejct是經(jīng)過(guò)了處理的一個(gè)代理對(duì)象,個(gè)人理解三級(jí)緩存就是為了獲取在創(chuàng)建對(duì)象的過(guò)程中提前對(duì)其進(jìn)行一些擴(kuò)展操作。
三級(jí)緩存實(shí)現(xiàn)bean的擴(kuò)展,將代理對(duì)象放入二級(jí)緩存中,供其他依賴該bean的對(duì)象的使用,如果沒(méi)有了三級(jí)緩存,將bean擴(kuò)展放在二級(jí)緩存中實(shí)現(xiàn),那么如果有bean a被其他多個(gè)bean依賴,那么在其他bean填充屬性的過(guò)程中會(huì)多次獲取bean a,這個(gè)過(guò)程中就會(huì)多次執(zhí)行獲取bean a代理,就有些多余,而三級(jí)緩存結(jié)構(gòu)就是在第三級(jí)緩存完成bean的擴(kuò)展,生成代理對(duì)象,放入二級(jí)緩存之中,供其他bean獲取。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
MyBatis-Plus中公共字段的統(tǒng)一處理的實(shí)現(xiàn)
在開(kāi)發(fā)中經(jīng)常遇到多個(gè)實(shí)體類有共同的屬性字段,這些字段屬于公共字段,本文主要介紹了MyBatis-Plus中公共字段的統(tǒng)一處理的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08
舉例講解Java設(shè)計(jì)模式編程中Decorator裝飾者模式的運(yùn)用
這篇文章主要介紹了Java設(shè)計(jì)模式編程中Decorator裝飾者模式的運(yùn)用,裝飾者模式就是給一個(gè)對(duì)象動(dòng)態(tài)的添加新的功能,裝飾者和被裝飾者實(shí)現(xiàn)同一個(gè)接口,裝飾者持有被裝飾者的實(shí)例,需要的朋友可以參考下2016-05-05
深入解析System.load 與 System.loadLibrary
以下是對(duì)System.load與System.loadLibrary進(jìn)行了詳細(xì)的分析介紹。需要的朋友可以過(guò)來(lái)參考下2013-08-08
SpringBoot 定制化返回?cái)?shù)據(jù)的實(shí)現(xiàn)示例
這篇文章主要介紹了SpringBoot 定制化返回?cái)?shù)據(jù)的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
5分鐘快速學(xué)會(huì)spring boot整合Mybatis的方法
這篇文章主要給大家介紹了如何通過(guò)5分鐘快速學(xué)會(huì)spring boot整合Mybatis的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12

