最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot優(yōu)先加載指定Bean的實(shí)現(xiàn)

 更新時(shí)間:2023年05月06日 10:59:30   作者:談?wù)?974  
SpringBoot框架在啟動(dòng)時(shí)可以自動(dòng)將托管的Bean實(shí)例化,一般情況下它的依賴注入特性可以正確處理Bean之間的依賴關(guān)系,無(wú)需手動(dòng)指定某個(gè) Bean優(yōu)先創(chuàng)建實(shí)例,文中有詳細(xì)的代碼示例,需要的朋友可以參考下

1. 背景

SpringBoot 框架在啟動(dòng)時(shí)可以自動(dòng)將托管的 Bean 實(shí)例化,一般情況下它的 依賴注入特性 可以正確處理 Bean 之間的依賴關(guān)系,無(wú)需手動(dòng)指定某個(gè) Bean 優(yōu)先創(chuàng)建實(shí)例。但是一些特殊的需求確實(shí)需要某個(gè) Bean 優(yōu)先實(shí)例化,要實(shí)現(xiàn)這樣的需求就要對(duì) Bean 對(duì)象的創(chuàng)建順序有一定了解

2. Bean 對(duì)象的創(chuàng)建順序分析

  • 首先我們要知道,SpringBoot 源碼中 Bean 對(duì)象的實(shí)例化都是從 AbstractApplicationContext#refresh() 方法開始的。這個(gè)方法包含了容器中對(duì)象創(chuàng)建的主流程,主要分為以下幾步:
  • BeanFactory 對(duì)象工廠的獲取及內(nèi)置配置
  • BeanFactory 對(duì)象工廠的后置處理,主要是通過 BeanFactoryPostProcessor 添加、修改注冊(cè)到容器中的 BeanDefinition,BeanFactoryPostProcessor 的子類實(shí)現(xiàn) BeanDefinitionRegistryPostProcessor在執(zhí)行順序上優(yōu)先級(jí)更高
  • BeanDefinitionRegistryPostProcessor 的來源分為兩類,一類是直接 new 創(chuàng)建后添加到容器,這種在執(zhí)行順序上優(yōu)先級(jí)更高;另一類是框架內(nèi)部封裝為 BeanDefinition 后通過對(duì)象工廠使用反射創(chuàng)建,典型如 ConfigurationClassPostProcessor
  • 對(duì)于通過 @Component 等注解托管給容器的類,主要由ConfigurationClassPostProcessor 這個(gè) Bean 工廠后置處理器將其掃描封裝為 BeanDefinition 并注冊(cè),有興趣的讀者可參考 SpringBoot 注解 @Import 的原理-ConfigurationClassPostProcessor 源碼解析
  • BeanPostProcessor 對(duì)象后置處理器的實(shí)例化
  • Bean 對(duì)象創(chuàng)建及其 BeanPostProcessor 后置處理器在創(chuàng)建對(duì)象時(shí)的切面應(yīng)用,這部分邏輯主要在 AbstractApplicationContext#finishBeanFactoryInitialization() 方法中
 @Override
 public void refresh() throws BeansException, IllegalStateException {
 	synchronized (this.startupShutdownMonitor) {
 		// Prepare this context for refreshing.
 		prepareRefresh();

 		// Tell the subclass to refresh the internal bean factory.
 		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

 		// Prepare the bean factory for use in this context.
 		prepareBeanFactory(beanFactory);

 		try {
 			// Allows post-processing of the bean factory in context subclasses.
 			postProcessBeanFactory(beanFactory);

 			// Invoke factory processors registered as beans in the context.
 			invokeBeanFactoryPostProcessors(beanFactory);

 			// Register bean processors that intercept bean creation.
 			registerBeanPostProcessors(beanFactory);

 			// Initialize message source for this context.
 			initMessageSource();

 			// Initialize event multicaster for this context.
 			initApplicationEventMulticaster();

 			// Initialize other special beans in specific context subclasses.
 			onRefresh();

 			// Check for listener beans and register them.
 			registerListeners();

 			// Instantiate all remaining (non-lazy-init) singletons.
 			finishBeanFactoryInitialization(beanFactory);

 			// Last step: publish corresponding event.
 			finishRefresh();
 		}

 		catch (BeansException ex) {
 			if (logger.isWarnEnabled()) {
 				logger.warn("Exception encountered during context initialization - " +
 						"cancelling refresh attempt: " + ex);
 			}

 			// Destroy already created singletons to avoid dangling resources.
 			destroyBeans();

 			// Reset 'active' flag.
 			cancelRefresh(ex);

 			// Propagate exception to caller.
 			throw ex;
 		}

 		finally {
 			// Reset common introspection caches in Spring's core, since we
 			// might not ever need metadata for singleton beans anymore...
 			resetCommonCaches();
 		}
 	}
}
  • AbstractApplicationContext#finishBeanFactoryInitialization() 方法的核心是調(diào)用 DefaultListableBeanFactory#preInstantiateSingletons() 方法實(shí)例化 Bean 對(duì)象
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
 	// Initialize conversion service for this context.
 	if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
 			beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
 		beanFactory.setConversionService(
 				beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
 	}

 	// Register a default embedded value resolver if no bean post-processor
 	// (such as a PropertyPlaceholderConfigurer bean) registered any before:
 	// at this point, primarily for resolution in annotation attribute values.
 	if (!beanFactory.hasEmbeddedValueResolver()) {
 		beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
 	}

 	// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
 	String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
 	for (String weaverAwareName : weaverAwareNames) {
 		getBean(weaverAwareName);
 	}

 	// Stop using the temporary ClassLoader for type matching.
 	beanFactory.setTempClassLoader(null);

 	// Allow for caching all bean definition metadata, not expecting further changes.
 	beanFactory.freezeConfiguration();

 	// Instantiate all remaining (non-lazy-init) singletons.
 	beanFactory.preInstantiateSingletons();
}
  • DefaultListableBeanFactory#preInstantiateSingletons() 方法會(huì)遍歷容器內(nèi)部的 beanDefinitionNames列表 進(jìn)行 Bean 實(shí)例化,也就說這個(gè)列表的順序就決定了 Bean 的創(chuàng)建順序,而實(shí)際上 beanDefinitionNames列表 中的元素是 BeanDefinition 注冊(cè)到 BeanDefinitionRegistry 時(shí)產(chǎn)生的

在 本節(jié)步驟1第2步 中,筆者提到通過 @Component 等注解托管給容器的類主要由 ConfigurationClassPostProcessor 掃描注冊(cè),那么要想讓指定的 Bean 優(yōu)先加載,只需要在 ConfigurationClassPostProcessor 掃描之前注冊(cè)指定 Bean 即可

	@Override
 public void preInstantiateSingletons() throws BeansException {
 	if (logger.isTraceEnabled()) {
 		logger.trace("Pre-instantiating singletons in " + this);
 	}

 	// Iterate over a copy to allow for init methods which in turn register new bean definitions.
 	// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
 	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

 	// Trigger initialization of all non-lazy singleton beans...
 	for (String beanName : beanNames) {
 		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
 		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
 			if (isFactoryBean(beanName)) {
 				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
 				if (bean instanceof FactoryBean) {
 					final FactoryBean<?> factory = (FactoryBean<?>) bean;
 					boolean isEagerInit;
 					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
 						isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
 										((SmartFactoryBean<?>) factory)::isEagerInit,
 								getAccessControlContext());
 					}
 					else {
 						isEagerInit = (factory instanceof SmartFactoryBean &&
 								((SmartFactoryBean<?>) factory).isEagerInit());
 					}
 					if (isEagerInit) {
 						getBean(beanName);
 					}
 				}
 			}
 			else {
 				getBean(beanName);
 			}
 		}
 	}

 	// Trigger post-initialization callback for all applicable beans...
 	for (String beanName : beanNames) {
 		Object singletonInstance = getSingleton(beanName);
 		if (singletonInstance instanceof SmartInitializingSingleton) {
 			final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
 			if (System.getSecurityManager() != null) {
 				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
 					smartSingleton.afterSingletonsInstantiated();
 					return null;
 				}, getAccessControlContext());
 			}
 			else {
 				smartSingleton.afterSingletonsInstantiated();
 			}
 		}
 	}
 }

3. 實(shí)現(xiàn)方式

經(jīng)過上一節(jié)分析,我們知道只要找到一個(gè)切入點(diǎn),在 ConfigurationClassPostProcessor 掃描注冊(cè) Bean 之前注冊(cè)指定 Bean 到容器中就能實(shí)現(xiàn)優(yōu)先加載。SpringBoot 提供了不少這樣的切入點(diǎn),本文主要涉及如下兩個(gè):

  • ApplicationContextInitializer
  • ApplicationListener

3.1 實(shí)現(xiàn) ApplicationListener 監(jiān)聽初始化事件

該方式實(shí)現(xiàn)的步驟如下:

  • 在 SpringBoot 主類中調(diào)用 SpringApplication#addListeners() 方法添加一個(gè) ContextInitializedListener 監(jiān)聽器
  • ContextInitializedListener 監(jiān)聽 ApplicationContextInitializedEvent事件,在事件觸發(fā)的時(shí)候往容器中注冊(cè)指定的 BeanDefinitionRegistryPostProcessor 后置處理器
  • BeanDefinitionRegistryPostProcessor 后置處理器實(shí)現(xiàn)類在 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry() 方法中將指定 Bean 注冊(cè)到容器中,從而實(shí)現(xiàn)優(yōu)先加載
@SpringBootApplication()
public class ApiApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(ApiApplication.class);
        application.addListeners(new ContextInitializedListener());
        application.run(args);
    }


    static class ContextInitializedListener implements ApplicationListener<ApplicationContextInitializedEvent>, BeanDefinitionRegistryPostProcessor {

        @Override
        public void onApplicationEvent(ApplicationContextInitializedEvent event) {
            AbstractApplicationContext context = (AbstractApplicationContext) event.getApplicationContext();
            context.addBeanFactoryPostProcessor(this);
        }

        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            registry.registerBeanDefinition("example", new RootBeanDefinition(ContentDTO.class));
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        }
    }
}

3.2 實(shí)現(xiàn) ApplicationContextInitializer

該方式實(shí)現(xiàn)的原理與事件監(jiān)聽類似,不再贅述

@SpringBootApplication()
public class ApiApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(ApiApplication.class);
        application.addInitializers(new MyApplicationContextInitializer());
        application.run(args);
    }


    static class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, BeanDefinitionRegistryPostProcessor {

        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            applicationContext.addBeanFactoryPostProcessor(this);
        }

        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            registry.registerBeanDefinition("example", new RootBeanDefinition(ContentDTO.class));
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        }
    }
}

以上就是SpringBoot優(yōu)先加載指定Bean的實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot加載Bean的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring Boot中防止遞歸查詢的兩種方式

    Spring Boot中防止遞歸查詢的兩種方式

    這篇文章主要給大家介紹了關(guān)于Spring Boot中防止遞歸查詢的兩種方式,兩種方式分別是在application.properties中配置和在entity中添加注解,都給出了詳細(xì)的示例代碼,需要的朋友們下面來一起看看吧。
    2017-06-06
  • Java讀取Properties文件的七種方法的總結(jié)

    Java讀取Properties文件的七種方法的總結(jié)

    這篇文章主要介紹了Java讀取Properties文件的七種方法的總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • Java Testcontainers庫(kù)實(shí)現(xiàn)測(cè)試功能

    Java Testcontainers庫(kù)實(shí)現(xiàn)測(cè)試功能

    這篇文章主要介紹了Java Testcontainers庫(kù)實(shí)現(xiàn)測(cè)試功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Eclipse添加xml文件提示及Hibernate配置學(xué)習(xí)

    Eclipse添加xml文件提示及Hibernate配置學(xué)習(xí)

    文件提示功能在開發(fā)過程中很實(shí)用的,本文實(shí)現(xiàn)了一個(gè)Eclipse添加xml文件提示,感興趣的朋友可以了解下啊,希望本文對(duì)你有所幫助
    2013-01-01
  • Hibernate連接三種數(shù)據(jù)庫(kù)的配置文件

    Hibernate連接三種數(shù)據(jù)庫(kù)的配置文件

    今天小編就為大家分享一篇關(guān)于Hibernate連接三種數(shù)據(jù)庫(kù)的配置文件,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • 解決HttpServletResponse和HttpServletRequest取值的2個(gè)坑

    解決HttpServletResponse和HttpServletRequest取值的2個(gè)坑

    這篇文章主要介紹了解決HttpServletResponse和HttpServletRequest取值的2個(gè)坑問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Java反射及性能詳細(xì)

    Java反射及性能詳細(xì)

    這篇文章主要介紹了Java反射及性能,現(xiàn)如今的java工程中,反射的使用無(wú)處無(wú)在。無(wú)論是設(shè)計(jì)模式中的代理模式,還是紅透半邊天的Spring框架中的IOC,AOP等等,都存在大量反射的影子。下面我們就對(duì)該話題進(jìn)行詳細(xì)介紹,感興趣的小伙伴可以參考一下
    2021-10-10
  • java文件的簡(jiǎn)單讀寫操作方法實(shí)例分析

    java文件的簡(jiǎn)單讀寫操作方法實(shí)例分析

    這篇文章主要介紹了java文件的簡(jiǎn)單讀寫操作方法,結(jié)合實(shí)例形式分析了java文件流進(jìn)行讀寫操作的方法與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2020-05-05
  • JAVA maven項(xiàng)目使用釘釘SDK獲取token、用戶

    JAVA maven項(xiàng)目使用釘釘SDK獲取token、用戶

    這篇文章主要介紹了JAVA maven項(xiàng)目使用釘釘SDK獲取token、用戶,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 從入門到精通:Java List遍歷修改的完全指南

    從入門到精通:Java List遍歷修改的完全指南

    在Java中,我們經(jīng)常需要遍歷List集合并對(duì)其中的元素進(jìn)行修改,這可以通過使用迭代器或for循環(huán)來實(shí)現(xiàn),需要的朋友可以參考下
    2023-10-10

最新評(píng)論

阿城市| 梨树县| 溧阳市| 洛阳市| 枝江市| 棋牌| 望谟县| 宁都县| 凌海市| 贡山| 桃源县| 余姚市| 云南省| 广东省| 奇台县| 偏关县| 通江县| 保定市| 定南县| 新野县| 霞浦县| 即墨市| 祁门县| 南乐县| 巨野县| 衡阳县| 洞口县| 济阳县| 翁牛特旗| 资溪县| 凯里市| 桃江县| 大石桥市| 芜湖市| 新乐市| 杂多县| 柳州市| 天台县| 洛浦县| 淮滨县| 沙雅县|