关于java:Spring源码分析spring源码核心方法refresh介绍

37次阅读

共计 13597 个字符,预计需要花费 34 分钟才能阅读完成。

1. 前言

2.spring 源码外围办法 refresh()介绍

3. 总结

1. 前言

github 源码地址(带正文):
https://github.com/su15967456…

在进入明天的 spring 外围办法介绍之前,咱们先来温习一下上次咱们学习的内容,Spring 源码剖析——spring 容器总览,如图所示:

大抵流程为:

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

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

    

    <bean id="a" class="slf.A" >
        <property name="b" ref="b"/>
    </bean>

    
    <bean id="b" class="slf.B">
        <property name="a" ref="a"/>
    </bean>


</beans>

最初写一个启动 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 很重要,这里我放上一张截图,咱们能够依据须要抉择哪个性能。

2.2spring 源码外围办法 refresh()介绍

好的,那咱们先进入这个办法,点击下面那张图的第二个箭头:

能够看出咱们进入了一个办法,然而不是 ClassPathXmlApplicationContext 的构造方法。

这里咱们能够不必理睬,点击步出按钮,再点步入按钮。

就会进入到咱们所心愿看到的办法了:

然而当初还看不出什么端倪,所以咱们能够持续步入:

好了,终于看到了咱们的外围 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 的操作。

咱们能够点进去看一下:

    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. 事件公布

正文完
 0