初始化IoC容器(Spring源码阅读)
我们到底能走多远系列(31)
扯淡:
有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的。未婚的你们,你们是怎么分配工资的?
毕竟,对自己的收入的分配差不多体现了自己的现状,以及自己对自己未来有什么样的期许~
主题:
本人在阅读源码基本参考了《Spring技术内幕:深入解析 Spring架构与设计原理》,很不错的书籍,建议大家阅读。
初始化IoC容器:1,resource定位 2,BeanDefinition载入 3,把BeanDefinition载入到IoC
重要的解释:
Spring中的IoC容器,所谓的容器核心就是个hashMap,准确的说是ConcurrentHashMap,键值对就是:<String, BeanDefinition> key是bean的name。
那么BeanDefinition就是对外界bean描述的抽象,比如你写的<bean>标签,最会被抽象成一个BeanDefinition放进这个Map中去。
所以一个完整的IoC容器来说,只要提供对这个Map的必要操作就可以了。一般DefaultListableBeanFactory 作为一个默认功能完整的IoC容器来使用。
上面的解释已经很清楚了,IoC容器的初始化就可以理解成,我们有个xml描述了一些bean的属性,Spring把它读进来,按照自己的规则解析一边,把其中的Bean描述转换成一个个BeanDefinition放进Map中去就完成初始化了。
拿FileSystemXmlApplicationContext为例子看一下源码中是如何实现的:(希望有兴趣的先看下Spring中BeanFactory下面的继承结构)
我们会写类似下面的代码开始我们的Spring之旅:
ApplicationContext ct = new FileSystemXmlApplicationContext("src/service/service.xml");
FileSystemXmlApplicationContext的构造函数:
public FileSystemXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { // 起点 refresh(); } }
调用的refresh()方法是AbstractApplicationContext的方法:(这个方法就像执行顺序图,描述好了先做什么,后做什么)
obtainFreshBeanFactory()方法:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 这个方法是在AbstractApplicationContext定义没有实现的abstract方法,这就是所谓的模板模式吧,后面很多地方这样实现的,可以学习下。 refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; } protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
refreshBeanFactory()方法是谁实现的呢?子类去实现。AbstractRefreshableApplicationContext实现的:
protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try {
// 这里我们就看到了,通过这个方法调用来初始化的,都使用了DefaultListableBeanFactory作为IoC容器 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory);
// 这个方法就去完成load Bean啦,不过它也使用了子类的实现 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
AbstractXmlApplicationContext实现的loadBeanDefinitions(DefaultListableBeanFactory beanFactory):
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. // 更深一层后,这里就开始定义了Reader是什么了。我们要读取的xml文件,这个Reader可以帮助我们 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader);
// 调用下面的的方法 loadBeanDefinitions(beanDefinitionReader); } protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) {
// 把工作交给XmlBeanDefinitionReader完成 reader.loadBeanDefinitions(configLocations); } }
通过XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader的方法把String configLocations封装成Recource,然后调用XmlBeanDefinitionReader实现的loadBeanDefinitions(resource)方法。
下面是父类的调用方法:
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0;
// 这里的for就可以理解多个xml文件的情况了
// 从外面看,一个bean首先是放在多个xml中的一个,然后是一个xml文件中的多个bean描述中的一个,这个也是定位到一个bean要走的路。 for (Resource resource : resources) {
// 交给子类实现 counter += loadBeanDefinitions(resource); } return counter; }
子类实现:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } 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 {
// 用流的方式来读取,有点样子了哦~ InputStream inputStream = encodedResource.getResource().getInputStream(); try {
// 只是一个封装类,里面有属性防这个流而已 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); }
// 我觉得到这里,看方法名就知道了,要开始do了,前面好像都没do嘛 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(); } } } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource);
// 先解析成Document Document doc = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
// 按照Spring的规则开始解析,放进BeanDefinition return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
XmlBeanDefinitionReader的registerBeanDefinitions方法:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); documentReader.setEnvironment(this.getEnvironment()); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
上面代码可以看到:继续交给DefaultBeanDefinitionDocumentReader来解析Document:
下面是详细的处理过程:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); } protected void doRegisterBeanDefinitions(Element root) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { Assert.state(this.environment != null, "environment property must not be null"); String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!this.environment.acceptsProfiles(specifiedProfiles)) { return; } } // any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createHelper(readerContext, root, parent); preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; } protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes();
// 前面的注释中提到过bean定位时要从多个bean描述中一个个取,这个for循环就是了,
//所以这个否循环里面的逻辑就是处理一个个bean的映射的,当然也有其他的标签,这里我们且认为只有bean吧 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方法:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { // 对各种标签的解析 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// 看下个个方法 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse ==>注意这个beans就递归,有调用了前面的doRegisterBeanDefinitions方法 doRegisterBeanDefinitions(ele); } }
processBeanDefinition方法:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 这里就出现了BeanDefinitionHolde 它封装了BeanDefinition
// BeanDefinitionParserDelegate 来完成最底层的按照spring的规则解析 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. 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)); } }
parseBeanDefinitionElement方法的代码:
上面解析的代码中有调用的一个方法parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)带我们去解析bean中更加详细的属性:
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) {// class属性 className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) {// parent属性 parent = ele.getAttribute(PARENT_ATTRIBUTE); } AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 下面parse方法解析其他的属性 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); parseConstructorArgElements(ele, bd); parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; }
// 下面的错误用spring有时候有看见过吧 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; }
以上就是解析好了,然后就是放进传说中的IoC容器里啦:
BeanDefinitionReaderUtils的registerBeanDefinition方法:
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName();
// 调用了下BeanDefinitionRegistry 的方法,
// 而这个registry就是DefaultListableBeanFactory,所有的BeanDefinition都要注册到它里面去呀... registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String aliase : aliases) { registry.registerAlias(beanName, aliase); } } }
DefaultListableBeanFactory的registerBeanDefinition:
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); } } synchronized (this.beanDefinitionMap) { Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 保证加载bean唯一性 if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } } else { this.beanDefinitionNames.add(beanName); this.frozenBeanDefinitionNames = null; }
// 放到前面提到的ConcurrentHashMap里面去喽~ this.beanDefinitionMap.put(beanName, beanDefinition); resetBeanDefinition(beanName); } }
以上就是初始化IoC容器的过程。各位有兴趣可以自己debug进去一步步看既可以了。
另外用maven的话直接用下面命令下载源码即可:
mvn dependency:sources
让我们继续前行
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不会成功。
共勉。