Spring源码教程02--Spring的IoC容器分析

栏目: Java · 发布时间: 6年前

内容简介:Spring源码教程02--Spring的IoC容器分析

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

Spring IoC容器启动过程

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

Spring源码教程02--Spring的IoC容器分析

首先看下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定位

为了便于理解;提前解析了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接口,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 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)加载配置文件的路径

Spring源码教程02--Spring的IoC容器分析

(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文件的解析

Spring源码教程02--Spring的IoC容器分析
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容器

Spring源码教程02--Spring的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"); 这样一句代码其实;也写了一章节;其实如果你是看着篇文章可以参考我的方法堆栈进行阅读。最后文章的部分其实源都简明的展示给你;后期我希望可以使用更多图展示给大家。

参考资料:


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据压缩导论(第4版)

数据压缩导论(第4版)

[美] Khalid Sayood / 贾洪峰 / 人民邮电出版社 / 2014-1 / 129.00

数据压缩已经成为信息革命的一门支撑技术,这场革命已经改变了我们的生活,而在此过程中,数据压缩也变得几乎无处不在。从MP3播放器到智能手机,再到数字电视和数字电影,数据压缩几乎成了所有信息技术的必备要素。 近年来,以大数据为标志的互联网技术高歌猛进。数据规模大、产生速度快、来源多样等特性,导致数据存储和处理都前所未有地复杂。《数据压缩导论(第4版)》作为迄今为止数据压缩领域最全面而深入的著作,......一起来看看 《数据压缩导论(第4版)》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

MD5 加密
MD5 加密

MD5 加密工具

html转js在线工具
html转js在线工具

html转js在线工具