Spring boot:2.2.1
jdk:1.8
所谓的Spring加载过程的第一阶段是指下面这个地方:
AbstractApplicationContext的refrsh方法的registerBeanPostProcessors(beanFactory);会进入到下面到方法里面:PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List regularPostProcessors = new ArrayList<>();
List registryProcessors = new ArrayList<>();
// 省略
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); //第一次调用
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); //第二次调用
// 后续代码省略
在注释的第一次调用的地方,虽然我们自己的实现类实现了PriorityOrdered接口,但是依然拿不到。
检查一下beanFactory的beanDefinitionNames,这里面只有7个。而且都是内部使用的一些bean。
我们自己的bean还没有被加载。所以拿不到。那这几个内部使用的bean是在哪里加载的呢。
还得看SpringApplication.run方法。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 省略其他
}
在createApplicationContext方法里面,进去debug,会通过反射的方式创建AnnotationConfigApplicationContext,
在创建它的过程中,先先初始化其父类里面的DefaultListableBeanFactory,这时候,这个beanFactory里面没有管理任何bean。
然后进入到构造方法。
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
实例化reader的过程中,就会开始加载内部使用的bean了。具体进入到AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);里面查看。
那么如果我想要在比较早的阶段就要初始化bean,或者初始化一些其他资源怎么搞?
方案一:事件监听
我们可以监听ApplicationContextInitializedEvent事件,而这个事件又是在如下过程中发布的。
SpringApplication.run
->
SpringApplicationRunListeners listeners = getRunListeners(args);
->
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
->
applyInitializers(context);
listeners.contextPrepared(context);
而这个事件在Spring的refresh之前发生,所以达到提前初始化资源的目的。
同时需要在spring.factory里面进行如下配置
# Application Listeners
org.springframework.context.ApplicationListener=\
com.demo.InitResourceListener
方案二:使用Initializers
还是上面方案1的流程,看applyInitializers方法,他会调用实现了ApplicationContextInitializer接口的具体实现类。
那么现在需要找一下这些initializers是如何被赋值的?
依然在SpringApplication的类里面
/**
* Sets the {@link ApplicationContextInitializer} that will be applied to the Spring
* {@link ApplicationContext}.
* @param initializers the initializers to set
*/
public void setInitializers(Collection extends ApplicationContextInitializer>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
/**
* Add {@link ApplicationContextInitializer}s to be applied to the Spring
* {@link ApplicationContext}.
* @param initializers the initializers to add
*/
public void addInitializers(ApplicationContextInitializer>... initializers) {
this.initializers.addAll(Arrays.asList(initializers));
}
然后在setInitializers方法里面debug,看哪里调用了这个方法。结果发现入口是在构造SpringApplication
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 从spring.factory里面加载ApplicationContextInitializer的实现类
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
所以同时需要在spring.factory里面添加如下配置。
org.springframework.context.ApplicationContextInitializer=\
com.demo.InitResourceInitializer