Spring源码解读 IOC容器初始化

前言

自己对IOC容器和Spring容器理解是不一样,在我看来IOC容器的基本功能就是获取Bean,没有其它功能。而spring容器就是在IOC容器基础上扩展了许多功能,例如生命周期

的管理,支持不同的信息源,应用事件等等。而本篇文章主要就是来分析IOC容器的初始化过程,通过分析这一过程让我们了解,我们定义好bean之后,spring内部做了些什

么。分析源码的版本是spring4.1.7。

正文

在分析之前先看一下配置文件和代码,都是最简单的。凡是都是由浅入深吗。

配置文件

<bean id="user" class="vo.User">
		<property name="name" value="czy" />
	</bean>

实体类

package vo;

public class User {
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
}


测试代码

	ApplicationContext  ctx = new ClassPathXmlApplicationContext("spring.xml");
		User u = (User) ctx.getBean("user");
		System.out.println(u.getName());

整个配置和代码都非常简单,自己觉得对于了解其原理的话,足够了。

从上面的代码中可以猜到IOC容器的初始化过程,在ClassPathXMLApplicationContext的构造方法中。调试代码,发现确实如此。

先看一下ClassPathXmlApplication的构造方法,

	public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}


setConfigLocations()方法主要是讲我们传入的参数"spring.xml"设置进入,没什么可说的。看来主要逻辑在refresh()方法中。refresh()方法如下:

public void refresh() throws BeansException, IllegalStateException { 
    synchronized (this.startupShutdownMonitor) { 
        //1. 刷新前的准备工作 
        prepareRefresh(); 
        //2. 关闭释放旧的beanFactory创建新的beanFactory,读取配置文件等 
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 
        //3. 对beanFactory进行一些基本的初始化 
        prepareBeanFactory(beanFactory); 
  
        try { 
            //4. 下面两行主要用户扩展,处理所有已注册的BeanFactoryPostProcessor,实现在已经加载配置但未初始化bean时对配置进行修改 
            postProcessBeanFactory(beanFactory); 
            invokeBeanFactoryPostProcessors(beanFactory); 
            //5. 处理所有已注册的BeanPostProcessor,主要用于扩展,实现bean初始化前后的一些定制操作 
            registerBeanPostProcessors(beanFactory); 
  
            //6. 初始化消息源bean 
            initMessageSource(); 
            //7. 初始化事件监听器集,也有人叫事件监听器的注册表,所有的事件监听器都在这个bean里进行管理 
            initApplicationEventMulticaster(); 
            //8. 主要用于扩展,实现一些特殊bean的初始化,时间点是类似消息源事件监听器集等特殊bean初始化后,普通的bean初始化前 
            onRefresh(); 
            //9. 注册监听器 
            registerListeners(); 
            //10. 初始化其余的非延迟加载的单例bean 
            finishBeanFactoryInitialization(beanFactory); 
  
            //11. 刷新完成调用LifecycleProcessor的onRefresh方法,并且发布ContextRefreshedEvent事件 
            finishRefresh(); 
        } catch (BeansException ex) { 
            // 销毁已经创建的单例bean 
            destroyBeans(); 
            // 重新设置active标记 
        cancelRefresh(ex); 
            throw ex; 
        } 
    } 
}

整个方法有很多功能,分别针对spring容器各个功能进行初始化,而本篇文章要分析的IOC容器的初始化在方法过程2,obtainFreshBeanFactory()方法中,在该方法中有调

用了refreshBeanFactory(),从名字也可以看出来初始化IOC容器的过程在这个方法里。

protected final void refreshBeanFactory() throws BeansException {
		//1.如果已经有了beanFactory则销毁所有的bean,并关闭beanFactory
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//2.创建BeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			//3.设置BeanFactory的Id,ID是个弱引用。
			beanFactory.setSerializationId(getId());
			//4.应用上下文使用的自定义BeanFactory
			customizeBeanFactory(beanFactory);
			//5.解析我们定义的bean,并添加到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);
		}
	}

在看loadBeanDefitions()方法

	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// 为指定的BeanFactory创建了一个新的XmlBeanDefinitionReader 
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// 设置beanDefinitionReader的参数
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		//初始化BeanDefitionReader
		initBeanDefinitionReader(beanDefinitionReader);
		//通过beanDefitionReader解析我们定义的bean
		loadBeanDefinitions(beanDefinitionReader);
	}

又是一个loadBeanDefitions()方法,不过这次参数的类型是XmlBeanDefinitionReader。在看这个方法
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		//获取资源,然后再次调用XmlBeanDefinitionReader的loadBeanDefinitions()方法
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			reader.loadBeanDefinitions(configLocations);
		}
	}

还是loadBeanDefitions()方法,不过这里是调用XmlBeanDefitionReader的。我调试的时候走的是下面的那个逻辑,在看下面的那个loadBeanDefitions()方法。中间

还有很多简单的loadBeanDefitions()方法,在此不表,在看下面俩个主要的loadBeanDefitions()方法

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// 将我们定义的文件通过ResourceLoader转换成Resource
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// Can only load single resources by absolute URL.
			Resource resource = resourceLoader.getResource(location);
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}


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<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			//获取resource的流
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				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();
			}
		}
	}

漫无止境的loadBeanDefitions方法终于结束了,突然想到了动漫凉宫春日的忧郁里漫无止境的八月的剧情,如果动漫里那么多次八月的循环只是春日为了换回阿虚的一次挽
留,那么这么多漫无止境loadBeanDefitions()的调用就是为了获取我们定义bean文件的流。好像用如果...那么不合适,不过不要紧反正都是自以为。
说了一些有的没的,回到正题,loadBeanDefitions()里出现了Resource,ResourceLoader,ResourcePatternResoulver。
Resource就是spring对我们定义bean文件的抽象,里面声明了获取InputStream的方法。

ResourceLoader定义了如何通过路径抽象成Resource

ResourcePatternResoulver 实现了ResourceLoader,可以传入一个多个路径。

总结一下IOC容器初始化的第一步就是通过ResourceLoader根据文件路径转换成Resource,然后通过Resource获取这个文件的流。

在看一下doLoadBeanDefitions()方法,主要代码如下:

Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);

将IO流,解析成document文档,然后再看一下registerBeanDefitions()方法,

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		documentReader.setEnvironment(getEnvironment());
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

声明一个BeanDefitionDocumentReader来解析document文档,再看一下doucmetReader的registerBeanDefitions()方法,

	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);
	}


获取文档的根元素,然后通过解析根元素,再看一下doRegisterBeanDefitions()方法,

protected void doRegisterBeanDefinitions(Element root) {
		//BeanDefinitionParserDelegate里包含了spring bean定义规则的处理,
		//用它来讲document文档,解析成spring内部使用的数据结构BeanDefition
		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)) {
					return;
				}
			}
		}

		preProcessXml(root);
		//将document文档解析成BeanDeftions的过程。
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

		this.delegate = parent;
	}


再看一下parseBeanDefitons()方法

		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)) {
						//解析默认的元素
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

再看一下parseDefaultELement()方法,

		//处理导入的文件
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		//解析别名
		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);
		}

看一下如何解析Bean

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		//将元素解析成BeanDefinitonHolder
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				//将BeanDefitinionHolder注入容器
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

在这里说明一下BeanDefinitionHolder是BeanDefinition的封装,封装了BeanDefition,bean的名字和别名。而BeanDefition就是spring就是我们定义的各种bean在spring内部的数据结构。

public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {

		this.parseState.push(new BeanEntry(beanName));
		//获取Bean中设置的class的名字,载入到BeanDefitions中
		String className = null;
		if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
		}

		try {
			String parent = null;
			if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
				parent = ele.getAttribute(PARENT_ATTRIBUTE);
			}
			//生创建BeanDefition对象,为Bean定义信息的载入做准备
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);
			//对当前Bean元素进行属性解析,并设置到descrition
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
			//解析Bean的各种元素
			parseMetaElements(ele, bd);
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
			//解析bean的构造函数
			parseConstructorArgElements(ele, bd);
			//解析bean的属性
			parsePropertyElements(ele, bd);
			parseQualifierElements(ele, bd);

			bd.setResource(this.readerContext.getResource());
			bd.setSource(extractSource(ele));

			return bd;
		}
		catch (ClassNotFoundException ex) {
			error("Bean class [" + className + "] not found", ele, ex);
		}
		catch (NoClassDefFoundError err) {
			error("Class that bean class [" + className + "] depends on not found", ele, err);
		}
		catch (Throwable ex) {
			error("Unexpected failure during bean definition parsing", ele, ex);
		}
		finally {
			this.parseState.pop();
		}

		return null;
	}

在这里个人对spring IOC容器的初始化的研究就结束了,至于具体怎么解析bean的各种元素,个人认为不重要,无非就是各种解析XML的规则而已。而整个spring IOC容器加载的过程与结果我认为自己已经了解了。最后做一下总结。

IOC容器初始化主要过程

1.通过ResourceLoader将文件路径转换成Resource。

2.通过Resource获取InputStream

3.通过BeanDefinitionDocumentReader将IO流转换成Document

4.通过BeanDefinitionParserDelegate 把文档中的元素解析成BeanDefinition并放到BeanDefinitionHolder中。

5.将BeanDefinition注册到BeanFactory中。













  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值