欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot預(yù)加載與懶加載實現(xiàn)方法超詳細(xì)講解

 更新時間:2022年11月17日 17:03:17   作者:氵奄不死的魚  
Spring一直被詬病啟動時間慢,可Spring/SpringBoot是輕量級的框架。因為當(dāng)Spring項目越來越大的時候,在啟動時加載和初始化Bean就會變得越來越慢,很多時候我們在啟動時并不需要加載全部的Bean,在調(diào)用時再加載就行,那這就需要預(yù)加載與懶加載的功能了

預(yù)加載

bean在springBoot啟動過程中就完成創(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);
        }
        //所有要進行初始的Bean
        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
        // 對所有Bean進行初始化,除了懶加載
        for (String beanName : beanNames) {
            // 去合并Bean
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            //非抽象、單例、懶加載才會進行注冊
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                //判斷是不是 FactoryBean,簡單說是我們這個 Bean 實現(xiàn)了
                if (isFactoryBean(beanName)) {
                    // 是不是 FactoryBean,獲取 FactoryBean 的方式就是  前綴+beanName
                    Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                    // 判斷是否 FactoryBean
                    if (bean instanceof FactoryBean) {
                        // 強轉(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 {
                            // 判斷 是不是 這個類的,如果是就去創(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 需要實現(xiàn)這個接口~~~
            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去了,這里其實有兩個動作

將取出來的 BeanDefinition 進行合并

將BeanDefinition 轉(zhuǎn)換成 RottBeanDefinition 也就說頂級的 Bean ,此處的頂級Bean 指的就是User extends SuperUser,可以認(rèn)為是是 User,也可以認(rèn)為是 SuperUser,如果是 User 就代表了已經(jīng)進行了合并,如果 SuperUser 由于其本身就是頂級類,所以不需要合并,這里會排除掉 Object

需要注意的是第一次 從 mergedBeanDefinitions 是從 當(dāng)前的 BeanFactory 中查找

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    //從當(dāng)前 BeanFactory 中的緩存中獲取
    RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
    // 不是NUll 并且  沒有過期的話,如果過期了或者修改了 會重新去查找
    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,為什么又一次獲取了呢?因為如果在多線程操作下 可能 這個Bean已經(jīng)被修改了,所以重新獲取一次
                mbd = this.mergedBeanDefinitions.get(beanName);
            }
            //如果緩存中沒有,或者過期了,則會重新創(chuàng)建一個
            if (mbd == null || mbd.stale) {
                previous = mbd;
                //如果 父 parentName 為空
                if (bd.getParentName() == null) {
                    // 如果當(dāng)前類型就是 RootBeanDefinition
                    if (bd instanceof RootBeanDefinition) {
                        // 進行克隆~~~
                        mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
                    } else {
                        // 如果不是就會將 其他 BeanDefinition 轉(zhuǎn)換成 RootBeanDefinition 代表的是當(dāng)前 BeanDefinition 為頂級 Bean
                        mbd = new RootBeanDefinition(bd);
                    }
                }
                //  如果父 BeanDefinition 不為 空,也就代表了 當(dāng)前類存在繼承,如果不理解這段的話,可以看一下<bean parent=""> bean標(biāo)簽中的 paretn 屬性,
                else {
                    // 子bean定義:需要與父bean合并。
                    BeanDefinition pbd;
                    try {
                        String parentBeanName = transformedBeanName(bd.getParentName());
                        // 當(dāng)前的 BeanName 不是 parentBeanName,會去獲取 父BeanDefinition,否則則會去父工廠去獲取
                        if (!beanName.equals(parentBeanName)) {
                            // 獲取 parent BeanDefinition這里會進行一個遞歸操作,
                            pbd = getMergedBeanDefinition(parentBeanName);
                        } else {
                            // 如果當(dāng)前的BeanName 和傳遞的 parentName 一模一樣 則會去父 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);
                    }
                    // 進行合并
                    // 進行合并,這里的合并是指將 父級的Bean 合并到子 中,例如  user extends superUser
                    // 也就說講 super中的 屬性 合并到 user 中
                    // Deep copy with overridden values.
                    mbd = new RootBeanDefinition(pbd);
                    mbd.overrideFrom(bd);
                }
                //設(shè)置成單例,如果之前沒設(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中,進行緩存
                    this.mergedBeanDefinitions.put(beanName, mbd);
                }
            }
            if (previous != null) {
                copyRelevantMergedBeanDefinitionCaches(previous, mbd);
            }
            return mbd;
        }
    }

總結(jié)這里的合并是指將 父級的Bean 合并到子 中,例如 user extends superUser

也就說講 super中的 屬性 合并到 user 中,父類的BeanDefinition會被子類的BeanDefinition繼承。

循環(huán)創(chuàng)建bean

if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->

對于非抽象,單例,非懶加載的bean分別調(diào)用getBean方法

getBean方法比較復(fù)雜,簡單總結(jié)下就創(chuàng)建bean的對象并且創(chuàng)建bean依賴的對象并且注入到當(dāng)前bean完成對bean的初始化

懶加載

@Lazy

在類上加上@Lazy標(biāo)簽,那么就開啟了懶加載

@Service("helloServiceB")
@Lazy
public class HelloServiceB {
············

懶加載指的是,初始化時不會創(chuàng)建實例,在真正被使用到的時候再進行加載。

看了前面的預(yù)加載可以知道,在preInstantiateSingletons方法中會跳過懶加載的bean。

如果懶加載的bean被依賴會怎么樣?

比如又有serviceA依賴了ServiceB

@Service("helloServiceA")
public class HelloServiceA implements HelloService {
    @Autowired
    private HelloService helloServiceB;

那么此時HelloServiceB懶加載會失效

HelloServiceA沒有@Lazy標(biāo)簽會在啟動時預(yù)加載通過getBean方法創(chuàng)建。同時會注入其依賴的bean。serviceB也會被創(chuàng)建。

因此要使懶加載生效,應(yīng)該在HelloServiceA也加@Lazy注解

全局懶加載

一般情況程序在啟動時時有大量的 Bean 需要初始化,例如 數(shù)據(jù)源初始化、緩存初始化等導(dǎo)致應(yīng)用程序啟動非常的慢。在 spring boot 2.2 之前的版本,我們對這些 bean 使用手動增加 @Lazy 注解,來實現(xiàn)啟動時不初始化,業(yè)務(wù)程序在調(diào)用需要時再去初始化,如上代碼修改為即可:

為什么需要全局懶加載

同上文中提到我們需要手動在 bean 增加 @Lazy 注解,這就意味著我們僅能對程序中自行實現(xiàn)的 bean 進行添加。但是現(xiàn)在 spring boot 應(yīng)用中引入了很多第三方 starter ,比如 druid-spring-boot-starter 數(shù)據(jù)源注入、spring-boot-starter-data-redis 緩存等默認(rèn)情況下, 引入即注入了相關(guān) bean 我們無法去修改添加 @Lazy。

spring boot 2.2 新增全局懶加載屬性,開啟后全局 bean 被設(shè)置為懶加載,需要時再去創(chuàng)建

spring:
  main:
    lazy-initialization: true

原理

在SpringApplication#prepareContext方法中

if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}

如果開啟了lazy-initialization,那么添加LazyInitializationBeanFactoryPostProcessor

LazyInitializationBeanFactoryPostProcessor執(zhí)行,會將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);
		}
	}

對于全局懶加載

個別 bean 可以通過設(shè)置 @Lazy(false) 排除,設(shè)置為啟動時加載

@Lazy(false)
@Configuration
public class DemoConfig {}

當(dāng)然也可以指定規(guī)則實現(xiàn) LazyInitializationExcludeFilter 規(guī)則實現(xiàn)排除

@Bean LazyInitializationExcludeFilter integrationLazyInitExcludeFilter() {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E--> return LazyInitializationExcludeFilter.forBeanTypes(DemoConfig.class); }

全局懶加載的好處與問題

當(dāng)項目比較大時。開發(fā)人員本地調(diào)試時,并不需要使用到全部的bean,那么開啟全局懶加載可以節(jié)省很多啟動項目的時間

通過設(shè)置全局懶加載,我們可以減少啟動時的創(chuàng)建任務(wù)從而大幅度的縮減應(yīng)用的啟動時間。但全局懶加載的缺點可以歸納為以下兩點:

  • Http 請求處理時間變長。 這里準(zhǔn)確的來說是第一次 http 請求處理的時間變長,之后的請求不受影響
  • 錯誤不會在應(yīng)用啟動時拋出,不利于早發(fā)現(xiàn)、早解決、早下班。

到此這篇關(guān)于SpringBoot預(yù)加載與懶加載實現(xiàn)方法超詳細(xì)講解的文章就介紹到這了,更多相關(guān)SpringBoot預(yù)加載與懶加載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論