每天五分钟,学透Spring《二、Bean的创建过程》

475 阅读5分钟

图片

做一个单纯的程序汪,分享干货,聊聊人生。

微信公众号:后端架构进阶

关注我发现更多的干货,微服务、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。

浅析Spring三级缓存解决循环依赖

三级缓存其实就是三个Map,没那么高大上,主要是解决问题的思路和办法。缓存起来也是为了提高我们程序的效率。

四、小结

这篇我们就分析到这里,上面的十多步去初始创建的我们会一个个进行分析,一口吃不成胖子。先知道我们的Bean创建会经历上面的每个步骤。创建完成一个就缓存一个,如果设置了懒加载,那么会使用到的时候才去创建。

每天五分钟,学透Spring,让源码不再难。

历史文章汇总,包含Netty、源码、并发、JVM

总结

以上就是之前的内容的总结和分享,感谢各位大佬的 关注点赞收藏

微信公众号:后端架构进阶

更多文章正在赶来,喜欢记得给我点个 👍 ,感谢支持!

公众号文章同步更新!关注我,不迷路!