内容简介:Spring源码教程02--Spring的IoC容器分析
上一节 “[原创]Spring教程01--Spring开始篇_Helloworld” 中简单的介绍SpringFramwork的简单使用;通过Helloworld的程序做演示,本节将继续解读Spring的Ioc容器实现和分析。
Spring IoC容器启动过程
Spring的IoC容器启动大致分为下面的三个步骤: Resource定位 、 Resoure的载入 、 Resoured的注册 三个步骤;下面使用xml文件配置方式显示的配置方式步骤:
首先看下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)加载配置文件的路径
(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文件的解析
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容器
@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");
这样一句代码其实;也写了一章节;其实如果你是看着篇文章可以参考我的方法堆栈进行阅读。最后文章的部分其实源都简明的展示给你;后期我希望可以使用更多图展示给大家。
参考资料:
- 《Spring技术内幕》
- Spring-IOC核心源码学习
- 【Spring源码分析】Bean加载流程概览
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Spring源码探究:容器
- springMVC容器加载源码分析
- Spring容器创建源码解析
- 详解CopyOnWrite容器及其源码
- stl 源码阅读之容器vertor
- Spring源码解析:高级容器的扩展内幕
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
C++ API设计
[美] Martin Reddy / 刘晓娜、臧秀涛、林健 / 人民邮电出版社 / 2013-8 / 89.00
现代软件开发中的一大难题就是如何编写优质的API。API负责为某个组件提供逻辑接口并隐藏该模块的内部细节。多数程序员依靠的是经验和冒险,从而很难达到健壮、高效、稳定、可扩展性强的要求。Martin Reddy博士在自己多年经验基础之上,对于不同API风格与模式,总结出了API设计的种种最佳策略,着重针对大规模长期开发项目,辅以翔实的代码范例,从而有助于设计决策的成功实施,以及软件项目的健壮性及稳定......一起来看看 《C++ API设计》 这本书的介绍吧!