做一个单纯的程序汪,分享干货,聊聊人生。
微信公众号:后端架构进阶
关注我发现更多的干货,微服务、Spring源码、JVM、SpringCloud Alibaba、K8S等。
上一篇我们讨论了Spring是如何获取Bean,感兴趣的记得去翻看。
每天五分钟,学透Spring《一、Spring中是如何获取Bean》
那么这一节我们来聊聊Spring到底是如何来创建一个Bean,也就是Bean的生命周期是啥样的。
上一节我们聊了创建获取Bean的方式,一种是通过xml创建的Bean,另外一种是注解。那么今天我们就来继续细聊下基于注解的方式的内部实现,到底是如何创建一个Bean的。
扒开AnnotationConfigApplicationContext
,我们可以看到:
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
this.register(componentClasses);
this.refresh();
}
一、初始化Bean定义的读取器和扫描器
从上面可以看到,this()方法就是调用他的一个空参构造方法,一般情况,这都是为了进行初始化,是不是这样呢?
我们看到核心就两条。一个是创建了基于注解的Bean定义的读取器,另一个是创建了类路径下Bean定义的扫描器,这样就很明显了。
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
二、注册Bean定义
继续打开我们的register,可以看到会往刚才上一步init()中初始化的读取器中注册一个class。
public void register(Class<?>... componentClasses) {
......
this.reader.register(componentClasses);
......
}
继续跟进去我们能看到如下的代码。spring的代码都有很多约定俗成的规则,就是基本上正在去做这个事情的都会带上do,比如doCreateBean()
、doRegisterBean()
。
public void registerBean(Class<?> beanClass) {
this.doRegisterBean(beanClass, (String)null, (Class[])null, (Supplier)null, (BeanDefinitionCustomizer[])null);
}
具体里面又是干了些啥呢?我们继续跟进去。记住,我们别抓住每一句代码,一定要先把主干给搞清楚,不然很容易绕进去,然后把自己给整懵逼了。
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
......
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
......
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
}
这个方法我们也保留了主要的部分,这样很方便进行查看和理解,当然不是说省略的就不重要,只是我们在初次看的时候这些会干扰我们的理解,等我们把全部都整通透了,再来扒细节那样就非常清晰了。
从上面看来,无非就是创建了一个被注解了的普通Bean定义。然后用Bean定义持有器去包装了下,然后注册Bean定义。
什么是Bean定义?初次见到这个的同学可能有点懵,你就可以把他理解成还未成型的Bean。比如小鸡是通过鸡蛋孵出来的,这个可以理解为创建鸡蛋,然后放到鸡窝里面。
最后依据注册是注册到哪里呢?问得好。
private final Map<String, BeanDefinition> beanDefinitionMap;
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
......
this.beanDefinitionMap.put(beanName, beanDefinition);
......
}
注册到哪里?无非就是放到一个Map去,也就是我们的鸡窝beanDefinitionMap
。是不是其实源码也没那么难,只要坚持着去看,慢慢就熟悉了。ok,我们的register()
就走完了。
三、Spring的核心入口refresh()
这个就是Spring的核心的步骤了,启动我们的容器的时候就会做下面这么多的事情。Spring YYDS。
public void refresh() throws BeansException, IllegalStateException {
//1、做一些初始化操作
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//2、准备Bean工厂需要的东西
this.prepareBeanFactory(beanFactory);
try {
//3、里面是空的,可以做扩展
this.postProcessBeanFactory(beanFactory);
//4、调用Bean工厂的后置处理器
this.invokeBeanFactoryPostProcessors(beanFactory);
//5、注册Bean工厂的后置处理器
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
//6、初始化消息资源
this.initMessageSource();
//7、初始化容器事件多播器
this.initApplicationEventMulticaster();
//8、也是空的,可以扩展
this.onRefresh();
//9、注册监听器
this.registerListeners();
//10、完成Bean工厂的初始化
this.finishBeanFactoryInitialization(beanFactory);
//11、完成刷新
this.finishRefresh();
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
//如果有异常就销毁Bean
this.destroyBeans();
//取消刷新。
this.cancelRefresh(var10);
throw var10;
} finally {
//12、重置一些通用的缓存
this.resetCommonCaches();
//13、刷新结束
contextRefresh.end();
}
}
}
通过上面的这些步骤,我们的Bean就已经全部帮我们初始化完毕,我们使用的时候就可以直接从一级缓存中拿,这里有个三级缓存的概念。之前的文章写过,分析的是解决循环依赖的问题。一级就是存的我们孵化出来的小鸡,也就是我们的Bean。
三级缓存其实就是三个Map,没那么高大上,主要是解决问题的思路和办法。缓存起来也是为了提高我们程序的效率。
四、小结
这篇我们就分析到这里,上面的十多步去初始创建的我们会一个个进行分析,一口吃不成胖子。先知道我们的Bean创建会经历上面的每个步骤。创建完成一个就缓存一个,如果设置了懒加载,那么会使用到的时候才去创建。
每天五分钟,学透Spring,让源码不再难。
总结
以上就是之前的内容的总结和分享,感谢各位大佬的 关注、点赞和 收藏 !
微信公众号:后端架构进阶
更多文章正在赶来,喜欢记得给我点个 👍 ,感谢支持!
公众号文章同步更新!关注我,不迷路!