博客 Spring从成神到升仙系列 二】2023年再不会 IOC 源码,就要被淘汰了

Spring从成神到升仙系列 二】2023年再不会 IOC 源码,就要被淘汰了

   数栈君   发表于 2023-06-20 15:27  161  0

IOC 源码剖析
那 Spring 是如何管理的呢?源码背后又有什么小技巧呢?今天我们一起来看一下 IOC 源码的解析

为了阅读性,我们将以 xml 文件的配置来阅读源码

首先,我们从 ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); 这一行入手,看其到底执行了什么

我们 debug 点进去可以看到共分为了三部分:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent){
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}

简单来说,这三部分的作为分别是:

super(parent):调用父类的构造方法,创建 PathMathingResourcePatternResolver 解析配置文件
setConfigLocations(configLocations):设置 configLocations(配置文件路径) 到当前应用程序中
refresh():解析配置文件、完成bean的注册、实例化、初始化、代理等一系列的工作最终创建出一个 Bean 实例
我们一起来看一下 refresh 到底做了什么

1、prepareRefresh
**整体简介:**做容器刷新前的准备工作

设置容器的启动时间:this.startupDate = System.currentTimeMillis();
设置活跃状态为 true:active.set(true)
设置关闭状态为 false:closed.set(false)
获取 Environment 对象,并加载当前系统的属性值到 Environment 对象中
准备监听器和事件的集合对象,默认为空的集合:earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
这个方法不重要,也不必要去深入了解,知道做了容器刷新的准备工作即可。

2、obtainFreshBeanFactory
**整体简介:**创建容器,并且完成配置文件的加载

重要的来了,这个方法是比较重要的,一定要记住

首先该方法分为了以下几部分:

refreshBeanFactory:解析我们的 application.xml 文件并生成 BeanDefinition 注册至 DefaultListableBeanFactory 的 beanDefinitionMap 中
getBeanFactory:获取工厂bean
2.1 refreshBeanFactory
protected final void refreshBeanFactory() throws BeansException {
// 创建 BeanFactory,类型为 DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}

上述我们必须记住这个工厂类:DefaultListableBeanFactory,甚至要做到背诵+默写的程度

其次,最重要的就属 loadBeanDefinitions(beanFactory) 方法了

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory){
// 创建XmlBeanDefinitionReader,这个是我们xml文件的解析器
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);

// 加载BeanDefinitions
loadBeanDefinitions(beanDefinitionReader);
}

我们继续深入看其做了什么

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
// 拿到xml文件的地址
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();

if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}

继续往下看,一直到 loadBeanDefinitions

public int loadBeanDefinitions(String location, Set<Resource> actualResources){
ResourceLoader resourceLoader = getResourceLoader();

if (resourceLoader instanceof ResourcePatternResolver) {
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 直接看这行,其余不重要
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
return loadCount;
}
} else {
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
return loadCount;
}
}

XmlBeanDefinitionReader 第 388 行

// 将路径封装成一个DOC格式
Document doc = doLoadDocument(inputSource, resource);
// 继续注册
return registerBeanDefinitions(doc, resource);

XmlBeanDefinitionReader 第 505 行

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 其余不重要,直接看这行
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
// 获取根节点
Element root = doc.getDocumentElement();
// 从根节点开始解析遍历
doRegisterBeanDefinitions(root);
}

protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);

if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
}
}

preProcessXml(root);
// 直接看这里,其余不重要
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);

this.delegate = parent;
}

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 直接看这里
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

// 根据不同的配置走不同的分支,配置:import、alias、bean、beans
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}

我们这里只看 bean 的配置,其余的读者有兴趣可以自己去 debug 下

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 解析各种xml标签去生成对应的BeanDefinition,读者有兴趣可以自己看一下
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 重点:正式开始注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
{

// 重点,开始注册
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// 注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
// 看一下原来的beanDefinitionMap是不是已经有该 beanName 了
// 如果已经存在,我们要排抛出异常(Spring不允许覆盖)
BeanDefinition oldBeanDefinition = this.beanDefinitionMap
beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException()
}
}
if (oldBeanDefinition != null) {
this.beanDefinitionMap.put(beanName, beanDefinition);
}else {
// 重点在这:将我们的beanName与beanDefinition放至beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);

}
}

到这里,基本就结束了,我们来回顾一下 refreshBeanFactory 的业务:

通过我们传递的xml 文件的路径,利用 documentLoader 将其封装成 Document 格式
创建 BeanDefinitionDocumentReader 来正式解析 xml 文件并找到文件的 root
根据 root 扫描遍历,对不同配置的标签(import、alias、bean、beans)走不同的逻辑判断
将当前的标签各属性进行组装成 beanDefinition,调用 DefaultListableBeanFactory 进行注册
根据 BeanName 查询该 beanDefinition 是否被注册过,如果被注册过,则直接抛出异常(Spring不允许覆盖)
如果没有注册过,则将 BeanName 与 beanDefinition 注册至 DefaultListableBeanFactory 的 beanDefinitionMap 中
最后,如果该 beanDefinition 含有别名,也要将别名进行注册,至于为什么注册别名,可见:附录1
这里可能有人会说,小黄小黄,按你之前解析 kafka 的套路,肯定会分析 beanDefinition 的形成的,现在怎么偷懒不分析了,是不是看不懂~

答:之前分享的 kafka 系列的文章,大家都知道分享的很细,但是我们细细品味一下,我们读源码到底为了什么,以及如何去读、如何有效的读、如何快速的读,我相信每一个人心中都有一套读源码的方式。至于哪一种阅读方式更为合理,后面博主准备单独出一篇文章来讲解,或者你可以私信我,告知我你的读源码的方式,一起加油、一起学习。

3、prepareBeanFactory
整体简介: beanFactory 的准备工作,对各种属性进行填充

这个方法不重要,也不必要去深入了解,知道做了 beanFactory 的填充即可

不过,这里记住,beanFactory 的类一定要记清楚,是 DefaultListableBeanFactory ,不多说直接 背诵+默写

4、postProcessBeanFactory
整体简介: 默认没有实现,留给子类进行实现操作

5、invokeBeanFactoryPostProcessors
整体简介: 可以自由扩展,通过实现BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 接口,对 beanFactory 里面的 BeanDefinition 进行修改

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// 找到当前 beanDefinitionMap 中`BeanFactoryPostProcessor` 和 `BeanDefinitionRegistryPostProcessor`接口的实现
// 若这些实现有对应的order(顺序),则排序之后依次调用
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}

温馨小提示:这里有的小伙伴可能对 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 接口不熟悉,我们将此处的讲解放至:附录2

6、registerBeanPostProcessors
整体简介: 完成 spring 自带或者用户自定义的 BeanPostProcessor 的解析

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// 实例化并且注册所有的beanPostProcessor
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
1
2
3
4
这里的操作实际上和我们上面 invokeBeanFactoryPostProcessors 里面很像,都是对实现一些特定接口的类做加载,但需要注意的是:对于实现 BeanPostProcessor 接口的来说,我们不会在此立即调用,会在 Bean 初始化方法前后调用。

对了,提前剧透一下,我们响当当的 AOP 也是在这里实现的,后续我们也会讲的。

温馨小提示:这里有的小伙伴可能对 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 接口不熟悉,我们将此处的讲解放至:附录3

7、initMessageSource
整体简介: Spring 中国际化的功能

8、initApplicationEventMulticaster
整体简介: 初始化事件广播器

9、onRefresh
整体简介: 在 spring 中默认没有任何实现,模板方法,但是在 springboot 中启动了 web 容器

10、registerListeners
整体简介: 注册监听器,为了方便接受广播的事件

11、finishBeanFactoryInitialization
整体简介:完成所有非懒加载的单例对象的实例化操作,从此方法开始进行对象的创建,包含了实例化,初始化,循环依赖,AOP等核心逻辑的处理过程,此步骤是最最核心且关键的点,要对其中的细节最够清楚

由于篇幅原因,博主会尽量挑选一些重要的地方进行分析。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 实例化剩下的单例对象
beanFactory.preInstantiateSingletons();
}

public void preInstantiateSingletons(){
// 拿到我们之前存储的所有beanDefinition的名字
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); 
// 触发单例bean的初始化,遍历集合的对象
for (String beanName : beanNames) {
// 如果beanName对应的bean不是FactoryBean,只是普通的bean,通过beanName获取bean实例
getBean(beanName);
}
}

public Object getBean(String name) throws BeansException {
// 此方法是实际获取bean的方法,也是触发依赖注入的方法
return doGetBean(name, null, null, false);
}

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){
// 这里需要一步转换,这里的原因我们附录1提到过,这里不再过多讨论
String beanName = transformedBeanName(name);

// 提前检查单例缓存中是否有手动注册的单例对象,剧透一下(和循环依赖有关联)
Object sharedInstance = getSingleton(beanName);

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

if (mbd.isSingleton()) {
// 返回以beanName的(原始)单例对象,如果尚未注册,则使用singletonFactory创建并注册一个对象:
sharedInstance = getSingleton(beanName, () -> {
try {
// 为给定的合并后BeanDefinition(和参数)创建一个bean实例
// 这也是我们的核心方法
return createBean(beanName, mbd, args);
}
});
}

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
// 实际创建bean的调用
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
// 根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);

// 对bean的属性进行填充,将各个属性值注入,其中,可能存在依赖于其他bean的属性,则会递归初始化依赖的bean
populateBean(beanName, mbd, instanceWrapper);

// 执行初始化逻辑
exposedObject = initializeBean(beanName, exposedObject, mbd);
}

到这里我们先停一停,我们总结一下创建实例的一些步骤:

拿到我们之前注册的 beanDefinitionNames,遍历整个 beanDefinitionNames,每一个 BeanName 生成一个对象

我们需要进行名称转化,防止传入的是一个别名或其他的名称,利用转换后的别名去调用

查询我们的单例缓存中是否已经存在该实例,如果存在直接返回即可

如果不存在,则需要去根据该 beanDefinition 去生成对应的实例

对于生成实例共有三个步骤:

创建实例
属性填充
初始化逻辑
实现 BeanPostProcessor 的前置方法
对象的初始化方法
实现 BeanPostProcessor 的后置方法
我们对于每个步骤都进行分析:

11.1 创建实例
对于实例创建,Spring 中创建 bean 的方式大致可分为三种:

类名称 + 自定义 beanName
工厂类名称+ 自定义工厂静态方法 + 自定义 beanName
提前注册工厂bean,使用工厂bean + 工厂方法+自定义 beanName
可能大家有点懵,怎么这么多创建的方法,这里其实我们不需要太过于关注,只需要关注 类名称 + 自定义 beanName 这种方法即可,其余两种基本很少用到

对于 类名称 + 自定义 beanName 我们一般有两种构造方法:

无参构造(常用)
有参构造(不常用)
为了便于理解,我们这里只介绍无参构造

BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// 根据当前的beanName拿到其 Class
Class<?> beanClass = resolveBeanClass(mbd, beanName);

// 前面的有参都不存在,则进行无参构造
return instantiateBean(beanName, mbd);
}

protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
// 获取实例化策略并且进行实例化操作
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
}

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// 锁一下对象,线程安全
synchronized (bd.constructorArgumentLock) {
// 得到当前bean的Class
Class<?> clazz = bd.getBeanClass();
// 通过class得到其默认的构造方法
constructorToUse = clazz.getDeclaredConstructor();
// 
return BeanUtils.instantiateClass(constructorToUse);
}
}

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) {
// 构造方法+入参
// 如果当前是无参构造方法的话,则argsWithDefaultValues为空
return ctor.newInstance(argsWithDefaultValues);
}

总结一下通过无参构造创建实例的步骤:

加锁,保证线程安全
得到当前 bean 的 Class,通过其 Class 得到默认的无参构造方法
通过反射直接创建即可
其实有参的构造方法也类似,只不过相较于无参构造,反射传入的 argsWithDefaultValues 的参数,这里的参数可以为 Bean 也可以为数值,所以这里也会出现循环依赖的问题。

11.2 属性填充
属性填充相对简单,流程我们大致过一下,属性注入类似:

<bean id="messageService">
<property name="name" value="hls"/>
</bean>

其中的 property 标签就是我们的属性值。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// 得到当前 BeanDefinition 的属性值
PropertyValues pvs = mbd.getPropertyValues();
if (pvs != null) {
// 注入属性
applyPropertyValues(beanName, mbd, bw, pvs);
}
}

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs){
// 获取pvs的PropertyValue对象数组,并将其转换成列表
List<PropertyValue> original = Arrays.asList(pvs.getPropertyValues());

for (PropertyValue pv : original) {
// 获取属性的名字
String propertyName = pv.getName();
// 获取未经类型转换的值
Object originalValue = pv.getValue();

// 这里需要进行一系列的转换
// 因为我们的属性注入有可能注入的是一个BeanReference,需要重新去 BeanFactory 中获取实例
// 转换后的放至 deepCopy

// 按原样使用deepCopy构造一个新的MutablePropertyValues对象然后设置到bw中以对bw的属性值更新
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
}

public void setPropertyValues(PropertyValues pvs){
setPropertyValues(pvs, false, false);
}

public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid){
// 后续主要通过反射对值进行设置,感兴趣的可以自己去看下源码实现
setPropertyValue(pv);
}

11.3 初始化逻辑
我们这里先实现一个 BeanPostProcessor 接口,便于我们的观察:

public class MyTest implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("我前置增强");
return bean;
}


public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("我后置增强");
return bean;
}
}

直接看我们的源码:

// 执行初始化逻辑
exposedObject = initializeBean(beanName, exposedObject, mbd);

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// 将BeanPostProcessors应用到给定的现有Bean实例,调用它们的postProcessBeforeInitialization初始化方法。
// 返回的Bean实例可能是原始Bean包装器
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

//调用初始化方法,先调用bean的InitializingBean接口方法,后调用bean的自定义初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);


// 将BeanPostProcessors应用到给定的现有Bean实例,调用它们的postProcessAfterInitialization方法。
// 返回的Bean实例可能是原始Bean包装器
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

//返回包装后的Bean
return wrappedBean;
}

// 执行所有的BeanPostProcessors接口下的类
// 如果我们自己实现的类对 Bean 进行了包装,比如AOP,则使用我们实现类里面返回的
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName){
Object result = existingBean;
//遍历 该工厂创建的bean的BeanPostProcessors列表
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 默认实现按原样返回给定的 Bean
Object current = processor.postProcessBeforeInitialization(result, beanName);
// 如果 current为null
if (current == null) {
//直接返回result,中断其后续的BeanPostProcessor处理
return result;
}
//让result引用processor的返回结果,使其经过所有BeanPostProcess对象的后置处理的层层包装
result = current;
}
//返回经过所有BeanPostProcess对象的后置处理的层层包装后的result
return result;
}

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd){
// 如果mbd不为null&&bean不是NullBean类
if (mbd != null && bean.getClass() != NullBean.class) {
// 获取mbd指定的初始化方法名
String initMethodName = mbd.getInitMethodName();
// 在bean上调用指定的自定义init方法
invokeCustomInitMethod(beanName, bean, mbd);

// 具体调用,反射执行
// Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);
// methodToInvoke.invoke(bean);
}
}

// 执行所有的BeanPostProcessors接口下的类
// 如果我们自己实现的类对 Bean 进行了包装,比如AOP,则使用我们实现类里面返回的
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName){

//初始化结果对象为result,默认引用existingBean
Object result = existingBean;
//遍历该工厂创建的bean的BeanPostProcessors列表
for (BeanPostProcessor processor : getBeanPostProcessors()) {
//回调BeanPostProcessor#postProcessAfterInitialization来对现有的bean实例进行包装
Object current = processor.postProcessAfterInitialization(result, beanName);
// 如果current为null
if (current == null) {
//直接返回result,中断其后续的BeanPostProcessor处理
return result;
}
//让result引用processor的返回结果,使其经过所有BeanPostProcess对象的后置处理的层层包装
result = current;
}
//返回经过所有BeanPostProcess对象的后置处理的层层包装后的result
return result;
}
其实初始化的逻辑也很简单,就是调用我们的 BeanPostProcess 实现扩展点的应用

然后初始化 init 方法即可

12、finishRefresh
整体简介: 完成整个容器的启动,所有的对象都准备完成,可以进行后续业务流程的操作,清除上下文缓存,初始化生命周期处理器,发送刷新完成事件

五、流程图

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/b51e93549e4696655d8a069b2ac9b74d..jpg

  • 免责申明:

    本文系转载,版权归原作者所有,如若侵权请联系我们进行删除!

  • 《数据治理行业实践白皮书》下载地址:https://fs80.cn/4w2atu


  • 《数栈V6.0产品白皮书》下载地址:https://fs80.cn/cw0iw1

  • 想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:https://www.dtstack.com/?src=bbs

    同时,欢迎对大数据开源项目有兴趣的同学加入「袋鼠云开源框架钉钉技术群」,交流最新开源技术信息,群号码:30537511,项目地址:
    https://github.com/DTStack


0条评论
社区公告
  • 大数据领域最专业的产品&技术交流社区,专注于探讨与分享大数据领域有趣又火热的信息,专业又专注的数据人园地

最新活动更多
微信扫码获取数字化转型资料
钉钉扫码加入技术交流群