spring代码分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

最近用spring的时候感觉不怎么了解内部原理,用起来有些不习惯,于是找空看了一下spring的源码,看了一下bean加载的流程以及调用流程,现总结如下:

加载

contextLoaderListener->ContextLoader.initWebApplicationContext,调用本对象的createWebApplicationContext->ConfigurableWebApplicationContext.refresh()->AbstractApplicationContext.refresh();

在上面的refresh里面有

    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

追踪这段代码,发现调用了:AbstractRefreshableApplicationContext的以下方法:

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

这个方法里面有一句很重要: loadBeanDefinitions(beanFactory);

这一句的作用是从配置文件加载所有的BeanDefinition.

项目中用到的是基于xml的spring配置方式,所以最终的工厂类是:XmlWebApplicationContext。

加载beandefinition时,把逐个文件用XmlBeanDefinitionReader进行解析并加载bean。

XmlBeanDefinitionReader首先把每个配置文件载入成一个w3c的Document对象,然后 用 BeanDefinitionDocumentReader解析Document.

BeanDefinitionDocumentReader的实现类是DefaultBeanDefinitionDocumentReader。

实现类加载bean的方法如下:

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
        {
            if(delegate.isDefaultNamespace(delegate.getNamespaceURI(root)))
            {
                NodeList nl = root.getChildNodes();
                for(int i = 0; i < nl.getLength(); i++)
                {
                    org.w3c.dom.Node node = nl.item(i);
                    if(node instanceof Element)
                    {
                        Element ele = (Element)node;
                        String namespaceUri = delegate.getNamespaceURI(ele);
                        if(delegate.isDefaultNamespace(namespaceUri))
                            parseDefaultElement(ele, delegate);
                        else
                            delegate.parseCustomElement(ele);
                    }
                }

            } else
            {
                delegate.parseCustomElement(root);
            }
        }

方法中的Element对象root其实就是之前的Document,是一个顶级的Element。

然后再通过BeanDefinitionParserDelegate解析这个root下面的每个Element,看看是不是下级Element,如果是,

判断Element是不是默认namespace(比如:beans-这个是默认,security,aop等),如果是:

调用parseDefaultElement(ele, delegate);否则调用delegate.parseCustomElement(ele)。

BeanDefinitionParserDelegate是一个很核心的类,这个类完成对bean对应的element的构造函数,bean参数,

bean的fields的解析并且分别进行保存,之后生成bean的实例的时候需要从中取出这些信息来生成bean实例。

parseCustomElemen方法的主要作用就是根据namespace调用对用的Namespacehandler解析element并且做 相应的操作(比如:securitynamespacehandler的作用是为应用根据security配置文件添加相应的filter).现在主要集中 在parseDefaultElement 方法上面,这个方法是提取bean的关键方法。

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
        {
            if(delegate.nodeNameEquals(ele, "import"))
                importBeanDefinitionResource(ele);
            else
            if(delegate.nodeNameEquals(ele, "alias"))
                processAliasRegistration(ele);
            else
            if(delegate.nodeNameEquals(ele, "bean"))
                processBeanDefinition(ele, delegate);
        }

对于在默认namespace下面的常用标签:import,alias,bean,上面的方法都做了处理。下面主要跟踪一下常用的bean的解析: processBeanDefinition。

        protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
        {
            BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
            if(bdHolder != null)
            {
                bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                try
                {
                    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
                }
                catch(BeanDefinitionStoreException ex)
                {
                    getReaderContext().error((new StringBuilder("Failed to register bean definition with name '")).append(bdHolder.getBeanName()).append("'").toString(), ele, ex);
                }
                getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
            }
        }

至此,真正执行bean解析工作的类出来了:BeanDefinitionParserDelegate 。此类解析Element并且返回一个

BeanDefinitionHolder 对象。然后通过

     BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

支持,把BeanDefinition注册到了beanfactory里面。下面的工作就是取出bean了。

bean的获取

一个常用的方式是通过ApplicationContext接口来获取bean。getBean(String name)的实现类是AbstractApplicationContext。追踪代码,最终发现bean的获取着落在AbstractBeanFactory的doGetBean方法上。继续跟踪,可以跟踪到:AbstractAutowireCapableBeanFactory的doCreateBean方法(对于Prototype的bean),此方法的作用是创建bean的实例。跟踪以上方法,发现是依靠InstantiationStrategy来实例化bean。

有两张策略来实例化bean: SimpleInstantiationStrategy,CglibSubclassingInstantiationStrategy。从名字上很容易看出,前者是不用代理的实例化,后者是通过cglib生成代理类对象的实例化。

至此,bean的取出过程也大概清楚了。因为时间仓促,很多细节来不及描述,等有空了补全这篇文章。


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring代码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏