前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >对spring web启动时IOC源码研究(二)

对spring web启动时IOC源码研究(二)

作者头像
老梁
发布2019-09-10 16:39:18
3210
发布2019-09-10 16:39:18
举报

发现这样debug到哪说到哪好像有点回不来了~让我重新理下思路,主要步骤先上图,有什么不同意见欢迎批评教育~

(一)spring IOC的主要步骤都在refresh()这个方法中,我给出了自己的理解注释

代码语言:javascript
复制
@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 解析前准备工作,主要为打log,设置active标志
            prepareRefresh();

            // 对xml文件进行解析工作,并把解析后的BeanDefintiton定义信息保存入容器
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // 在上下文中用容器之前的准备工作,主要为对DefaultListableBeanFactory beanFactory的参数设置

(二)接下来讲解几个我认为比较重要的部分,注意整篇随笔是根据方法的调用顺序一个个讲下去的

代码语言:javascript
复制
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

1、obtainFreshBeanFactory()方法,注释里有说明他的作用,具体深入下,主要操作在refreshBeanFactory()中,对beanFactory处理完成后getBeanFactory()去获得处理完成的beanFactory返回。

代码语言:javascript
复制
    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、进入refreshBeanFactory()方法,先是检查是否已经存在容器,第一次进都是没有的,之后进入红色字体方法createBeanFactory(),创建默认的beanFactory容器,设id和定制化操作后开始进入加载bean描述信息的loadBeanDefinitions()方法。

代码语言:javascript
复制
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 为beanFactory创建XmlBeanDefinitionReader即xml阅读器
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 初始化阅读器的一些配置,比如加载环境信息,加载容器本身等等,由下面提供的运行调用截图可以看出,虽然在不同的子类调用方法,其本质都是XmlWebApplicationContext容器在运行
        beanDefinitionReader.setEnvironment(getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 允许子类提供定制的阅读器进行初始化,之后处理加载bean描述信息
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    } 

3、

代码语言:javascript
复制
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            for (String configLocation : configLocations) {
                reader.loadBeanDefinitions(configLocation);
            }
        }
    }

接上面的方法,loadBeanDefinitions()方法开始加载xml信息了,此方法后也算结束了一阶段xmlWebApplicationContext容器的使用,进入阅读器解析xml阶段

此图可以看出从XmlWebApplicationContext到XmlBeanDefinitionReader的转换。

4、

代码语言:javascript
复制
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            for (String configLocation : configLocations) {
                reader.loadBeanDefinitions(configLocation);
            }
        }
    }
    @Override
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(location, null);
    }
    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
       //省略。。
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                int loadCount = loadBeanDefinitions(resources);
     //省略。。
    }
代码语言:javascript
复制
    @Override
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int counter = 0;
        for (Resource resource : resources) {
            counter += loadBeanDefinitions(resource);
        }
        return counter;
    }
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        //省略
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
      //省略
    }

我尽量把它进入的一个个步骤都列出来,以免看客觉得怎么突然到了这个方法,但还是会省略一些细节,比如进入上述最后一个方法后,会对encodedResource进行保存操作等等,这里只列出特别主要的部分。上述操作进行了对资源的编解码,这么多方法的层层进入都还是准备工作,主要工作的方法为doLoadBeanDefinitions().

5、

代码语言:javascript
复制
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
       //加载资源信息成Document对象
            Document doc = doLoadDocument(inputSource, resource);
            return registerBeanDefinitions(doc, resource);
    }
  //开始
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
     //创建beanDefinition文档阅读器
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        documentReader.setEnvironment(getEnvironment());
     //获取DefalutListableBeanFactory中已经被加载的beanDefinition资源的多少
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    //这个方法是在DefalutListablebeanFactory中进行的,可以看出bean描述信息被保存在了这个类中
    @Override
    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }
  //这个方法开始对节点解析了,之前创建的文档阅读器开始发挥作用,
  //开始从XmlBeanDefinitionReader类处理转向DefaultBeanDefinitionDocumentReader类处理(BeanDefinitionDocumentReader的实现类)看下图

6、

代码语言:javascript
复制
   protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
     //创建beanDefinition解析代理
        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)) {
                    return;
                }
            }
        }
     //根节点前置处理,预留子类实现
        preProcessXml(root);
     //使用代理,解析bean定义
        parseBeanDefinitions(root, this.delegate);
     //根节点后置处理,预留子类实现
        postProcessXml(root);

        this.delegate = parent;
    }

    protected BeanDefinitionParserDelegate createDelegate(
            XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {

        BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
     //初始化代理,查询根节点的各项默认属性并赋给代理文档默认定义的类DocumentDefaultsDefinition,这些属性包括lazy-init,autowire,init-method,destory-method等等

7、

代码语言:javascript
复制
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
       //这里面开始对root根节点下的元素节点遍历
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }
代码语言:javascript
复制
  //这里主要是对context:component-scan,mvc:annotation-driven等特别功能进行解析,同时对注解的类进行相应功能的操作,解析出对应的beanDefinfition

8、对另一个解析方法parseDefaultElement()再进行探讨,它主要用来解析我们自定义的<bean>、<import>、<alias>、<beans>标签

代码语言:javascript
复制
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
     //这块代码很清晰的表示了对各明确元素的处理
     //对improt元素处理

        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
     //对alias元素处理
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
     //对bean元素处理
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
     //对beans元素处理
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

9、就以bean处理为例子吧,对bean的处理也是最复杂的

代码语言:javascript
复制
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
     //解析bean,取得bean信息持有者
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
       //包装bean,额外操作
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 注册被包装好的最终的beanDefintion
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // 发送注册事件
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

10、由代理对bean元素解析

代码语言:javascript
复制
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
    }
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
     //根据英文名也很好理解,获取id和name
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

     //省略

        String beanName = id;
        //省略
     //对bean元素各方面进行解析,里面有很多细节处理,它的描述信息主要在这里生成
     //在这里我们获得的描述信息有:scope是否单例,isAbstract,lazy-init,autowire,dependCheck,depend-on,autowire-candidate
     //primary,init-method,destory-method,factory-method,factory-bean,className,parent,description,look-up,replace-method,
     //构造方法的name,type,index,属性的name,value等等,还设置extractSource,额外的资源供使用者扩展使用
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            //省略(若没有beanName则系统生成,有别名获取别名)
       //创建beanDefinitionHolder这个bean信息持有者类,将beanDefinition等信息传进去,并返回
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

11、正式注册处理完的beanDefinition信息,调用registerBeanDefinition()方法

代码语言:javascript
复制
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // 用beanName作为key值,注册beanDefinition信息
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // 如果有别名,注册别名和beanName绑定
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }
代码语言:javascript
复制
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
     //省略(可靠性判断)
     //这里主要就是把beanDefinition放入Map集合中由容器保存,这里省掉了对是否有旧的同名beanName的判断
        this.beanDefinitionMap.put(beanName, beanDefinition);

        //省略
    }

到此为止就把bean信息全部解析注册完成了,至于最后一步fireComponentRegistered()方法,是对BeanDefinition再做了一层包装,然后通知监听器,spring没有再进行处理,此处是用来给用户扩展用的。

(pass:对bean的解说只是加载了一个bean的全部信息,执行完fireComponentRegistered()方法后,又进入了元素节点的循环解析,直到全部解析完成,XmlWebApplicationContext将保存存储有beanDefiniton信息的DefaultListableBeanFactory beanFactory,至此obtainFreshBeanFactory()方法才算运行完毕)

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-03-24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档