Spring源码分析——spring源码核心方法refresh()介绍

1.前言

2.spring源码核心方法refresh()介绍

3.总结

1.前言

github源码地址(带注释):
https://github.com/su15967456...

在进入今天的spring核心方法介绍之前,我们先来复习一下上次我们学习的内容,Spring源码分析——spring容器总览,如图所示:

image.png

大致流程为:

1.IOC容器从xml,注解等地方获取类的信息。
2.将类的信息封装成BeanDefinition
3.beanFactoryPostProcessor前置处理
4.ioc容器获取类信息通过反射进行实例化
5.执行实例化
6.执行初始化逻辑(填充属性(populateBean)->执行aware接口方法->beanPostProcessor:before->init-method->beanPostProcessor:after)

以上,我们只是将spring容器的执行流程大体介绍了一下,接下来,我们要进入源码中,大致因为我们现在是第一回看源码,希望不要被卷入一细节,而是大致走一遍就可以了,卷入细节的旋涡很容易劝退。)了解一下spring启动的顺序代码。

spring源码中,最重要的核心方法,就是refresh()了,只要看懂了refresh()中的核心的13个方法,差不多就看懂spring了!

我们在开始看源码前,可以看着执行过程图猜测一下,第一步会执行什么?

一般我们都会觉得是读取配置文件
但是其实是————要把beanFactory创建好。因为只有把组件初始化完毕之后,我们才会有工具类去读配置文件,去实例化等操作。

2.spring源码核心方法refresh()介绍

2.1 debug的前置工作
在开始看spring源码之前,我们先准备一下前置工作。

我们先写两个普通的类A和B(为了体现出实例化和循环依赖的效果,循环依赖我们后期会讲),

类A:

public class A {

    private B b;

    public void init(){
        System.out.println("执行init()");
    }

    public void destroy(){
        System.out.println("执行destroy()");
    }

    public A(B b) {
        this.b = b;
    }

    public A() {
        System.out.println("A 创建了");
    }

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}

类B:

public class B {

    private A a;

    public B() {
        System.out.println("B 创建了");
    }

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }
}

再写一个配置文件:

applicationContext.xml

image.png




    

    
        
    

    
    
        
    


最后写一个启动demo类,就算大功告成了!

public static void main(String[] args) throws Exception {
        //启动容器
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        A a = (A) classPathXmlApplicationContext.getBean(A.class);
        B b = classPathXmlApplicationContext.getBean(B.class);

    }

我们可以在启动容器那一行上面打上断点,然后debug启动!

读spring源码,如何debug很重要,这里我放上一张截图,我们可以根据需要选择哪个功能。

Spring源码分析——spring源码核心方法refresh()介绍_第1张图片

2.2spring源码核心方法refresh()介绍

好的,那我们先进入这个方法,点击上面那张图的第二个箭头:

Spring源码分析——spring源码核心方法refresh()介绍_第2张图片

可以看出我们进入了一个方法,但是不是ClassPathXmlApplicationContext的构造方法。

Spring源码分析——spring源码核心方法refresh()介绍_第3张图片

这里我们可以不用理会,点击步出按钮,再点步入按钮。
image.png

就会进入到我们所希望看到的方法了:
image.png

但是现在还看不出什么端倪,所以我们可以继续步入:

Spring源码分析——spring源码核心方法refresh()介绍_第4张图片

好了,终于看到了我们的核心refresh()方法了。

    public ClassPathXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {
        //调用父类构造方法,进行相关的对象创建操作
        super(parent);
        //设置配置文件路径,放入一个对象中,方便后续的读取和使用
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

我们可以看出,除了refresh()方法,还有一些其它的方法,我们大致地介绍一下:

2.2.1 super()方法介绍

super()主要用来调用父类构造方法,进行一些属性的对象创建,后期使用。

往super()里面点,点到最里面,我们发现它调用了两个方法:

    /**
     * Create a new AbstractApplicationContext with the given parent context.
     * @param parent the parent context
     */
    public AbstractApplicationContext(@Nullable ApplicationContext parent) {
        this();
        setParent(parent);
    }

首先我们看this()方法:

    /**
     * Create a new AbstractApplicationContext with no parent.
     */
    public AbstractApplicationContext() {
        //加载资源模式解析器 用来加载我们系统运行的时候需要的资源(xml)
        this.resourcePatternResolver = getResourcePatternResolver();
    }

通过getResourcePatternResolver方法的注释我们可以看出,它创建了一个资源模式解析器,是后面用来加xml等载资源用的。上文提到过,我们容器的初始化前面的阶段主要就是要将容器,组件进行实例化。

2.2.2 setParent(parent)方法介绍

接下来看我们的setParent(parent)方法,往里面点我们可以看出,如果父类环境属性不等于空,那么久把父类容器的环境和子类容器合并。

    public void setParent(@Nullable ApplicationContext parent) {
        this.parent = parent;
        if (parent != null) {
            Environment parentEnvironment = parent.getEnvironment();
            if (parentEnvironment instanceof ConfigurableEnvironment) {
                getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
            }
        }
    }

我们可能搞不清楚父子容器的概念,因为目前spring只有一个容器,可以暂时先略过。

2.2.3 setConfigLocations(configLocations)方法介绍
这个方法比较简单,就是将传进来的 applicationContext.xml 这个字符串保存一下,方便后续读取xml,可以直接从数组中获取。

2.2.4 refresh()方法介绍

好了,接下来终于要到我们的refresh()方法了,我们直接进入这个方法。

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

            // Prepare this context for refreshing.
            //容器刷新前的准备工作
            /*
            1.设置容器的启动时间
            2.设置活跃状态为true
            3.设置关闭状态为false
            4.获取Environment对象,并加载当前系统的属性到Environment中
            5.准备监听器和事件的集合对象,默认为空的对象集合
             */
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            //创建容器对象,DefaultListableBeanFactory
            //加载xml配置文件的属性到当前工厂中,最重要的就是BeanDefinition
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            //准备bean工厂,完成bean工厂属性的一些初始化操作
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                //空方法
                //子类覆盖做额外的处理,此处我们一般不做任何扩展工作,这个方法具体可以做什么,可以进去看注释,springmvc/springboot里有具体的扩展实现
                postProcessBeanFactory(beanFactory);

                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                // Invoke factory processors registered as beans in the context.
                //执行beanFactoryPostProcessors,调用各种beanFactory处理器,比如替换工作 ${username}
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                //注册BeanPostProcessors
                registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();

                // Initialize message source for this context.
                //初始化消息资源 国际化设置
                initMessageSource();

                // Initialize event multicaster for this context.
                //初始化应用的多播器
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                //注册监听器到多播器里面
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                //实例化非懒加载的实例对象
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                //事件发布
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
                contextRefresh.end();
            }
        }
    }

可以看出,一共调用了13个方法,这13个方法就是我们要理解的关键,我已经相应地输出了一些注解,接下来我们挨个进行解释:

2.2.4.1 prepareRefresh()方法介绍

protected void prepareRefresh() {
        // Switch to active.
        //创建一下开启时间
        this.startupDate = System.currentTimeMillis();
        //关闭状态设置为false
        this.closed.set(false);
        //激活状态设置为true
        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();

        // 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);
        }

        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
        //准备监听器事件的集合
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }

我们可以看出prepareRefresh()方法主要还是做了一些准备工作,比如:
1.创建一下开启的时间
2.设置关闭状态
3.设置激活状态
4.打印日志
5.获取环境对象并验证,验证所需要的属性文件是否已经放入环境中
6.创建一些基本的监听器,准备监听器事件的集合(监听器在后面的发布->订阅模式中有用到,这里只是初始化一下类。)

我们可以看出,这个方法,主要还是做一些初始化上下文的工作,也就是刷新前的准备工作!

2.2.4.2 obtainFreshBeanFactory()方法介绍

            // Tell the subclass to refresh the internal bean factory.
            //创建容器对象,DefaultListableBeanFactory
            //加载xml配置文件的属性到当前工厂中,最重要的就是BeanDefinition
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

这个方法就比较重要了,大概做了两步操作:
1.创建一个容器对象(DefaultListableBeanFactory)
2.读取xml的bean信息封装成beanDefinition的操作。

Spring源码分析——spring源码核心方法refresh()介绍_第5张图片

我们可以点进去看一下:

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //初始化BeanFactory,并进行xml文件读取,并将得到的BeanFactory记录在当前实体的属性中
        refreshBeanFactory();
        return getBeanFactory();//返回当前实体的beanFactory属性
    }

我们先对容器进行初始化操作(因为容器到现在还没进行过初始化呢),然后再读取xml中的bean并封装成BeanDefinition。

debug 步入 refreshBeanFactory();

    @Override
    protected final void refreshBeanFactory() throws BeansException {
        //如果有bean工厂了,先销毁掉
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //创建DefaultListableBeanFactory对象
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            //每个容器都有自己的id,为了序列化指定id,可以从id反序列化到beanFactory对象
            beanFactory.setSerializationId(getId());
            //定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖,可以通过子类重写
            customizeBeanFactory(beanFactory);
            //初始化documentReader,并进行对xml文件进行解析
            loadBeanDefinitions(beanFactory);
            this.beanFactory = beanFactory;
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

1.如果有了容器的话,先销毁容器。
2.创建一个DefaultListableBeanFactory对象
3.接着进行相关属性设置
4.最重要的就是对xml文件进行解析的loadBeanDefinitions方法,把xml文件进行解析读取bean并封装。(解析过程比较复杂,所以这次先不点进去看了)

2.2.4.3 prepareBeanFactory()方法介绍

接下来是对bean工厂的属性进行一些初始化操作:

            // Prepare the bean factory for use in this context.
            //准备bean工厂,完成bean工厂属性的一些初始化操作
            prepareBeanFactory(beanFactory);

我们可以点击进去看看:

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Tell the internal bean factory to use the context's class loader etc.
        //设置beanFactory的类加载器为当前context的类加载器
        beanFactory.setBeanClassLoader(getClassLoader());
        if (!shouldIgnoreSpel) {
            //设置beanfactory的表达式语言处理器  处理类->解析类->配置类
            beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        }
        //为beanFactory增加一个默认的PropertyEditor,这个主要是对bean的属性设置管理的一个工具类 属性编辑类,比如date的format
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

        // Configure the bean factory with context callbacks.
        //添加beanPostProcessor 此类用来完成某些aware对象的注入
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        //设置忽略自动装配的接口,这些接口的实现是容器是需要 beanPostProcessor 注入的
        //所以在使用@Autowired进行注入的时候需要对这些接口进行忽略
        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);

        // BeanFactory interface not registered as resolvable type in a plain factory.
        // MessageSource registered (and found for autowiring) as a bean.
        //设置几个自动装配的特殊规则,当在进行IOC的时候,如果有多个实现,就按指定的对象注入
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

        // Register early post-processor for detecting inner beans as ApplicationListeners.
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        // Detect a LoadTimeWeaver and prepare for weaving, if found.
        //增加aspectJ的支持,在java中织入有三种方式,分别为编译器织入,类加载器织入,运行时织入
        //编译器织入指通过特殊的编译器,将切面织入到java中
        //类加载器织入指通过特殊的类加载器,在类字节码加载入JVM的时候,织入切面,类似cglib
        //aspectj采用了两种织入方式,特殊编译器和类加载器,类加载器就是下面的load_time_wave
        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()));
        }

        // Register default environment beans.
        // 注册默认的系统环境bean到一级缓存中
        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());
        }
    }

这个看不懂没关系,就是对beanFactory的一些属性值进行了一些操作。我们后续会进行讲解。

2.2.4.4 postProcessBeanFactory()方法介绍

                //空方法
                //子类覆盖做额外的处理,此处我们一般不做任何扩展工作,这个方法具体可以做什么,可以进去看注释,springmvc/springboot里有具体的扩展实现
                postProcessBeanFactory(beanFactory);

2.2.4.5 invokeBeanFactoryPostProcessors()方法介绍

                // Invoke factory processors registered as beans in the context.
                //执行beanFactoryPostProcessors,调用各种beanFactory处理器,比如替换工作 ${username}
                invokeBeanFactoryPostProcessors(beanFactory);

这个方法主要用来执行BeanFactoryPostProcessor对BeanDefinition进行处理。
我们以后调用的时候再来讲解。

2.2.4.6 registerBeanPostProcessors()方法介绍

                //注册BeanPostProcessors
                registerBeanPostProcessors(beanFactory);

这个步骤初始化了一下所有的beanPostProcessor,方便后续初始化的时候进行调用。

2.2.4.7 beanPostProcess.end()方法介绍

空方法,模板方法,在容器刷新的时候可以自定义逻辑,不同的Spring容器做不同的事情。

2.2.4.8 initMessageSource()方法介绍

                // Initialize message source for this context.
                //初始化消息资源 国际化设置
                initMessageSource();

为上下文初始化 Message 源,即对不同语言的消息体进行国际化处理。

2.2.4.9 initApplicationEventMulticaster()方法介绍

                // Initialize event multicaster for this context.
                //初始化应用的多播器
                initApplicationEventMulticaster();

初始化事件广播器,并放入 applicationEventMulticaster bean 中,用于后面的发布,订阅模式事件广播。

2.2.4.10 onRefresh()方法介绍

                // Initialize other special beans in specific context subclasses.
                //模板方法,在容器刷新的时候可以自定义逻辑,不同的Spring容器做不同的事情。
                onRefresh();

2.2.4.11 registerListeners()方法介绍

                // Check for listener beans and register them.
                //注册监听器到多播器里面
                registerListeners();

2.2.4.12 finishBeanFactoryInitialization()方法介绍

                // Instantiate all remaining (non-lazy-init) singletons.
                //实例化非懒加载的实例对象
                finishBeanFactoryInitialization(beanFactory);

这个方法就是refresh()的核心方法,创建bean,解决循环依赖,AOP都在这里面。也是以后要重点讲解的方法。

2.2.4.13 finishRefresh()方法介绍

                // Last step: publish corresponding event.
                //事件发布
                finishRefresh();

前面的事件完成了,启动发布订阅模式,通知另外一些类完成一些事情。

3.总结

今天我们介绍了refresh()的核心十三个方法,非常重要,我们可以debug跟一遍,然后打上注释。
执行的流程如下:
1.容器刷新前的准备工作
2.创建bean工厂
3.加载xml配置文件到bean工厂中
4.对bean工厂进行赋值
5.执行beanFactoryPostProcessors
6.注册BeanPostProcessors
7.初始化消息资源 国际化设置
8.初始化应用的多播器
9.注册监听器到多播器里面
10.实例化非懒加载的实例对象
11.事件发布

你可能感兴趣的:(Spring源码分析——spring源码核心方法refresh()介绍)