SpringBoot优先加载指定Bean的实现

SpringBoot框架在启动时可以自动将托管的Bean实例化,一般情况下它的依赖注入特性可以正确处理Bean之间的依赖关系,无需手动指定某个 Bean优先创建实例,文中有详细的代码示例,需要的朋友可以参考下

1. 背景

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

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

  • 首先我们要知道,SpringBoot 源码中 Bean 对象的实例化都是从 AbstractApplicationContext#refresh() 方法开始的。这个方法包含了容器中对象创建的主流程,主要分为以下几步:
  • BeanFactory 对象工厂的获取及内置配置
  • BeanFactory 对象工厂的后置处理,主要是通过 BeanFactoryPostProcessor 添加、修改注册到容器中的 BeanDefinition,BeanFactoryPostProcessor 的子类实现 BeanDefinitionRegistryPostProcessor在执行顺序上优先级更高
  • BeanDefinitionRegistryPostProcessor 的来源分为两类,一类是直接 new 创建后添加到容器,这种在执行顺序上优先级更高;另一类是框架内部封装为 BeanDefinition 后通过对象工厂使用反射创建,典型如 ConfigurationClassPostProcessor
  • 对于通过 @Component 等注解托管给容器的类,主要由ConfigurationClassPostProcessor 这个 Bean 工厂后置处理器将其扫描封装为 BeanDefinition 并注册,有兴趣的读者可参考 SpringBoot 注解 @Import 的原理-ConfigurationClassPostProcessor 源码解析
  • BeanPostProcessor 对象后置处理器的实例化
  • 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(); } } } 
  • 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(); } 
  • 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 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) ((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) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } } 

3. 实现方式

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

  • ApplicationContextInitializer
  • ApplicationListener

3.1 实现 ApplicationListener 监听初始化事件

该方式实现的步骤如下:

  • 在 SpringBoot 主类中调用 SpringApplication#addListeners() 方法添加一个 ContextInitializedListener 监听器
  • ContextInitializedListener 监听 ApplicationContextInitializedEvent事件,在事件触发的时候往容器中注册指定的 BeanDefinitionRegistryPostProcessor 后置处理器
  • 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, 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, 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优先加载指定Bean的实现的详细内容,更多关于SpringBoot加载Bean的资料请关注0133技术站其它相关文章!

以上就是SpringBoot优先加载指定Bean的实现的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » Java