SpringBoot优先加载指定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<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 监听初始化事件
该方式实现的步骤如下:
- 在 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<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 {
}
}
}
以上就是SpringBoot优先加载指定Bean的实现的详细内容,更多关于SpringBoot加载Bean的资料请关注其它相关文章!
相关文章