SpringBoot預(yù)加載與懶加載實(shí)現(xiàn)方法超詳細(xì)講解
預(yù)加載
bean在springBoot啟動(dòng)過(guò)程中就完成創(chuàng)建加載
在AbstractApplicationContext的refresh方法中
// Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons();
public void preInstantiateSingletons() throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Pre-instantiating singletons in " + this); } //所有要進(jìn)行初始的Bean List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // 對(duì)所有Bean進(jìn)行初始化,除了懶加載 for (String beanName : beanNames) { // 去合并Bean RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); //非抽象、單例、懶加載才會(huì)進(jìn)行注冊(cè) if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //判斷是不是 FactoryBean,簡(jiǎn)單說(shuō)是我們這個(gè) Bean 實(shí)現(xiàn)了 if (isFactoryBean(beanName)) { // 是不是 FactoryBean,獲取 FactoryBean 的方式就是 前綴+beanName Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); // 判斷是否 FactoryBean if (bean instanceof FactoryBean) { // 強(qiáng)轉(zhuǎn) FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged( (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { // 判斷 是不是 這個(gè)類(lèi)的,如果是就去創(chuàng)建 SmartFactoryBean 屬于 FactoryBean 子接口,擁有更加細(xì)粒度操作原數(shù)據(jù)的方式, isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { //獲取具體的Bean getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); // 執(zhí)行所有 單例Bean 的回調(diào),當(dāng)然Bean 需要實(shí)現(xiàn)這個(gè)接口~~~ if (singletonInstance instanceof SmartInitializingSingleton) { SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
getMergedLocalBeanDefinition
這里是去合并Bean去了,這里其實(shí)有兩個(gè)動(dòng)作
將取出來(lái)的 BeanDefinition 進(jìn)行合并
將BeanDefinition 轉(zhuǎn)換成 RottBeanDefinition 也就說(shuō)頂級(jí)的 Bean ,此處的頂級(jí)Bean 指的就是User extends SuperUser,可以認(rèn)為是是 User,也可以認(rèn)為是 SuperUser,如果是 User 就代表了已經(jīng)進(jìn)行了合并,如果 SuperUser 由于其本身就是頂級(jí)類(lèi),所以不需要合并,這里會(huì)排除掉 Object
需要注意的是第一次 從 mergedBeanDefinitions 是從 當(dāng)前的 BeanFactory 中查找
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException { //從當(dāng)前 BeanFactory 中的緩存中獲取 RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName); // 不是NUll 并且 沒(méi)有過(guò)期的話,如果過(guò)期了或者修改了 會(huì)重新去查找 if (mbd != null && !mbd.stale) { //返回當(dāng)前的 return mbd; } // 先去 beanDefinitionMap 中去獲取 BeanDefinition thisBeanDefinition= getBeanDefinition(beanName); // 緩存中未找到,就到 BeanFactory 中尋找 return getMergedBeanDefinition(beanName, thisBeanDefinition); } protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd) throws BeanDefinitionStoreException { //傳遞Name 和BeanDefinition return getMergedBeanDefinition(beanName, bd, null); }
具體的邏輯
protected RootBeanDefinition getMergedBeanDefinition( String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd) throws BeanDefinitionStoreException { // 將 mergedBeanDefinitions 防止線程安全,為什么? // 雖然 ConcurrentHashMap 是線程安全的,但是下面的業(yè)務(wù)并不一定是線程安全的,所以要加鎖 synchronized (this.mergedBeanDefinitions) { RootBeanDefinition mbd = null; RootBeanDefinition previous = null; // 這里為空,代表的是 當(dāng)前的 BeanDefinition 是頂層的 Bean 不存在 嵌套Bean if (containingBd == null) { // 獲取當(dāng)前Bean,為什么又一次獲取了呢?因?yàn)槿绻诙嗑€程操作下 可能 這個(gè)Bean已經(jīng)被修改了,所以重新獲取一次 mbd = this.mergedBeanDefinitions.get(beanName); } //如果緩存中沒(méi)有,或者過(guò)期了,則會(huì)重新創(chuàng)建一個(gè) if (mbd == null || mbd.stale) { previous = mbd; //如果 父 parentName 為空 if (bd.getParentName() == null) { // 如果當(dāng)前類(lèi)型就是 RootBeanDefinition if (bd instanceof RootBeanDefinition) { // 進(jìn)行克隆~~~ mbd = ((RootBeanDefinition) bd).cloneBeanDefinition(); } else { // 如果不是就會(huì)將 其他 BeanDefinition 轉(zhuǎn)換成 RootBeanDefinition 代表的是當(dāng)前 BeanDefinition 為頂級(jí) Bean mbd = new RootBeanDefinition(bd); } } // 如果父 BeanDefinition 不為 空,也就代表了 當(dāng)前類(lèi)存在繼承,如果不理解這段的話,可以看一下<bean parent=""> bean標(biāo)簽中的 paretn 屬性, else { // 子bean定義:需要與父bean合并。 BeanDefinition pbd; try { String parentBeanName = transformedBeanName(bd.getParentName()); // 當(dāng)前的 BeanName 不是 parentBeanName,會(huì)去獲取 父BeanDefinition,否則則會(huì)去父工廠去獲取 if (!beanName.equals(parentBeanName)) { // 獲取 parent BeanDefinition這里會(huì)進(jìn)行一個(gè)遞歸操作, pbd = getMergedBeanDefinition(parentBeanName); } else { // 如果當(dāng)前的BeanName 和傳遞的 parentName 一模一樣 則會(huì)去父 ParentBeanFactory 查找 BeanFactory parent = getParentBeanFactory(); // 如果當(dāng)前是層次 BeanFactory 轉(zhuǎn)換查找,如果不是 拋出異常 if (parent instanceof ConfigurableBeanFactory) { pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } else { throw new NoSuchBeanDefinitionException(parentBeanName, "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without a ConfigurableBeanFactory parent"); } } } catch (NoSuchBeanDefinitionException ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex); } // 進(jìn)行合并 // 進(jìn)行合并,這里的合并是指將 父級(jí)的Bean 合并到子 中,例如 user extends superUser // 也就說(shuō)講 super中的 屬性 合并到 user 中 // Deep copy with overridden values. mbd = new RootBeanDefinition(pbd); mbd.overrideFrom(bd); } //設(shè)置成單例,如果之前沒(méi)設(shè)置的話~~~~ if (!StringUtils.hasLength(mbd.getScope())) { // 默認(rèn)為單例 mbd.setScope(SCOPE_SINGLETON); } // A bean contained in a non-singleton bean cannot be a singleton itself. // Let's correct this on the fly here, since this might be the result of // parent-child merging for the outer bean, in which case the original inner bean // definition will not have inherited the merged outer bean's singleton status. if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) { mbd.setScope(containingBd.getScope()); } // Cache the merged bean definition for the time being // (it might still get re-merged later on in order to pick up metadata changes) // 是否緩存Bean的元數(shù)據(jù) if (containingBd == null && isCacheBeanMetadata()) { // 將當(dāng)前的 BeanDefinitions 放入到map中,進(jìn)行緩存 this.mergedBeanDefinitions.put(beanName, mbd); } } if (previous != null) { copyRelevantMergedBeanDefinitionCaches(previous, mbd); } return mbd; } }
總結(jié)這里的合并是指將 父級(jí)的Bean 合并到子 中,例如 user extends superUser
也就說(shuō)講 super中的 屬性 合并到 user 中,父類(lèi)的BeanDefinition會(huì)被子類(lèi)的BeanDefinition繼承。
循環(huán)創(chuàng)建bean
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->
對(duì)于非抽象,單例,非懶加載的bean分別調(diào)用getBean方法
getBean方法比較復(fù)雜,簡(jiǎn)單總結(jié)下就創(chuàng)建bean的對(duì)象并且創(chuàng)建bean依賴(lài)的對(duì)象并且注入到當(dāng)前bean完成對(duì)bean的初始化
懶加載
@Lazy
在類(lèi)上加上@Lazy標(biāo)簽,那么就開(kāi)啟了懶加載
@Service("helloServiceB") @Lazy public class HelloServiceB { ············
懶加載指的是,初始化時(shí)不會(huì)創(chuàng)建實(shí)例,在真正被使用到的時(shí)候再進(jìn)行加載。
看了前面的預(yù)加載可以知道,在preInstantiateSingletons方法中會(huì)跳過(guò)懶加載的bean。
如果懶加載的bean被依賴(lài)會(huì)怎么樣?
比如又有serviceA依賴(lài)了ServiceB
@Service("helloServiceA") public class HelloServiceA implements HelloService { @Autowired private HelloService helloServiceB;
那么此時(shí)HelloServiceB懶加載會(huì)失效
HelloServiceA沒(méi)有@Lazy標(biāo)簽會(huì)在啟動(dòng)時(shí)預(yù)加載通過(guò)getBean方法創(chuàng)建。同時(shí)會(huì)注入其依賴(lài)的bean。serviceB也會(huì)被創(chuàng)建。
因此要使懶加載生效,應(yīng)該在HelloServiceA也加@Lazy注解
全局懶加載
一般情況程序在啟動(dòng)時(shí)時(shí)有大量的 Bean 需要初始化,例如 數(shù)據(jù)源初始化、緩存初始化等導(dǎo)致應(yīng)用程序啟動(dòng)非常的慢。在 spring boot 2.2 之前的版本,我們對(duì)這些 bean 使用手動(dòng)增加 @Lazy 注解,來(lái)實(shí)現(xiàn)啟動(dòng)時(shí)不初始化,業(yè)務(wù)程序在調(diào)用需要時(shí)再去初始化,如上代碼修改為即可:
為什么需要全局懶加載
同上文中提到我們需要手動(dòng)在 bean 增加 @Lazy 注解,這就意味著我們僅能對(duì)程序中自行實(shí)現(xiàn)的 bean 進(jìn)行添加。但是現(xiàn)在 spring boot 應(yīng)用中引入了很多第三方 starter ,比如 druid-spring-boot-starter 數(shù)據(jù)源注入、spring-boot-starter-data-redis 緩存等默認(rèn)情況下, 引入即注入了相關(guān) bean 我們無(wú)法去修改添加 @Lazy。
spring boot 2.2 新增全局懶加載屬性,開(kāi)啟后全局 bean 被設(shè)置為懶加載,需要時(shí)再去創(chuàng)建
spring:
main:
lazy-initialization: true
原理
在SpringApplication#prepareContext方法中
if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); }
如果開(kāi)啟了lazy-initialization,那么添加LazyInitializationBeanFactoryPostProcessor
LazyInitializationBeanFactoryPostProcessor執(zhí)行,會(huì)將beanFactory中的bean設(shè)置lazyinit
public final class LazyInitializationBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // Take care not to force the eager init of factory beans when getting filters Collection<LazyInitializationExcludeFilter> filters = beanFactory .getBeansOfType(LazyInitializationExcludeFilter.class, false, false).values(); for (String beanName : beanFactory.getBeanDefinitionNames()) { BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); if (beanDefinition instanceof AbstractBeanDefinition) { postProcess(beanFactory, filters, beanName, (AbstractBeanDefinition) beanDefinition); } } } private void postProcess(ConfigurableListableBeanFactory beanFactory, Collection<LazyInitializationExcludeFilter> filters, String beanName, AbstractBeanDefinition beanDefinition) { Boolean lazyInit = beanDefinition.getLazyInit(); if (lazyInit != null) { return; } Class<?> beanType = getBeanType(beanFactory, beanName); if (!isExcluded(filters, beanName, beanDefinition, beanType)) { beanDefinition.setLazyInit(true); } }
對(duì)于全局懶加載
個(gè)別 bean 可以通過(guò)設(shè)置 @Lazy(false) 排除,設(shè)置為啟動(dòng)時(shí)加載
@Lazy(false) @Configuration public class DemoConfig {}
當(dāng)然也可以指定規(guī)則實(shí)現(xiàn) LazyInitializationExcludeFilter 規(guī)則實(shí)現(xiàn)排除
@Bean LazyInitializationExcludeFilter integrationLazyInitExcludeFilter() {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E--> return LazyInitializationExcludeFilter.forBeanTypes(DemoConfig.class); }
全局懶加載的好處與問(wèn)題
當(dāng)項(xiàng)目比較大時(shí)。開(kāi)發(fā)人員本地調(diào)試時(shí),并不需要使用到全部的bean,那么開(kāi)啟全局懶加載可以節(jié)省很多啟動(dòng)項(xiàng)目的時(shí)間
通過(guò)設(shè)置全局懶加載,我們可以減少啟動(dòng)時(shí)的創(chuàng)建任務(wù)從而大幅度的縮減應(yīng)用的啟動(dòng)時(shí)間。但全局懶加載的缺點(diǎn)可以歸納為以下兩點(diǎn):
- Http 請(qǐng)求處理時(shí)間變長(zhǎng)。 這里準(zhǔn)確的來(lái)說(shuō)是第一次 http 請(qǐng)求處理的時(shí)間變長(zhǎng),之后的請(qǐng)求不受影響
- 錯(cuò)誤不會(huì)在應(yīng)用啟動(dòng)時(shí)拋出,不利于早發(fā)現(xiàn)、早解決、早下班。
到此這篇關(guān)于SpringBoot預(yù)加載與懶加載實(shí)現(xiàn)方法超詳細(xì)講解的文章就介紹到這了,更多相關(guān)SpringBoot預(yù)加載與懶加載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Annotation(Java 注解)的實(shí)現(xiàn)代碼
本篇文章介紹了,Java Annotation(Java 注解)的實(shí)現(xiàn)代碼。需要的朋友參考下2013-05-05springcloud-gateway集成knife4j的示例詳解
這篇文章主要介紹了springcloud-gateway集成knife4j的示例詳解,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03解決IDEA中多模塊下Mybatis逆向工程不生成相應(yīng)文件的情況
這篇文章主要介紹了解決IDEA中多模塊下Mybatis逆向工程不生成相應(yīng)文件的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01Jlabel實(shí)現(xiàn)內(nèi)容自動(dòng)換行簡(jiǎn)單實(shí)例
這篇文章主要介紹了Jlabel實(shí)現(xiàn)內(nèi)容自動(dòng)換行簡(jiǎn)單實(shí)例,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01SpringMVC后端Controller頁(yè)面跳轉(zhuǎn)的三種方式匯總
這篇文章主要介紹了SpringMVC后端Controller頁(yè)面跳轉(zhuǎn)的三種方式匯總,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10Java中的ReadWriteLock高效處理并發(fā)讀寫(xiě)操作實(shí)例探究
這篇文章主要為大家介紹了Java中的ReadWriteLock高效處理并發(fā)讀寫(xiě)操作實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01Java?對(duì)象在?JVM?中的內(nèi)存布局超詳細(xì)解說(shuō)
這篇文章主要介紹了Java?對(duì)象在?JVM?中的內(nèi)存布局超詳細(xì)解說(shuō),文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09解決MybatisPlus批量插入數(shù)據(jù)報(bào)錯(cuò):Error getting generated 
在使用MybatisPlus進(jìn)行批量插入數(shù)據(jù)時(shí)遇到空指針異常錯(cuò)誤,分析原因是由于主鍵生成策略導(dǎo)致的,嘗試通過(guò)設(shè)置useGeneratedKeys屬性解決問(wèn)題,但因批量插入方法限制,該方法未能成功,最終通過(guò)自定義mapper方法實(shí)現(xiàn)批量插入,解決了問(wèn)題2024-09-09JavaMail實(shí)現(xiàn)發(fā)送超文本(html)格式郵件的方法
這篇文章主要介紹了JavaMail實(shí)現(xiàn)發(fā)送超文本(html)格式郵件的方法,實(shí)例分析了java發(fā)送超文本文件的相關(guān)技巧,需要的朋友可以參考下2015-05-05