SpringBoot優(yōu)先加載指定Bean的實(shí)現(xiàn)
1. 背景
SpringBoot 框架在啟動時可以自動將托管的 Bean 實(shí)例化,一般情況下它的 依賴注入特性 可以正確處理 Bean 之間的依賴關(guān)系,無需手動指定某個 Bean 優(yōu)先創(chuàng)建實(shí)例。但是一些特殊的需求確實(shí)需要某個 Bean 優(yōu)先實(shí)例化,要實(shí)現(xiàn)這樣的需求就要對 Bean 對象的創(chuàng)建順序有一定了解
2. Bean 對象的創(chuàng)建順序分析
- 首先我們要知道,SpringBoot 源碼中 Bean 對象的實(shí)例化都是從 AbstractApplicationContext#refresh() 方法開始的。這個方法包含了容器中對象創(chuàng)建的主流程,主要分為以下幾步:
- BeanFactory 對象工廠的獲取及內(nèi)置配置
- BeanFactory 對象工廠的后置處理,主要是通過 BeanFactoryPostProcessor 添加、修改注冊到容器中的 BeanDefinition,BeanFactoryPostProcessor 的子類實(shí)現(xiàn) BeanDefinitionRegistryPostProcessor在執(zhí)行順序上優(yōu)先級更高
- BeanDefinitionRegistryPostProcessor 的來源分為兩類,一類是直接 new 創(chuàng)建后添加到容器,這種在執(zhí)行順序上優(yōu)先級更高;另一類是框架內(nèi)部封裝為 BeanDefinition 后通過對象工廠使用反射創(chuàng)建,典型如 ConfigurationClassPostProcessor
- 對于通過 @Component 等注解托管給容器的類,主要由ConfigurationClassPostProcessor 這個 Bean 工廠后置處理器將其掃描封裝為 BeanDefinition 并注冊,有興趣的讀者可參考 SpringBoot 注解 @Import 的原理-ConfigurationClassPostProcessor 源碼解析
- BeanPostProcessor 對象后置處理器的實(shí)例化
- Bean 對象創(chuàng)建及其 BeanPostProcessor 后置處理器在創(chuàng)建對象時的切面應(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 對象
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() 方法會遍歷容器內(nèi)部的 beanDefinitionNames列表 進(jìn)行 Bean 實(shí)例化,也就說這個列表的順序就決定了 Bean 的創(chuàng)建順序,而實(shí)際上 beanDefinitionNames列表 中的元素是 BeanDefinition 注冊到 BeanDefinitionRegistry 時產(chǎn)生的
在 本節(jié)步驟1第2步 中,筆者提到通過 @Component 等注解托管給容器的類主要由 ConfigurationClassPostProcessor 掃描注冊,那么要想讓指定的 Bean 優(yōu)先加載,只需要在 ConfigurationClassPostProcessor 掃描之前注冊指定 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é)分析,我們知道只要找到一個切入點(diǎn),在 ConfigurationClassPostProcessor 掃描注冊 Bean 之前注冊指定 Bean 到容器中就能實(shí)現(xiàn)優(yōu)先加載。SpringBoot 提供了不少這樣的切入點(diǎn),本文主要涉及如下兩個:
- ApplicationContextInitializer
- ApplicationListener
3.1 實(shí)現(xiàn) ApplicationListener 監(jiān)聽初始化事件
該方式實(shí)現(xiàn)的步驟如下:
- 在 SpringBoot 主類中調(diào)用 SpringApplication#addListeners() 方法添加一個 ContextInitializedListener 監(jiān)聽器
- ContextInitializedListener 監(jiān)聽 ApplicationContextInitializedEvent事件,在事件觸發(fā)的時候往容器中注冊指定的 BeanDefinitionRegistryPostProcessor 后置處理器
- BeanDefinitionRegistryPostProcessor 后置處理器實(shí)現(xiàn)類在 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry() 方法中將指定 Bean 注冊到容器中,從而實(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的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java安全?ysoserial?CommonsCollections1示例解析
這篇文章主要介紹了java安全?ysoserial?CommonsCollections1示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10Java實(shí)戰(zhàn)之基于swing的QQ郵件收發(fā)功能實(shí)現(xiàn)
這篇文章主要介紹了Java實(shí)戰(zhàn)之基于swing的QQ郵件收發(fā)功能實(shí)現(xiàn),文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04