SpringBoot 优先加载指定 Bean 的实现

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 {
    
    

        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_45505313/article/details/130507298