2021-08-25 SpringBean实例化源码解析

首先Spring通过读取XML由多方面解析.
1.Application上下文、
2.解析XML、
3.Application上下文消息总线传播进行通知,进行事件触发,从开启容器到关闭容器通过Spring发布消息总线通知上下文该怎样进行一个操作。
4、Spring的刷新机制
5、Spring实例化Bean进行装配

下面我们通过跟踪源码来进行解析

package org.springframework.bean;

/**
 * @author pm
 * @apiNode MyBean
 * @date 2021/8/2217:48
  * 稍后要进行实例化的Bean 自定义类
 */
public class MyBean {
}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="MyBean" class="org.springframework.bean.MyBean"></bean>
<!--    <bean name="HelloWorld" class="org.springframework.publish.HelloWorld">-->
<!--        <property name="msg" value="hhhhh"></property>-->
<!--    </bean>-->

<!--    <bean name= "eventReceiver"   class = "org.springframework.publish.EventReceiver" />-->
</beans>

启动类

package org.springframework.main;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.publish.HelloWorld;

/**
 * @author pm
 * @apiNode App
 * @date 2021/8/2217:49
 */
public class App {
      //这里我们是通过ClassPathXmlApplicationContext这个类来解析XML配置文件来装配Bean进行实例化
    //首先会进入ClassPathXmlApplicationContext的默认构造函数
    public static void main(String[] args) {
        BeanFactory bean = new ClassPathXmlApplicationContext("bean.xml");
        System.out.println(bean.getBean("MyBean"));
//      HelloWorld helloWorld = (HelloWorld) bean.getBean("HelloWorld");
//      helloWorld.send();
    }
}

下面我们进行跟踪 来到了ClassPathXmlApplicationContext

首先我们进入了第一个断点,通过传入的bean.xml进行读取,我们看到该方法有几个关键参数
1、configLocation 传入的是bean.xml 显而易见后续会通过该方法进行解析
2、refresh 是否对上下文进行刷新,后续会讲到,这里能理解到,当我们要进行Bean实例化之前会进行刷新一次
3、parent 这个参数表示,是否有父级容器,在Spring加载时通过配置文件解析,再通过Application上下文通知会有两个容器产生,一个父容器,一个子容器,当我们只进行单纯的对象装配时,他不会使用到父容器,而是使用子容器。
这里通过this调用了本身的构造函数


image.png
image.png

在ClassPathXmlApplicationContext 类有参构造函数中我们发现他通过构造函数这里调用了两个方法
1、setConfigLocations 传入的是配置文件信息
2、刷新上下文机制

下面我们点进去看看这个setConfigLocations 其中会对路径 占位符 等进行解析然后返回一个xml


image.png

然后我们进入刷新上下文方法看做了什么,这里start方法进行上下文初始化,
他返回的是一个StartupStep类,我们打开该类里面描述了一个上下文的生命周期,通过调用start方法FlightRecorderStartupStep类实现了该方法,返回了上下文启动的操作记录


image.png

然后我们往下走,进入了prepareRefresh()方法


image.png

在该方法中进行了设置上下文相关属性以及解析XML,发布Spring消息源的预处理

/**
     * Prepare this context for refreshing, setting its startup date and
     * active flag as well as performing any initialization of property sources.
     */
    protected void prepareRefresh() {
        // Switch to active.
        //上下文启动时间
        this.startupDate = System.currentTimeMillis();
        //是否关闭上下文
        this.closed.set(false);
        //上下文是否处于活动状态
        this.active.set(true);

        if (logger.isDebugEnabled()) {
            if (logger.isTraceEnabled()) {
                logger.trace("Refreshing " + this);
            }
            else {
                logger.debug("Refreshing " + getDisplayName());
            }
        }
        //资源初始化
        // Initialize any placeholder property sources in the context environment.
        initPropertySources();

        //验证XML
        // Validate that all properties marked as required are resolvable:
        // see ConfigurablePropertyResolver#setRequiredProperties
        getEnvironment().validateRequiredProperties();

        //存储预刷新应用程序侦听器。。。
        // Store pre-refresh ApplicationListeners...
        if (this.earlyApplicationListeners == null) {
            this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
        }
        else {
            //将本地应用程序侦听器重置为预刷新状态。
            // Reset local application listeners to pre-refresh state.
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }
        //允许收集早期ApplicationEvents,
        // Allow for the collection of early ApplicationEvents,
        //将在Multicast可用后发布。。。
        //预发布上下文事件
        // to be published once the multicaster is available...
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }

往下走


image.png

这里通过该方法会引用到AbstractXmlApplicationContext类的loadBeanDefinitions


image.png

可以看到已经生成了一个序列化ID,然后生成新的上下文环境,返回一个新的消息对象总之,在该方法中加载资源并且装配Bean,然后通过计数器进行计数


image.png

装配Bean,并且刷新,刷新后会经历清除缓存的一系列操作后生成Bean,返回消息对象

这里调用了prepareBeanFactory 方法传入的是刚刚的读取的Bean实例


image.png
/**
     * Configure the factory's standard context characteristics,
     * such as the context's ClassLoader and post-processors.
     * @param beanFactory the BeanFactory to configure
     */
    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Tell the internal bean factory to use the context's class loader etc.
        //设置Bean的类加载器
        beanFactory.setBeanClassLoader(getClassLoader());
        if (!shouldIgnoreSpel) {
            //解析Bean类名并进行返回 进行表达式解析
            beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        }
        //解析Bean上下文并解析注册
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

        // Configure the bean factory with context callbacks.
        //上下文解析器
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        //环境配置
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        //嵌入值解析
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        //加载资源
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        //用于发布消息总线设置上下文
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        //消息资源
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        //设置Application上下文
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
        //启动上下文对象
        beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);

        // BeanFactory interface not registered as resolvable type in a plain factory.
        // MessageSource registered (and found for autowiring) as a bean.
        //自动装配
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        //资源加载器
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        //发布消息总线
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        //上下文对象
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);
        //加载上下文的Bean监听器
        // Register early post-processor for detecting inner beans as ApplicationListeners.
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        // Detect a LoadTimeWeaver and prepare for weaving, if found.
        if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            //为类型匹配设置临时类加载器。
            // Set a temporary ClassLoader for type matching.
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

        //注册默认环境bean。
        // Register default environment beans.
        if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }
        if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
            beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,468评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,620评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,427评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,160评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,197评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,334评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,775评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,444评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,628评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,459评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,508评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,210评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,767评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,850评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,076评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,627评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,196评论 2 341

推荐阅读更多精彩内容