Spring如何实现将资源配置通过加载、解析,生成BeanDefinition并注册到IOC容器中的
如何将Bean从XML配置文件中解析后放到IOC容器中的
-
初始化入口(以XML配置为例):
ApplicationContext context = new ClassPathXmlApplicationContext("aspects.xml", "daos.xml", "services.xml");
-
查看ClassPathXmlApplicationContext的源码(构造器)
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException { this(configLocations, true, (ApplicationContext)null); } public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { // 设置Bean资源加载器 super(parent); // 设置配置路径 this.setConfigLocations(configLocations); // 初始化容器 if (refresh) { this.refresh(); } }
-
设置资源解析器和环境:super(parent)
调用父类容器AbstractApplicationContext的构造方法,为容器设置好Bean资源加载器
public AbstractApplicationContext(@Nullable ApplicationContext parent) { // 默认构造函数初始化容器id, name, 状态 以及 资源解析器 this(); // 将父容器的Environment合并到当前容器 this.setParent(parent); }
默认构造器初始化容器ID、Name、状态以及资源解析器
public AbstractApplicationContext() { this.logger = LogFactory.getLog(this.getClass()); this.id = ObjectUtils.identityToString(this); this.displayName = ObjectUtils.identityToString(this); this.beanFactoryPostProcessors = new ArrayList(); this.active = new AtomicBoolean(); this.closed = new AtomicBoolean(); this.startupShutdownMonitor = new Object(); this.applicationStartup = ApplicationStartup.DEFAULT; this.applicationListeners = new LinkedHashSet(); this.resourcePatternResolver = this.getResourcePatternResolver(); } // Spring资源加载器 protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this); }
通过AbstractApplicationContext的setParent()方法将父容器的Environment合并到当前容器
public void setParent(@Nullable ApplicationContext parent) { this.parent = parent; if (parent != null) { Environment parentEnvironment = parent.getEnvironment(); if (parentEnvironment instanceof ConfigurableEnvironment) { this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment); } } }
-
设置配置路径:this.setConfigLocations(configLocations)
在设置容器的资源加载器之后,接下来ClassPathXmlApplicationContext执行setConfigLocations方法通过调用其父类AbstractRefreshableConfigContext的方法进行对Bean定义资源文件的定位
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] = this.resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } } protected String resolvePath(String path) { // 从Environment中解析配置路径 return this.getEnvironment().resolveRequiredPlaceholders(path); }
-
初始化的主体流程:refresh()
Spring IOC容器对Bean定义资源的载入是从refresh()方法开始的,refresh()是一个模版方法,refresh()方法的作用是:在创建IOC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新创建的IOC容器,refresh的作用类似于对IOC容器的重启,在新建号的容器中对容器进行初始化,对Bean定义资源进行载入
refresh()方法源码@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // 初始化配置资源、环境资源 prepareRefresh(); // 通知子类刷新内部 bean 工厂。 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 初始化Bean工厂 prepareBeanFactory(beanFactory); try { //允许在上下文子类中对Bean工厂进行后置处理。 postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // 调用在上下文中注册为 bean 的工厂处理器 invokeBeanFactoryPostProcessors(beanFactory); // 注册拦截bean创建的bean处理器。 registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // 为这个上下文初始化消息源。 initMessageSource(); // 初始化此上下文的事件监听器。 initApplicationEventMulticaster(); // 在具体的上下文子类中初始化其他特殊的 Bean。 onRefresh(); // 检查监听器bean并注册它们。 registerListeners(); // 实例化所有剩余的(非lazy-init)单例。 finishBeanFactoryInitialization(beanFactory); // 最后一步:发布相应的事件。 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 销毁已经创建的单例,以避免悬空资源。 destroyBeans(); // 重置“active”标志。 cancelRefresh(ex); // 将异常传递给调用者。 throw ex; } finally { // 重置Spring核心中的常见内置缓存,因为我们可能不再需要单例bean的元数据... resetCommonCaches(); contextRefresh.end(); } } }
refresh()方法是非常典型的资源类加载处理型的思路
-
模版方法设计模式,模版方法中使用典型的钩子方法
-
将具体的初始化加载方法插入到钩子方法之间
-
将初始化的阶段封装,用来记录当前初始化到什么阶段;常见的设计是xxxPhase/xxxStage;
-
资源加载初始化有失败等处理,必然是try-catch-finally
-
obtainFreshBeanFactory
AbstractAppplicationContext的obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()方法,启动容器加载Bean定义资源文件的过程
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法 refreshBeanFactory(); return getBeanFactory(); }
AbstractAppplicationContext类只抽象定义了refreshBeanFactory()方法,容器真正调用的是其子类AbstractRefreshableApplicationContext实现的refreshBeanFactory()方法
protected final void refreshBeanFactory() throws BeansException { // 如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { // 创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean定义 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); // 对IoC容器进行定制化,如设置启动参数,开启注解的自动装配等 loadBeanDefinitions(beanFactory); // 调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器 this.beanFactory = beanFactory; } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
protected final void refreshBeanFactory() throws IllegalStateException { if (this.refreshed) { throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once"); } else { this.beanFactory.setSerializationId(this.getId()); this.refreshed = true; } }
-
loadBeanDefinitions
AbstractRefreshableApplicationContext中只定义了抽象的loadBeanDefinition方法,容器真正调用的是其子类AbstractXmlApplicationContext对该方法的实现
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 配置上下文的环境,资源加载器、解析器 beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 为Bean读取器设置SAX xml解析器 // 允许子类自行初始化(比如校验机制),并提供真正的加载方法 initBeanDefinitionReader(beanDefinitionReader); // 当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制 loadBeanDefinitions(beanDefinitionReader); } protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { // 加载XML配置方式里的Bean定义的资源 Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } // 加载构造函数里配置的Bean配置文件,即{"aspects.xml", "daos.xml", "services.xml"} String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
由于我们使用ClassPathXmlApplicationContext作为例子分析,因此getConfigResources的返回值为null,因此程序执行reader.loadBeanDefinitions(configLocations)分支。
XmlBeanDefinitionReader(XML Bean读取器)调用其父类AbstractBeanDefinitionReader的reader.loadBeanDefinitions方法读取Bean定义资源
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; Resource[] var3 = resources; int var4 = resources.length; for(int var5 = 0; var5 < var4; ++var5) { Resource resource = var3[var5]; counter += this.loadBeanDefinitions((Resource)resource); } return counter; } public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int counter = 0; String[] var3 = locations; int var4 = locations.length; for(int var5 = 0; var5 < var4; ++var5) { String location = var3[var5]; counter += this.loadBeanDefinitions(location); } return counter; }
-
AbstractBeanDefinitionReader读取Bean定义资源
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = this.getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } else { int count; //模式匹配类型的解析器,这种方式是加载多个满足匹配条件的资源 if (resourceLoader instanceof ResourcePatternResolver) { try { //获取到要加载的资源 Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location); //委派调用其子类的XmlBeanDefinitionReader的方法,实现加载的功能 count = this.loadBeanDefinitions(resources); if (actualResources != null) { Collections.addAll(actualResources, resources); } if (this.logger.isTraceEnabled()) { this.logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } catch (IOException var6) { throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var6); } } else { //如果模式匹配无法匹配到,则只能通过绝对路径URL加载单个资源 Resource resource = resourceLoader.getResource(location); count = this.loadBeanDefinitions((Resource)resource); if (actualResources != null) { actualResources.add(resource); } if (this.logger.isTraceEnabled()) { this.logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } } }
源码分析该方法做了两件事:
-
调用资源加载器的获取资源方法resouceLoader.getResource(location),获取到要加载的资源
-
真正执行加载功能的是其子类XmlBeanDefinitionReader的loadBeanDefinitions方法
-
-
XmlBeanDefinitionReader解析载入的Bean定义资源文件
//本质上是加载XML配置的Bean public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { //加载前确定xml资源的编码类型 return this.loadBeanDefinitions(new EncodedResource(resource)); } public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (this.logger.isInfoEnabled()) { this.logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } //获取当前正在加载的资源 Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!((Set)currentResources).add(encodedResource)) { throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } else { int var5; try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //调用doLoadBeanDefinitions来载入资源数据进行解析 var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException var15) { throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15); } finally { ((Set)currentResources).remove(encodedResource); if (((Set)currentResources).isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } return var5; } }
public int loadBeanDefinitions(InputSource inputSource, String resourceDescription) throws BeanDefinitionStoreException { //输入资源和描述性资源统一加载 return this.doLoadBeanDefinitions(inputSource, new DescriptiveResource(resourceDescription)); } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //将Bean定义资源转换为Document对象 Document doc = this.doLoadDocument(inputSource, resource); //获取到的Document对象来注册Bean的定义资源信息 return this.registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException var4) { throw var4; } catch (SAXParseException var5) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5); } catch (SAXException var6) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6); } catch (ParserConfigurationException var7) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7); } catch (IOException var8) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8); } catch (Throwable var9) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9); } } protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware()); }
-
DocumentLoader将Bean定义资源转换为Document对象
DocumentLoader将Bean定义资源转换成Document对象
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { //创建文件解析器工厂 DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } //创建文档解析器 DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource);//解析资源 } protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware) throws ParserConfigurationException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(namespaceAware); //设置解析xml的校验状态 if (validationMode != 0) { factory.setValidating(true); if (validationMode == 3) { factory.setNamespaceAware(true); try { factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); } catch (IllegalArgumentException var6) { ParserConfigurationException pcex = new ParserConfigurationException("Unable to validate using XSD: Your JAXP provider [" + factory + "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " + "Upgrade to Apache Xerces (or Java 1.5) for full XSD support."); pcex.initCause(var6); throw pcex; } } } return factory; } protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler) throws ParserConfigurationException { DocumentBuilder docBuilder = factory.newDocumentBuilder(); if (entityResolver != null) { docBuilder.setEntityResolver(entityResolver); } if (errorHandler != null) { docBuilder.setErrorHandler(errorHandler); } return docBuilder; }
-
XmlBeanDefinitionReader解析载入的Bean定义资源文件
解析XML资源文件转换为Document对象后,通过XMLBeanDefinitionReader的doLoadBeanDefinitions方法,首先对载入的Bean定义资源进行Document对象的转换(),然后调用registerBeanDefinitions启动SpringIOC容器对Bean定义的解析过程
//这里就是按照Spring的Bean语义要求将Bean定义资源解析为容器内部数据结构 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //创建BeanDefinitionDocumentReader对象,解析Document对象 BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader(); int countBefore = this.getRegistry().getBeanDefinitionCount(); //解析过程入口,这里依旧采用了委派模式,将具体解析实现的过程交给实现类DefaultBeanDefinitionDocumentReader完成 documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource)); //返回此次解析了多少个对象 return this.getRegistry().getBeanDefinitionCount() - countBefore; } protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { return (BeanDefinitionDocumentReader)BeanUtils.instantiateClass(this.documentReaderClass); } //创建读取xml文件的上下文 public XmlReaderContext createReaderContext(Resource resource) { return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, this.getNamespaceHandlerResolver()); }
Bean定义资源的载入解析分为两个过程
-
通过调用XML解析器将Bean定义资源文件转换得到Document对象,但是这些Document对象并没有按照Spring的Bean规则进行解析,这是一个载入的过程
-
在完成通用的XML解析之后,按照Spring的Bean规则对Document对象进行解析
-
-
DefaultBeanDefinitionDocumentReader对Bean定义的Document对象解析
BeanDefinitionDocumentReader接口通过registerBeanDefinitions方法调用其实现类对Document对象进行解析
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; this.doRegisterBeanDefinitions(doc.getDocumentElement()); } protected void doRegisterBeanDefinitions(Element root) { //任何嵌套的<beans>元素都会导致该方法中的递归。 //在这里为了正确地传递和保存<beans> default-*属性,跟踪当前(父)委托,该委托可能为null。 //创造新的(子)委托具有用于回退目的的对父的引用,然后最终将this.delegate重置回其原始(父)引用。 //这种行为模拟了一堆委托,而实际上不需要一个。 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); //从Document的根元素开始解析Bean定义的Document对象 this.parseBeanDefinitions(root, this.delegate); this.postProcessXml(root); this.delegate = parent; }
-
BeanDefinitionParserDelegate解析Bean定义资源文件生成BeanDefinition
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)) { this.parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //如果元素节点是<import>导入元素,进行导入解析 if (delegate.nodeNameEquals(ele, "import")) { this.importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, "alias")) { //如果元素节点是<Alias>别名元素,进行别名解析 this.processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, "bean")) { //如果元素节点<Bean>元素,按照Spring的Bean规则解析元素 this.processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, "beans")) { //如果元素节点<Bean>元素,即它是嵌套类型,就会进行递归解析 this.doRegisterBeanDefinitions(ele); } }
//解析各种xml元素,生成BeanDefinition protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { //注册最终的装饰实例 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)); } }
-
解析过后的BeanDefinition在IoC容器中的注册
Document对象的解析后得到封装BeanDefinition的BeanDefinitionHold对象,然后调用BeanDefinitionReaderUtils的registerBeanDefinition方法向IOC容器注册解析的Bean。
//通过BeanDefinitionRegistry将BeanDefinitionHolder注册到BeanFactory public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { //在唯一name下注册bean定义。 String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); //如果name有别名,就要注册name的别名 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); } } }
当调用BeanDefinitionReaderUtils向IoC容器注册解析的BeanDefinition时,真正完成注册功能的是DefaultListableBeanFactory。
-
DefaultListableBeanFactory向IoC容器注册解析后的BeanDefinition
IOC容器的本质就是一个BeanDefinitionMap,注册就是将BeanDefinition对象put到Map中
//Bean定义对象的映射,由Bean的name控制 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256); //从Bean name映射到合并的BeanDefinitionHolder private final Map<String, BeanDefinitionHolder> mergedBeanDefinitionHolders = new ConcurrentHashMap(256); 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 var8) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var8); } } BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName); //如果已经注册过了 if (existingDefinition != null) { //检查是否可以覆盖 if (!this.isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } if (existingDefinition.getRole() < beanDefinition.getRole()) { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (this.logger.isDebugEnabled()) { this.logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (this.logger.isTraceEnabled()) { this.logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } //覆盖操作 this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (this.hasBeanCreationStarted()) { //无法再修改启动时间的集合元素,用于稳定迭代 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; this.removeManualSingletonName(beanName); } } else { //仍然处于启动注册的阶段 this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.removeManualSingletonName(beanName); } //重置所有已经注册过的BeanDefinition的缓存 this.frozenBeanDefinitionNames = null; } if (existingDefinition == null && !this.containsSingleton(beanName)) { if (this.isConfigurationFrozen()) { this.clearByTypeCache(); } } else { this.resetBeanDefinition(beanName); } }
到这里,Bean定义资源文件中配置的Bean被解析后,已经注册到IOC容器中,IOC容器统一管理这些Bean实例,真正完成了IOC容器的初始化工作。这些BeanDefinition信息已经可以使用,并可以被检索,IOC容器的作用就是对这些注册的Bean定义信息进行处理和维护。
这些注册的Bean定义信息是IOC容器控制反转的基础,正是有了这些注册的数据,容器才可以进行依赖注入
-
总结
通过上面的源码解析,可以得到IOC容器初始化的基本步骤:
初始化的入口在容器实现中的 refresh()调用来完成
对 bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition,其中的大致过程如下:
-
通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader 是默认的实现,同时上下文本身就给出了 ResourceLoader 的实现,可以从类路径,文件系统, URL 等方式来定为资源位置。如果是 XmlBeanFactory作为 IOC 容器,那么需要为它指定 bean 定义的资源,也就是说 bean 定义文件时通过抽象成 Resource 来被 IOC 容器处理的
-
通过 BeanDefinitionReader来完成定义信息的解析和 Bean 信息的注册, 往往使用的是XmlBeanDefinitionReader 来解析 bean 的 xml 定义文件 - 实际的处理过程是委托给 BeanDefinitionParserDelegate 来完成的,从而得到 bean 的定义信息,这些信息在 Spring 中使用 BeanDefinition 对象来表示 - 这个名字可以让我们想到loadBeanDefinition,RegisterBeanDefinition 这些相关的方法 - 他们都是为处理 BeanDefinitin 服务的
-
容器解析得到 BeanDefinition 以后,需要把它在 IOC 容器中注册,这由 IOC 实现 BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器内部维护的一个HashMap 来保存得到的 BeanDefinition 的过程。这个 HashMap 是 IoC 容器持有 bean 信息的场所,以后对 bean 的操作都是围绕这个HashMap 来实现的.
然后我们就可以通过 BeanFactory 和 ApplicationContext 来享受到 Spring IOC 的服务了,在使用 IOC 容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IoC 风格编写的应用程序代码完全不用关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。 Spring 本身提供了对声明式载入 web 应用程序用法的应用程序上下文,并将其存储在ServletContext 中的框架实现。