Spring IOC 容器初始化过程

我们通过ClassPathXmlApplicationContext容器来探究IOC容器初始化的过程。
如以下代码:通过ApplicationContext创建Spring容器,该容器会读取配置文件" /bean.xml ",并统一管理由文件中定义好的bean实例对象。

 ApplicationContext  applicationContext = new ClassPathXmlApplicationContext("/bean.xml");

下面是IOC容器创建的几个阶段:Resource 定位、 载入BeanDefintion、 将BeanDefinition注册到容器

1、Resource 定位

Resource 是 Spring 中用于封装I/O操作的接口,在创建Spring容器时,会去访问 XML 配置文件,二进制流、URL等方式访问资源。其实类型如下。

  • FileSystemResource:以文件绝对路径进行资源访问。
  • ClassPathResourcee:以类路径的方式访问资源。
  • ServletContextResource:web应用根目录的方式访问资源。
  • UrlResource:访问网络资源的实现类。
  • ByteArrayResource: 访问字节数组资源的实现类。

那对于Spring来说这些类型其又是如何访问的?这儿其实Spring提供了一个ResourceLoader接口用于实现不同的Resource 加载策略。通过该接口实例对象可以获取一个Resource 对象

	// 通过提供的location参数获取Resouces实例
	Resource getResource(String location);
    // 获取ClassLoader,通过ClassLoader可以将资源载入JVM中
    ClassLoader getClassLoader();

注:ApplicationContext 的所有实现类都实现了ResourceLoader接口,可以直接调用getResourceLoader方法获取Resource对象。不同的ApplicatonContext实现类使用getResource方法取得的资源类型不同。例如:
XmlWebApplicationContext.getResource获取的就是ServletContextResource实例
FileSystemXmlApplicationContext.getResource获取的就是FileSystemResource实例
ClassPathXmlApplicationContext.getResource获取的就是ClassPathResource实例

载入BeanDefintion

通过Resouce定位,我们就可以读取到我们bean的资源对象了,现在我们就可以载入BeanDefintion了。那BeanDefintion又是个什么东西呢?其实BeanDefintion就是一个数据结构,其是根据Resource中的bean来生产的,bean会在Spring IOC容器内以BeanDefintion的形式存在,BeanDefinition就是Bean在IoC容器中的存在形式。

以下是载入BeanDefintion的过程

1、首先其会创建一个BeanDefintionReader对象,然后将其作为参数传入loadBeanDefinitions()方法中。

// 该方法属于AbstractXMLApplication
 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
 		// 创建BeanDefintionReader对象,并设置基本的上下文环境
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
        // 用于获取BeanDefintion 对象
        this.loadBeanDefinitions(beanDefinitionReader);
}

2、通过 loadBeanDefintions方法将用户定义的资源和容器本身的资源都载入到reader中

		// 获取到用户定义的Resource资源位置
		Resource[] configResources = this.getConfigResources();  
        if (configResources != null) {
        	// 载入Resource
            reader.loadBeanDefinitions(configResources);
        }

		// 获取容器自身的本地配置文件地址
        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }

3、接下来查看reader.loaderBeanDefintions(configResources) 方法,发现该方法属于AbstractBeanDefintionReader类,其父接口是BeanDefintionReader。

 public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int count = 0;
        Resource[] var3 = resources;
        int var4 = resources.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            Resource resource = var3[var5];
            // 加载所有资源,交给AbstractBeanDefintionReader的子类处理这些Resouce
            count += this.loadBeanDefinitions((Resource)resource);
        }

        return count;
    }

4、在此处我们可以发现loadBeanDefinitions(),其主要实现是XmlBeanDefintionReader来实现的。
在这里插入图片描述
5、回到XmlBeanDefintionReader类中查看loadBeadnDefintions()方法,我们发现其主要通过doLoadBeanDefinitions来实现bean的加载。
在这里插入图片描述
6、现在要我们看看doLoadBeanDefintion的具体内容,其主要是将resource资源文件读取到Document中,并注册到容器里。

	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException
	{
		// 将resource资源文件中的内容读取到document中
		Document doc = this.doLoadDocument(inputSource, resource);
		// 将document 文件的bean封装为BeanDefintion,并注册到容器
		int count = this.registerBeanDefinitions(doc, resource);
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Loaded " + count + " bean definitions from " + resource);
		}
		return count;
	}

7、现在接着深入到registerBeanDefinitions()方法中,它主要对Spring Bean语义进行转化,变成BeanDefintion类型。首先获取DefaultBeanDefinitionDocumentReader实例,然后获取容器中的bean数量,通过documentReader中的registerBeanDefinitions方法进行注册和转化工作。

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		// 获取DeanDefintionCocumentReader 实例
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        // 获取容器中要注册的bean数量
        int countBefore = this.getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }

8、顺着上面的思路继续往下,在DefaultBeanDefinitionDocumentReader 中的registerBeanDefinitions 方法如图11所示,其获取document的根结点然后顺势访问所有的子节点。同时把处理BeanDefinition的过程委托给BeanDefinitionParserDelegate对象来完成。

	protected void doRegisterBeanDefinitions(Element root) {
		//处理BeanDefintion的过程委托给BeanDefintionParserDelegate实例来完成
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute("profile");
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                    }

                    return;
                }
            }
        }

        this.preProcessXml(root);
        // 核心方法,代理
        this.parseBeanDefinitions(root, this.delegate);
        this.postProcessXml(root);
        this.delegate = parent;
    }

BeanDefinitionParserDelegate类主要负责BeanDefinition的解析,涉及到JDK和CGLIB动态代理,BeanDefinitionParserDelegate代理类会完成对符合Spring Bean语义规则的处理,比如、、等的检测。
9、查看parseBeanDefinitions方法,其主要通过BeanDefinitionParserDelegate代理类中的parseBeanDefinitions方法,用来对XML文件中的节点进行解析。通过遍历import标签节点调用importBeanDefinitionResource方法对其进行处理,然后接着便利bean节点调用processBeanDefinition对其处理。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();

            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)) {
                    	// 对节点元素进行处理的。从方法体的语句可以看出它对import标签、alias标签、bean标签进行了处理。
                    	// 每类标签对应不同的BeanDefinition的处理方法。
                        this.parseDefaultElement(ele, delegate);
                    } else {
                    	// 用户自己已节点处理方式
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }

    }

10、现在查看parseDefaultElement()方法,我们发现其是更具不同的标签进行不同的处理。

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

11、现在我们选取processBeanDefinition()方法查看其是如何处理Bean的,首先通过delegate的parseBeanDefinitionElement方法传入节点信息,获取该Bean对应的name和alias。然后通过BeanDefinitionReaderUtils中的registerBeanDefinition方法对其进行容器注册,也就是将Bean实例注册到容器中进行管理。最后,发送注册事件。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

            try {
            	// 注册Bean实例
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
            } catch (BeanDefinitionStoreException var5) {
                this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
            }

            this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }

    }

将BeanDefintion对象注册到容器

我们接着上面的registerBeanDefinition方法来看,Bean会被解析成BeanDefinition并与BeanName、Alias一同封装到BeanDefinitionHolder类中, 之后beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition()),注册到DefaultListableBeanFactory.beanDefinitionMap中。如果客户端需要获取Bean对象,Spring容器会根据注册的BeanDefinition信息进行实例化。

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
        String beanName = definitionHolder.getBeanName();
        // 注册BeanDefintion
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            String[] var4 = aliases;
            int var5 = aliases.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                String alias = var4[var6];
                registry.registerAlias(beanName, alias);
            }
        }

    }

DefaultListableBeanFactory实现了上面调用BeanDefinitionRegistry接口的 registerBeanDefinition( beanName, bdHolder.getBeanDefinition())方法。这一部分的主要逻辑是向DefaultListableBeanFactory对象的beanDefinitionMap中存放beanDefinition,也就是说beanDefinition都放在beanDefinitionMap中进行管理。当初始化容器进行bean初始化时,在bean的生命周期分析里必然会在这个beanDefinitionMap中获取beanDefition实例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值