初始化基本流程
SpringApplication的prepareContext准备上下文
这里面有干了很多事,他会将最前面获得的初始化器都初始化,然后广播上下文准备好事件,然后这里居然还设置了不能覆盖同名bean
定义,这样就避免了用户去捣乱了。最后会把启动类的注册到bean
定义里,然后广播上下文加载完成事件。
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);//配置环境
postProcessApplicationContext(context);//一些设置处理
applyInitializers(context);//初始化监听器进行初始化
listeners.contextPrepared(context);//广播上下文准备好的事件ApplicationContextInitializedEvent
if (this.logStartupInfo) {//控制台打启动信息
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)//不允许同名的bean定义的覆盖
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();//获取启动源集合,就是传给SpringApplication的参数类
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));//注册启动类的bean定义
listeners.contextLoaded(context);//广播上下文加载完成事件ApplicationPreparedEvent
}
postProcessApplicationContext处理
就是提前去注册bean
名字生成器,资源加载器,还有前面创建的转换器也要放进来。
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {//添加转换器
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
applyInitializers初始化器初始化
获取最开始创建的初始化器,遍历每一个初始化器,进行初始化。
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);//获取ApplicationContextInitializer接口的泛型类型
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");//context不是requiredType类型是不行的
initializer.initialize(context);//初始化
}
}
load
创建bean
定义加载器,进行bean
定义的加载,就是把sources
注册到bean
定义里。
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
//遍历每一个加载
int load() {
int count = 0;
for (Object source : this.sources) {
count += load(source);
}
return count;
}
根据不同类型加载,最后都是registerBean
:
SpringApplication的refreshContext刷新上下文
除了刷新外,还注册了一个钩子,
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
refresh
调用当前上下文AbstractApplicationContext
类型的refresh
,当前上下文是ServletWebServerApplicationContext
类型的,所以会调用到他的refresh
。
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
ServletWebServerApplicationContext的refresh
然后他又调用父类的refresh
。
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
stopAndReleaseWebServer();
throw ex;
}
}
里面就是spring
的refresh
方法,进行初始化,就不讲了,可以看我写的spring
源码文章,其实内部干了不少事情,后面会讲,毕竟前面那么多初始化器初始化了,肯定会对后面spring
初始化有所作用。
看看钩子方法,其实就是注册一个关闭线程:
刷新完成后基本没啥事了,就进行启动完成事件通知,还有一些的ApplicationRunner
和CommandLineRunner
类型的bean
要处理,一般是没有的:
最后再通知一个运行事件初始化就完成了:
初始化基本讲完了,但是还有好多细节没讲,比如我们的tomcat
怎么启动的呀,哪些自动配置怎么回事,我们下次讲啦。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。