SpringBoot預(yù)加載與懶加載實(shí)現(xiàn)方法超詳細(xì)講解
預(yù)加載
bean在springBoot啟動(dòng)過程中就完成創(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)行注冊
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//判斷是不是 FactoryBean,簡單說是我們這個(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è)類的,如果是就去創(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)作
將取出來的 BeanDefinition 進(jìn)行合并
將BeanDefinition 轉(zhuǎn)換成 RottBeanDefinition 也就說頂級(jí)的 Bean ,此處的頂級(jí)Bean 指的就是User extends SuperUser,可以認(rèn)為是是 User,也可以認(rèn)為是 SuperUser,如果是 User 就代表了已經(jīng)進(jìn)行了合并,如果 SuperUser 由于其本身就是頂級(jí)類,所以不需要合并,這里會(huì)排除掉 Object
需要注意的是第一次 從 mergedBeanDefinitions 是從 當(dāng)前的 BeanFactory 中查找
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
//從當(dāng)前 BeanFactory 中的緩存中獲取
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
// 不是NUll 并且 沒有過期的話,如果過期了或者修改了 會(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);
}
//如果緩存中沒有,或者過期了,則會(huì)重新創(chuàng)建一個(gè)
if (mbd == null || mbd.stale) {
previous = mbd;
//如果 父 parentName 為空
if (bd.getParentName() == null) {
// 如果當(dāng)前類型就是 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)前類存在繼承,如果不理解這段的話,可以看一下<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
// 也就說講 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中,進(jìn)行緩存
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
if (previous != null) {
copyRelevantMergedBeanDefinitionCaches(previous, mbd);
}
return mbd;
}
}總結(jié)這里的合并是指將 父級(jí)的Bean 合并到子 中,例如 user extends superUser
也就說講 super中的 屬性 合并到 user 中,父類的BeanDefinition會(huì)被子類的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é)下就創(chuàng)建bean的對(duì)象并且創(chuàng)建bean依賴的對(duì)象并且注入到當(dāng)前bean完成對(duì)bean的初始化
懶加載
@Lazy
在類上加上@Lazy標(biāo)簽,那么就開啟了懶加載
@Service("helloServiceB")
@Lazy
public class HelloServiceB {
············懶加載指的是,初始化時(shí)不會(huì)創(chuàng)建實(shí)例,在真正被使用到的時(shí)候再進(jìn)行加載。
看了前面的預(yù)加載可以知道,在preInstantiateSingletons方法中會(huì)跳過懶加載的bean。
如果懶加載的bean被依賴會(huì)怎么樣?
比如又有serviceA依賴了ServiceB
@Service("helloServiceA")
public class HelloServiceA implements HelloService {
@Autowired
private HelloService helloServiceB;那么此時(shí)HelloServiceB懶加載會(huì)失效
HelloServiceA沒有@Lazy標(biāo)簽會(huì)在啟動(dòng)時(shí)預(yù)加載通過getBean方法創(chuàng)建。同時(shí)會(huì)注入其依賴的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 注解,來實(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 我們無法去修改添加 @Lazy。
spring boot 2.2 新增全局懶加載屬性,開啟后全局 bean 被設(shè)置為懶加載,需要時(shí)再去創(chuàng)建
spring:
main:
lazy-initialization: true
原理
在SpringApplication#prepareContext方法中
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
如果開啟了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 可以通過設(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); }全局懶加載的好處與問題
當(dāng)項(xiàng)目比較大時(shí)。開發(fā)人員本地調(diào)試時(shí),并不需要使用到全部的bean,那么開啟全局懶加載可以節(jié)省很多啟動(dòng)項(xiàng)目的時(shí)間
通過設(shè)置全局懶加載,我們可以減少啟動(dòng)時(shí)的創(chuàng)建任務(wù)從而大幅度的縮減應(yīng)用的啟動(dòng)時(shí)間。但全局懶加載的缺點(diǎn)可以歸納為以下兩點(diǎn):
- Http 請(qǐng)求處理時(shí)間變長。 這里準(zhǔn)確的來說是第一次 http 請(qǐng)求處理的時(shí)間變長,之后的請(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線程池的幾種實(shí)現(xiàn)方法和區(qū)別介紹實(shí)例詳解
本篇文章主要介紹了Java線程池的幾種實(shí)現(xiàn)方法和區(qū)別,需要的朋友可以參考2017-04-04
Java調(diào)用第三方http接口的四種方式總結(jié)
這篇文章主要給大家介紹了關(guān)于Java調(diào)用第三方http接口的四種方式,在實(shí)際開發(fā)中我們經(jīng)常會(huì)與第三方公司進(jìn)行合作,接入第三方接口,文中給出了詳細(xì)的代碼實(shí)例,需要的朋友可以參考下2023-08-08
MybatisPlus實(shí)現(xiàn)分頁查詢和動(dòng)態(tài)SQL查詢的示例代碼
本文主要介紹了MybatisPlus實(shí)現(xiàn)分頁查詢和動(dòng)態(tài)SQL查詢的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Java項(xiàng)目如何引入日志生成器及其日志分級(jí)
這篇文章主要介紹了Java項(xiàng)目引入日志生成器及其日志分級(jí),本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-12-12
Java如何利用LocalDate獲取某個(gè)月的第一天與最后一天日期
SpringMVC中的DispatcherServlet詳細(xì)解析

