【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

导读:本篇文章讲解 【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

准备一个干净的Spring项目,创建Spring核心配置文件,然后随便写几句能将Spring项目跑起来的代码。

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

打好断点,我们开始了!

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

F7进入代码中☝

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

这里是WebLogic的一个小问题不用管它,继续F8直至跳出到原来的main方法后,再次F7进入代码中就可以看到真正Spring的启动流程☝

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

加载核心applicationContext.xml配置文件☝

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

super(parent); // 调用父类构造方法,进行相关的对象创建
相当于进行一些初始化的操作

setConfigLocations(configLocations); // 设置配置路径
就是将刚刚加载的applicationContext配置文件读取进来

refresh()这个方法重中之重,F7进入代码中查看☝

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

refresh()中的这13个方法是可谓是整个Spring源码中的灵魂,将这13个方法弄懂明白,可以说等于就弄懂了整个Spring框架的源码了。接下来这些方法一个一个来仔细看下:

prepareRefresh(); 
准备刷新,做一些容器刷新前最基本的准备工作

this.startupDate = System.currentTimeMillis(); // 设置Spring当前启动时间

this.closed.set(false); // 关闭状态记为false
this.active.set(true);  // 活跃状态记为true

initPropertySources();  // 初始化属性资源:当前这一步来说没多大作用

getEnvironment().validateRequiredProperties();
这里注意一下,进入里面看虽然只是简单new一个StandardEnvironment,但是StandardEnvironment继承了AbstractEnvironment。在这个AbstractEnvironment构造方法中,有一个customizePropertySources(this.propertySources),本身没做实现,等到具体执行的时候由子类来实现。

if (this.earlyApplicationListeners == null) {
    this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
} else {
    this.applicationListeners.clear();
    this.applicationListeners.addAll(this.earlyApplicationListeners);
}
准备一些必要的监听器的集合

this.earlyApplicationEvents = new LinkedHashSet<>();
准备一些监听器事件的集合

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
获取一个刷新的Bean容器对象,创建一个工厂,这边需要注意几个具体的实现类及相关的类图关系

打开BeanFactory接口,在注释中有提到两个重要的接口:ListableBeanFactory和ConfigurableBeanFactory
再打开DefaultListableBeanFactory这个类的类图,这个类是接下来使用非常非常多的一个类,将这个类图大概有个印象记住

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

好了,继续回到刚刚的位置,进入到obtainFreshBeanFactory()中,执行refreshBeanFactory()刷新Bean工厂,而在刷新中,会进行判断
if (hasBeanFactory()) {
    destroyBeans();
    closeBeanFactory();
}
如果有Bean工厂,销毁Bean,关闭工厂

try {
    DefaultListableBeanFactory beanFactory = createBeanFactory();
    beanFactory.setSerializationId(getId());
    customizeBeanFactory(beanFactory);
    loadBeanDefinitions(beanFactory);
    this.beanFactory = beanFactory;
}
然后再创建新的Bean工厂,也就是说这么一来,获取的必然是新的工厂。而在createBeanFactory()中,执行的就是return new DefaultListableBeanFactory(getInternalParentBeanFactory())这个方法创建当前的工厂对象。至此,对象工厂也就创建完成

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

工厂创建好以后,接着就要往里面开始设值。所以下一步就会看到
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory); // 这个方法里面进行属性值的设置:
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
beanFactory.setAllowCircularReferences(this.allowCircularReferences);

loadBeanDefinitions(beanFactory); // 设置完属性后,加载配置文件读取
这个方法下特别多各种重载的方法,想方设法各种保证执行完一个完整的加载流程

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

整体上来讲,在这个ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()大方法中:
① 就是在创建容器对象:DefaultListableBeanFactory;
② 加载xml配置文件的属性值到当前工厂中,最重要的就是BeanDefinition;

prepareBeanFactory(beanFactory); // 准备Bean工厂
再上一步骤中创建好了Bean工厂,这一步就需要对创建出来的Bean工厂设置属性值

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

F7进入方法中,走来就是两个set一个add
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);
设置忽略Aware接口的处理,后面会有统一的处理

beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
处理一些依赖

这里的代码几乎一样,不是set就是add,prepareBeanFactory(beanFactory)这个方法基本就是为了完成Bean工厂的初始化操作☝

postProcessBeanFactory(beanFactory);
这个方法F7进去之后,发现里面空了,为了方便后面的扩展操作,用户可以自己实现

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

invokeBeanFactoryPostProcessors(beanFactory);

实例化并执行所有的BeanFactoryPostProcessors Bean,这个方法进入里面虽然深入剖析的话,比较麻烦,后面再细说

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

registerBeanPostProcessors(beanFactory); // 注册实现

F7进入方法中,代码很少就一句PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this),实例化并注册所有的BeanPostProcessors Bean,和上一步一样,其实也是准备工作

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

initMessageSource(); // 初始化消息资源,其实说白了就是一个国际化的操作,可以先不用重点关注

initApplicationEventMulticaster(); // 初始化应用程序时间广播器,当监听器进入到广播事件中以后,可以执行一些对应的操作

onRefresh();

这个方法也是一个空实现,也是留给子类做一些扩展工作,目前不用重点关注,后续用到在细说

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

registerListeners(); 

注册监听器,具体用到哪些监听器,后面用到的时候再细说

可以看到上面的几个方法:
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
其中initMessageSource()用来国际化的不用管,onRefresh()本身就是个空实现,剩余的几个方法都是用来做实例化前的一系列准备

finishBeanFactoryInitialization(beanFactory); // 实例化所有剩下的非懒加载的单例对象
这个方法非常关键,如果说工作中真的会动用到Spring源码级别的话,这个方法是绕不开的一个必经之地

F7进入方法,首先第一步
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));
}
这一步是将类型转换的操作设置进入beanFactory中,这一步不用重点关注

if (!beanFactory.hasEmbeddedValueResolver()) {
    beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
判断当前的beanFactory有没有内置的值处理器

…..

beanFactory.freezeConfiguration();
如果某些Bean的配置不需要进行修改,可以进行冻结

beanFactory.preInstantiateSingletons();
这一步才是核心的实例化所有剩下的非懒加载的单例对象
F7进入方法中,这个方法还是比较复杂,很多关键点在这里面完成

List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); 
把获取的beanDefinitionName塞进一个集合,下面会循环这些名字

RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
RootBeanDefinition是Bean的描述定义信息,而有了这些信息以后,方便下面的实例化操作

if (isFactoryBean(beanName))
判断当前的这个Bean有没有实现FactoryBean接口,没有实现则直接实例化getBean(beanName),进入里面就是doGetBean
而在Spring源码中,do…开头的都是正经实际干活的代码,F7进入方法中

Object sharedInstance = getSingleton(beanName);
Bean实例化之后放入容器中,而容器是放在一堆容器Map中,而在Map中又有一级二级三级缓存,所以这个方法里面其实是先去缓存中找有没有这个实例,没有缓存直接创建

if (isPrototypeCurrentlyInCreation(beanName))
当对象都是单例的时候会尝试解决循环依赖问题,但是原型模式下如果存在循环依赖的情况,则直接抛出异常

if (!typeCheckOnly) {
    markBeanAsCreated(beanName);
}
如果不是做类型检查,那么表示要创建Bean,此处在结合中做一个标记

String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null)
如果存在依赖Bean的话,则优先实例化依赖的Bean

if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> {…..}
}
getSingleton方法里面的doCreateBean()方法才是利用反射机制来正经创建Bean
而在doCreateBean()中,首先是在内存中开辟空间,然后做一些前置的判断处理

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences  &&isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    if (logger.isTraceEnabled()) {
    logger.trace(“Eagerly caching bean ‘” + beanName +”‘ to allow for resolving potential circular references”);
    }
    
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
这几句代码非常重要,解决了一个非常重要的问题:循环依赖
解决循环依赖的一个重要思想就是:提前暴露。在进行对象创建的时候,为什么要把实例化和初始化分的那么清楚,实例化只是完成了当前对象的一个创建,暴露的就是只完成了实例化,还没有完成初始化,此时将对象提前暴露出去。在addSingletonFactory()方法里面,this.singletonFactories.put(beanName, singletonFactory)这一句意思是先放到三级缓存中去。

那么,为什么要使用三级缓存?关键点在于getEarlyBeanReference(beanName, mbd, bean)这个方法中:当需要对象被代理的时候,就必须要三级缓存来实现了。

好,继续往下走,来到:
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
完成当前对象属性值的注入,完成属性值的填充

再往下,registerDisposableBeanIfNecessary(beanName, bean, mbd);
当容器关闭的时候,需要销毁当前的对象。当需要进行销毁的时候,创建一个销毁的方法,确实正确的销毁

当执行完上述这些步骤之后,基本上Bean的实例化就基本完成

finishRefresh();
Bean创建完成之后,完成整体的刷新,基本上就是发布时间,重置缓存等等,这一步结束之后,就可以拿到创建完成的Bean进行业务操作了

【闲聊杂谈】源码追踪Spring工作流程-Debug全程跟踪

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/111917.html

(0)
Java光头强的头像Java光头强

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!