• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

SpringBoot 优先加载指定 Bean 的实现

武飞扬头像
谈谈1974
帮助1

1. 背景

SpringBoot 框架在启动时可以自动将托管的 Bean 实例化,一般情况下它的 依赖注入特性 可以正确处理 Bean 之间的依赖关系,无需手动指定某个 Bean 优先创建实例。但是一些特殊的需求确实需要某个 Bean 优先实例化,要实现这样的需求就要对 Bean 对象的创建顺序有一定了解

2. Bean 对象的创建顺序分析

  1. 首先我们要知道,SpringBoot 源码中 Bean 对象的实例化都是从 AbstractApplicationContext#refresh() 方法开始的。这个方法包含了容器中对象创建的主流程,主要分为以下几步:

    1. BeanFactory 对象工厂的获取及内置配置
    2. BeanFactory 对象工厂的后置处理,主要是通过 BeanFactoryPostProcessor 添加、修改注册到容器中的 BeanDefinitionBeanFactoryPostProcessor 的子类实现 BeanDefinitionRegistryPostProcessor在执行顺序上优先级更高
      1. BeanDefinitionRegistryPostProcessor 的来源分为两类,一类是直接 new 创建后添加到容器,这种在执行顺序上优先级更高;另一类是框架内部封装为 BeanDefinition 后通过对象工厂使用反射创建,典型如 ConfigurationClassPostProcessor
      2. 对于通过 @Component 等注解托管给容器的类,主要由ConfigurationClassPostProcessor 这个 Bean 工厂后置处理器将其扫描封装为 BeanDefinition 并注册,有兴趣的读者可参考 SpringBoot 注解 @Import 的原理-ConfigurationClassPostProcessor 源码解析
    3. BeanPostProcessor 对象后置处理器的实例化
    4. Bean 对象创建及其 BeanPostProcessor 后置处理器在创建对象时的切面应用,这部分逻辑主要在 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();
     		}
     	}
    }
    
    学新通
  2. AbstractApplicationContext#finishBeanFactoryInitialization() 方法的核心是调用 DefaultListableBeanFactory#preInstantiateSingletons() 方法实例化 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();
    }
    
    学新通
  3. DefaultListableBeanFactory#preInstantiateSingletons() 方法会遍历容器内部的 beanDefinitionNames列表 进行 Bean 实例化,也就说这个列表的顺序就决定了 Bean 的创建顺序,而实际上 beanDefinitionNames列表 中的元素是 BeanDefinition 注册到 BeanDefinitionRegistry 时产生的

    本节步骤1第2步 中,笔者提到通过 @Component 等注解托管给容器的类主要由 ConfigurationClassPostProcessor 扫描注册,那么要想让指定的 Bean 优先加载,只需要在 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. 实现方式

经过上一节分析,我们知道只要找到一个切入点,在 ConfigurationClassPostProcessor 扫描注册 Bean 之前注册指定 Bean 到容器中就能实现优先加载。SpringBoot 提供了不少这样的切入点,本文主要涉及如下两个:

  • ApplicationContextInitializer
  • ApplicationListener

3.1 实现 ApplicationListener 监听初始化事件

该方式实现的步骤如下:

  1. 在 SpringBoot 主类中调用 SpringApplication#addListeners() 方法添加一个 ContextInitializedListener 监听器
  2. ContextInitializedListener 监听 ApplicationContextInitializedEvent事件,在事件触发的时候往容器中注册指定的 BeanDefinitionRegistryPostProcessor 后置处理器
  3. BeanDefinitionRegistryPostProcessor 后置处理器实现类在 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry() 方法中将指定 Bean 注册到容器中,从而实现优先加载
@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 实现 ApplicationContextInitializer

该方式实现的原理与事件监听类似,不再赘述

@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 {

        }
    }
}
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgakkbf
系列文章
更多 icon
同类精品
更多 icon
继续加载