美文网首页征服Spring程序员spring专题
[原创]Spring源码教程02--Spring的IoC容器分析

[原创]Spring源码教程02--Spring的IoC容器分析

作者: weir_will | 来源:发表于2018-01-27 10:46 被阅读37次

    上一节“[原创]Spring教程01--Spring开始篇_Helloworld”中简单的介绍SpringFramwork的简单使用;通过Helloworld的程序做演示,本节将继续解读Spring的Ioc容器实现和分析。

    Spring IoC容器启动过程

    Spring的IoC容器启动大致分为下面的三个步骤:Resource定位Resoure的载入Resoured的注册 三个步骤;下面使用xml文件配置方式显示的配置方式步骤:

    启动过程

    首先看下ApplicationContext的构造方法定义:

    public FileSystemXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {
        
        //设置Context
        super(parent);
        //读取XML等配置文件定位
        setConfigLocations(configLocations);
        //创建BeanFactory的关键[重点]
        if (refresh) {
            refresh();
        }
    }
    
    
    @Override
    //这个refresh()启动容器[重要的放在首位]
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();
    
            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);
    
            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);
    
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);
    
                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);
    
                // 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();
            }
        }
    }
    

    Resource定位

    对象名 类 型 作 用 归属类
    configResources Resource[] 配置文件资源对象数组 ClassPathXmlApplicationContext
    configLocations String[] 配置文件字符串数组,存储配置文件路径 AbstractRefreshableConfigApplicationContext
    beanFactory DefaultListableBeanFactory 上下文使用的Bean工厂 AbstractRefreshableApplicationContext
    beanFactoryMonitor Object Bean工厂使用的同步监视器 AbstractRefreshableApplicationContext
    id String 上下文使用的唯一Id,标识此ApplicationContext AbstractApplicationContext
    parent ApplicationContext 父级ApplicationContext AbstractApplicationContext
    beanFactoryPostProcessors List<BeanFactoryPostProcessor> 存储BeanFactoryPostProcessor接口,Spring提供的一个扩展点 AbstractApplicationContext
    startupShutdownMonitor Object refresh方法和destory方法公用的一个监视器,避免两个方法同时执行 AbstractApplicationContext
    shutdownHook Thread Spring提供的一个钩子,JVM停止执行时会运行Thread里面的方法 AbstractApplicationContext
    resourcePatternResolver ResourcePatternResolver 上下文使用的资源格式解析器 AbstractApplicationContext
    lifecycleProcessor LifecycleProcessor 用于管理Bean生命周期的生命周期处理器接口 AbstractApplicationContext
    messageSource MessageSource 用于实现国际化的一个接口 AbstractApplicationContext
    applicationEventMulticaster ApplicationEventMulticaster Spring提供的事件管理机制中的事件多播器接口 AbstractApplicationContext
    applicationListeners Set<ApplicationListener> Spring提供的事件管理机制中的应用监听器 AbstractApplicationContext

    为了便于理解;提前解析了Context的属性如下:

    对象名 类 型 作 用 归属类
    configResources Resource[] 配置文件资源对象数组 ClassPathXmlApplicationContext
    configLocations String[] 配置文件字符串数组,存储配置文件路径 AbstractRefreshableConfigApplicationContext
    beanFactory DefaultListableBeanFactory 上下文使用的Bean工厂 AbstractRefreshableApplicationContext
    beanFactoryMonitor Object Bean工厂使用的同步监视器 AbstractRefreshableApplicationContext
    id String 上下文使用的唯一Id,标识此ApplicationContext AbstractApplicationContext
    parent ApplicationContext 父级ApplicationContext AbstractApplicationContext
    beanFactoryPostProcessors List<BeanFactoryPostProcessor> 存储BeanFactoryPostProcessor接口,Spring提供的一个扩展点 AbstractApplicationContext
    startupShutdownMonitor Object refresh方法和destory方法公用的一个监视器,避免两个方法同时执行 AbstractApplicationContext
    shutdownHook Thread Spring提供的一个钩子,JVM停止执行时会运行Thread里面的方法 AbstractApplicationContext
    resourcePatternResolver ResourcePatternResolver 上下文使用的资源格式解析器 AbstractApplicationContext
    lifecycleProcessor LifecycleProcessor 用于管理Bean生命周期的生命周期处理器接口 AbstractApplicationContext
    messageSource MessageSource 用于实现国际化的一个接口 AbstractApplicationContext
    applicationEventMulticaster ApplicationEventMulticaster Spring提供的事件管理机制中的事件多播器接口 AbstractApplicationContext
    applicationListeners Set<ApplicationListener> Spring提供的事件管理机制中的应用监听器 AbstractApplicationContext

    1)设置存储配置文件路径

    AbstractRefreshableConfigApplicationContext 的 setConfigLocations() 方法确定其文件的位置;代码比较简单。

    public void setConfigLocations(@Nullable String... locations) {
        if (locations != null) {
            Assert.noNullElements(locations, "Config locations must not be null");
            this.configLocations = new String[locations.length];
            for (int i = 0; i < locations.length; i++) {
                this.configLocations[i] = resolvePath(locations[i]).trim();
            }
        }
        else {
            this.configLocations = null;
        }
    }
    

    在上面的过程;我们获取配置文件的路径内容。下面看看具体的IoC容器的创建的具体过程了。

    2)加载配置文件的路径

    方法堆栈信息

    (1)创建BeanFactory

    @Override
    protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }
    

    (2)读取文件配置
    这个中间调用过程省略了;直接看结果:这里直接看log日志结果我们不用说太多了...

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }
    
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //下面开始解析XML方法
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }
    

    Resource的载入

    下面我们接着根据路径配置,读取到内存中;采用SAX文件的解析;看源码:

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
        ...
    try {
        Document doc = doLoadDocument(inputSource, resource);
        //解析xml文件注册JavaBean
        return registerBeanDefinitions(doc, resource);
    }
        ...
        //省略catch()代码...
    

    Resouce文件的解析

    方法堆栈
    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);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
    
        preProcessXml(root);
        //重点代码:注册JavaBean
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);
    
        this.delegate = parent;
    }
    

    其实写到这里感觉大家有兴趣的可以继续看看这个;如何读取xml文件如理 <bean> 等标签的操作...这里我推荐一片文章吧: Spring解密 - XML解析 与 Bean注册;后面写的比较具体。截止到目前通过XML解析;将配置文件读取到BeanDefine中;下面看看如何将其注册到IoC容器。

    注册IoC容器

        @Override
        public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
                throws BeanDefinitionStoreException {
    
            Assert.hasText(beanName, "Bean name must not be empty");
            Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    
            if (beanDefinition instanceof AbstractBeanDefinition) {
                try {
                    ((AbstractBeanDefinition) beanDefinition).validate();
                }
                catch (BeanDefinitionValidationException ex) {
                    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                            "Validation of bean definition failed", ex);
                }
            }
    
            BeanDefinition oldBeanDefinition;
    
            oldBeanDefinition = this.beanDefinitionMap.get(beanName);
            if (oldBeanDefinition != null) {
                if (!isAllowBeanDefinitionOverriding()) {
                    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                            "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                            "': There is already [" + oldBeanDefinition + "] bound.");
                }
                else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                    // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                    if (this.logger.isWarnEnabled()) {
                        this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                                "' with a framework-generated bean definition: replacing [" +
                                oldBeanDefinition + "] with [" + beanDefinition + "]");
                    }
                }
                else if (!beanDefinition.equals(oldBeanDefinition)) {
                    if (this.logger.isInfoEnabled()) {
                        this.logger.info("Overriding bean definition for bean '" + beanName +
                                "' with a different definition: replacing [" + oldBeanDefinition +
                                "] with [" + beanDefinition + "]");
                    }
                }
                else {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Overriding bean definition for bean '" + beanName +
                                "' with an equivalent definition: replacing [" + oldBeanDefinition +
                                "] with [" + beanDefinition + "]");
                    }
                }
                //将获取BeanName和之前解析BeanDefine放入Map集合中从而完成注册
                this.beanDefinitionMap.put(beanName, beanDefinition);
            }
            else {
                if (hasBeanCreationStarted()) {
                    // Cannot modify startup-time collection elements anymore (for stable iteration)
                    synchronized (this.beanDefinitionMap) {
                        this.beanDefinitionMap.put(beanName, beanDefinition);
                        List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                        updatedDefinitions.addAll(this.beanDefinitionNames);
                        updatedDefinitions.add(beanName);
                        this.beanDefinitionNames = updatedDefinitions;
                        if (this.manualSingletonNames.contains(beanName)) {
                            Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                            updatedSingletons.remove(beanName);
                            this.manualSingletonNames = updatedSingletons;
                        }
                    }
                }
                else {
                    // Still in startup registration phase
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    this.beanDefinitionNames.add(beanName);
                    this.manualSingletonNames.remove(beanName);
                }
                this.frozenBeanDefinitionNames = null;
            }
    
            if (oldBeanDefinition != null || containsSingleton(beanName)) {
                resetBeanDefinition(beanName);
            }
        }
    

    上面的源码中我们呢需要关注this.beanDefinitionMap.put(beanName, beanDefinition);这句代码;这个Bean注入BeanDefineMap中;到此这里已经完成了IoC容器的初始化。

    到这里基本已经结束了;ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");这样一句代码其实;也写了一章节;其实如果你是看着篇文章可以参考我的方法堆栈进行阅读。最后文章的部分其实源都简明的展示给你;后期我希望可以使用更多图展示给大家。


    参考资料:

    相关文章

      网友评论

        本文标题:[原创]Spring源码教程02--Spring的IoC容器分析

        本文链接:https://www.haomeiwen.com/subject/micnaxtx.html