`
IXHONG
  • 浏览: 451845 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring的IoC容器实现原理(一)#loadBeanDefinition

阅读更多

Spring有十几个组件,核心组件为bean(演员)-context(舞台)-core(道具)

bean包装的是object,而object中肯定要有数据,如何给这些数据提供生存环境就是context要解决的问题,对于context来说他就是要发现每个bean之间的关系,为他们建立起来并维护好这些关系。所以context就是一个bean关系的集合,这个关系集合就是我们常说的IOC容器。core组件就是发现、建立和维护每个bean之间的关系所需要的一些工具,把core叫做util更为贴切。 

 

IOC的基础 
下面我们从IOC/AOP开始,它们是Spring平台实现的核心部分;虽然,我们一开始大多只是在这个层面上,做一些配置和外部特性的使用工作,但对这两个核心模块工作原理和运作机制的理解,对深入理解Spring平台,却是至关重要的;因为,它们同时也是Spring其他模块实现的基础。从Spring要做到的目标,也就是从简化Java EE开发的出发点来看,简单的来说,它是通过对POJO开发的支持,来具体实现的;具体的说,Spring通过为应用开发提供基于POJO的开发模式,把应用开发和复杂的Java EE服务,实现解耦,并通过提高单元测试的覆盖率,从而有效的提高整个应用的开发质量。这样一来,实际上,就需要把为POJO提供支持的,各种Java EE服务支持抽象到应用平台中去,去封装起来;而这种封装功能的实现,在Spring中,就是由IOC容器以及AOP来具体提供的,这两个模块,在很大程度上,体现了Spring作为应用开发平台的核心价值。它们的实现,是Rod.Johnson在他的另一本著作《Expert One-on-One J2EE Development without EJB》 中,所提到Without EJB设计思想的体现;同时也深刻的体现了Spring背后的设计理念。 

从更深一点的技术层面上来看,因为Spring是一个基于Java语言的应用平台,如果我们能够对Java计算模型,比如像JVM虚拟机实现技术的基本原理有一些了解,会让我们对Spring实现的理解,更加的深入,这些JVM虚拟机的特性使用,包括像反射机制,代理类,字节码技术等等。它们都是在Spring实现中,涉及到的一些Java计算环境的底层技术;尽管对应用开发人员来说,可能不会直接去涉及这些JVM虚拟机底层实现的工作,但是了解这些背景知识,或多或少,对我们了解整个Spring平台的应用背景有很大的帮助;打个比方来说,就像我们在大学中,学习的那些关于计算机组织和系统方面的基本知识,比如像数字电路,计算机组成原理,汇编语言,操作系统等等这些基本课程的学习。虽然,坦率的来说,对我们这些大多数课程的学习者,在以后的工作中,可能并没有太多的机会,直接从事这么如此底层的技术开发工作;但具备这些知识背景,为我们深入理解基于这些基础技术构架起来的应用系统,毫无疑问,是不可缺少的。随着JVM虚拟机技术的发展,可以设想到的是,更多虚拟机级别的基本特性,将会持续的被应用平台开发者所关注和采用,这也是我们在学习平台实现的过程中,非常值得注意的一点,因为这些底层技术实现,毫无疑问,会对Spring应用平台的开发路线,产品策略产生重大的影响。同时,在使用Spring作为应用平台的时候,如果需要更深层次的开发和性能调优,这些底层的知识,也是我们知识库中不可缺少的部分。有了这些底层知识,理解整个系统,想来就应该障碍不大了。 

IOC的一点认识 
对Spring IOC的理解离不开对依赖反转模式的理解,我们知道,关于如何反转对依赖的控制,把控制权从具体业务对象手中转交到平台或者框架中,是解决面向对象系统设计复杂性和提高面向对象系统可测试性的一个有效的解决方案。这个问题触发了IoC设计模式的发展,是IoC容器要解决的核心问题。同时,也是产品化的IoC容器出现的推动力。而我觉得Spring的IoC容器,就是一个开源的实现依赖反转模式的产品。 

那具体什么是IoC容器呢?它在Spring框架中到底长什么样?说了这么多,其实对IoC容器的使用者来说,我们常常接触到的BeanFactory和ApplicationContext都可以看成是容器的具体表现形式。这些就是IoC容器,或者说在Spring中提IoC容器,从实现来说,指的是一个容器系列。这也就是说,我们通常所说的IoC容器,如果深入到Spring的实现去看,会发现IoC容器实际上代表着一系列功能各异的容器产品。只是容器的功能有大有小,有各自的特点。打个比方来说,就像是百货商店里出售的商品,我们举水桶为例子,在商店中出售的水桶有大有小;制作材料也各不相同,有金属的,有塑料的等等,总之是各式各样,但只要能装水,具备水桶的基本特性,那就可以作为水桶来出售来让用户使用。这在Spring中也是一样,它有各式各样的IoC容器的实现供用户选择和使用;使用什么样的容器完全取决于用户的需要,但在使用之前如果能够了解容器的基本情况,那会对容器的使用是非常有帮助的;就像我们在购买商品时进行的对商品的考察和挑选那样。 

我们从最基本的XmlBeanFactory看起,它是容器系列的最底层实现,这个容器的实现与我们在Spring应用中用到的那些上下文相比,有一个非常明显的特点,它只提供了最基本的IoC容器的功能。从它的名字中可以看出,这个IoC容器可以读取以XML形式定义的BeanDefinition。理解这一点有助于我们理解ApplicationContext与基本的BeanFactory之间的区别和联系。我们可以认为直接的BeanFactory实现是IoC容器的基本形式,而各种ApplicationContext的实现是IoC容器的高级表现形式。 

仔细阅读XmlBeanFactory的源码,在一开始的注释里面已经对 XmlBeanFactory的功能做了简要的说明,从代码的注释还可以看到,这是Rod Johnson在2001年就写下的代码,可见这个类应该是Spring的元老类了。它是继承DefaultListableBeanFactory这个类的,这个DefaultListableBeanFactory就是一个很值得注意的容器! 

Java代码  收藏代码
  1. public class XmlBeanFactory extends DefaultListableBeanFactory {  
  2.     private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);  
  3.     public XmlBeanFactory(Resource resource) throws BeansException {  
  4.         this(resource, null);  
  5.     }  
  6.     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {  
  7.         super(parentBeanFactory);  
  8.         this.reader.loadBeanDefinitions(resource);  
  9.     }  
  10. }  

BeanFactory测试:

public class BeanFactoryTest {

    public static void main(String[] args) {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource res = resolver.getResource("classpath:spring-test.xml");
        BeanFactory bf = new XmlBeanFactory(res);
        System.out.println("init BeanFactory");

        Mars mars = bf.getBean("mars", Mars.class);
        System.out.println(mars.getCnName() + ":" + mars.getAge());
    }
}

 
XmlBeanFactory的功能是建立在DefaultListableBeanFactory这个基本容器的基础上的,在这个基本容器的基础上实现了其他诸如XML读取的附加功能。对于这些功能的实现原理,看一看XmlBeanFactory的代码实现就能很容易地理解。在如下的代码中可以看到,在XmlBeanFactory构造方法中需要得到Resource对象。对XmlBeanDefinitionReader对象的初始化,以及使用这个这个对象来完成loadBeanDefinitions的调用,就是这个调用启动了从Resource中载入BeanDefinitions的过程,这个loadBeanDefinitions同时也是IoC容器初始化的重要组成部分。 

简单来说,IoC容器的初始化包括BeanDefinition的Resouce定位、载入和注册这三个基本的过程。我觉得重点是在载入和对BeanDefinition做解析的这个过程。可以从DefaultListableBeanFactory来入手看看IoC容器是怎样完成BeanDefinition载入的。在refresh调用完成以后,可以看到loadDefinition的调用: 

Java代码  收藏代码
  1. public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {  
  2.     public AbstractXmlApplicationContext() {  
  3.     }  
  4.     public AbstractXmlApplicationContext(ApplicationContext parent) {  
  5.         super(parent);  
  6.     }  
  7.     //这里是实现loadBeanDefinitions的地方  
  8.     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {  
  9.         // Create a new XmlBeanDefinitionReader for the given BeanFactory.  
  10.         // 创建 XmlBeanDefinitionReader,并通过回调设置到 BeanFactory中去,创建BeanFactory的使用的也是 DefaultListableBeanFactory。  
  11.         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
  12.   
  13.         // Configure the bean definition reader with this context's  
  14.         // resource loading environment.  
  15.         // 这里设置 XmlBeanDefinitionReader, 为XmlBeanDefinitionReader 配置ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用  
  16.         beanDefinitionReader.setResourceLoader(this);  
  17.         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
  18.   
  19.         // Allow a subclass to provide custom initialization of the reader,  
  20.         // then proceed with actually loading the bean definitions.  
  21.     // 这是启动Bean定义信息载入的过程  
  22.         initBeanDefinitionReader(beanDefinitionReader);  
  23.         loadBeanDefinitions(beanDefinitionReader);  
  24.     }  
  25.   
  26.     protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {  
  27.     }  


这里使用 XmlBeanDefinitionReader来载入BeanDefinition到容器中,如以下代码清单所示: 

Java代码  收藏代码
  1.     //这里是调用的入口。  
  2.     public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {  
  3.         return loadBeanDefinitions(new EncodedResource(resource));  
  4.     }  
  5.     //这里是载入XML形式的BeanDefinition的地方。  
  6.     public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  
  7.         Assert.notNull(encodedResource, "EncodedResource must not be null");  
  8.         if (logger.isInfoEnabled()) {  
  9.             logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
  10.         }  
  11.   
  12.         Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();  
  13.         if (currentResources == null) {  
  14.             currentResources = new HashSet<EncodedResource>(4);  
  15.             this.resourcesCurrentlyBeingLoaded.set(currentResources);  
  16.         }  
  17.         if (!currentResources.add(encodedResource)) {  
  18.             throw new BeanDefinitionStoreException(  
  19.                     "Detected recursive loading of " + encodedResource + " - check your import definitions!");  
  20.         }  
  21.         //这里得到XML文件,并得到IO的InputSource准备进行读取。  
  22.         try {  
  23.             InputStream inputStream = encodedResource.getResource().getInputStream();  
  24.             try {  
  25.                 InputSource inputSource = new InputSource(inputStream);  
  26.                 if (encodedResource.getEncoding() != null) {  
  27.                     inputSource.setEncoding(encodedResource.getEncoding());  
  28.                 }  
  29.                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());  
  30.             }  
  31.             finally {  
  32.                 inputStream.close();  
  33.             }  
  34.         }  
  35.         catch (IOException ex) {  
  36.             throw new BeanDefinitionStoreException(  
  37.                     "IOException parsing XML document from " + encodedResource.getResource(), ex);  
  38.         }  
  39.         finally {  
  40.             currentResources.remove(encodedResource);  
  41.             if (currentResources.isEmpty()) {  
  42.                 this.resourcesCurrentlyBeingLoaded.set(null);  
  43.             }  
  44.         }  
  45.     }  
  46. //具体的读取过程可以在doLoadBeanDefinitions方法中找到:  
  47.     //这是从特定的XML文件中实际载入BeanDefinition的地方  
  48.     protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)  
  49.             throws BeanDefinitionStoreException {  
  50.         try {  
  51.             int validationMode = getValidationModeForResource(resource);  
  52.             //这里取得XML文件的Document对象,这个解析过程是由 documentLoader完成的,这个documentLoader是DefaultDocumentLoader,在定义documentLoader的地方创建  
  53.             Document doc = this.documentLoader.loadDocument(  
  54.                     inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());  
  55.             //这里启动的是对BeanDefinition解析的详细过程,这个解析会使用到Spring的Bean配置规则,是我们下面需要详细关注的地方。  
  56.             return registerBeanDefinitions(doc, resource);  
  57.         }  
  58.         catch (BeanDefinitionStoreException ex) {  
  59.             throw ex;  
  60.         }  
  61.         catch (SAXParseException ex) {  
  62.             throw new XmlBeanDefinitionStoreException(resource.getDescription(),  
  63.                     "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);  
  64.         }  
  65.         catch (SAXException ex) {  
  66.             throw new XmlBeanDefinitionStoreException(resource.getDescription(),  
  67.                     "XML document from " + resource + " is invalid", ex);  
  68.         }  
  69.         catch (ParserConfigurationException ex) {  
  70.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  71.                     "Parser configuration exception parsing XML from " + resource, ex);  
  72.         }  
  73.         catch (IOException ex) {  
  74.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  75.                     "IOException parsing XML document from " + resource, ex);  
  76.         }  
  77.         catch (Throwable ex) {  
  78.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  79.                     "Unexpected exception parsing XML document from " + resource, ex);  
  80.         }  
  81.     }  
protected EntityResolver getEntityResolver() {
		if (this.entityResolver == null) {
			// Determine default EntityResolver to use.
			ResourceLoader resourceLoader = getResourceLoader();
			if (resourceLoader != null) {
				this.entityResolver = new ResourceEntityResolver(resourceLoader);
			}
			else {
				this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
			}
		}
		return this.entityResolver;
	}

 

public DelegatingEntityResolver(ClassLoader classLoader) {
		this.dtdResolver = new BeansDtdResolver();
		this.schemaResolver = new PluggableSchemaResolver(classLoader);
	}

 

public PluggableSchemaResolver(ClassLoader classLoader) {
		this.classLoader = classLoader;
		this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION; // "META-INF/spring.schemas"
	}

 
关于具体的Spring BeanDefinition的解析,是在BeanDefinitionParserDelegate中完成的。这个类里包含了各种Spring Bean定义规则的处理,感兴趣的同学可以仔细研究。我们举一个例子来分析这个处理过程,比如我们最熟悉的对Bean元素的处理是怎样完成的,也就是我们在XML定义文件中出现的<bean></bean>这个最常见的元素信息是怎样被处理的。在这里,我们会看到那些熟悉的BeanDefinition定义的处理,比如id、name、aliase等属性元素。把这些元素的值从XML文件相应的元素的属性中读取出来以后,会被设置到生成的BeanDefinitionHolder中去。这些属性的解析还是比较简单的。对于其他元素配置的解析,比如各种Bean的属性配置,通过一个较为复杂的解析过程,这个过程是由parseBeanDefinitionElement来完成的。解析完成以后,会把解析结果放到BeanDefinition对象中并设置到BeanDefinitionHolder中去,如以下清单所示: 
#DefaultBeanDefinitionDocumentReader

/**
	 * Parse the elements at the root level in the document:
	 * "import", "alias", "bean".
	 * @param root the DOM root element of the document
	 */
	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)) {
                  // 解析import, alias, beans等默认标签
						parseDefaultElement(ele, delegate);
					}
					else {
                  // 解析aop, mvc, dubbo等自定义标签,这部分在文末解释
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}
/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		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));
		}
	}

  #BeanDefinitionReaderUtils

 

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		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);
			}
		}
	}
 上面的registry正是DefaultListableBeanFactory!

 

#BeanDefinitionParserDelegate

Java代码  收藏代码
  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {  
  2.         //这里取得在<bean>元素中定义的id、name和aliase属性的值  
  3.         String id = ele.getAttribute(ID_ATTRIBUTE);  
  4.         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);  
  5.   
  6.         List<String> aliases = new ArrayList<String>();  
  7.         if (StringUtils.hasLength(nameAttr)) {  
  8.             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);  
  9.             aliases.addAll(Arrays.asList(nameArr));  
  10.         }  
  11.   
  12.         String beanName = id;  
  13.         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {  
  14.             beanName = aliases.remove(0);  
  15.             if (logger.isDebugEnabled()) {  
  16.                 logger.debug("No XML 'id' specified - using '" + beanName +  
  17.                         "' as bean name and " + aliases + " as aliases");  
  18.             }  
  19.         }  
  20.   
  21.         if (containingBean == null) {  
  22.             checkNameUniqueness(beanName, aliases, ele);  
  23.         }  
  24.   
  25.         //这个方法会引发对bean元素的详细解析  
  26. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);  
  27.         if (beanDefinition != null) {  
  28.             if (!StringUtils.hasText(beanName)) {  
  29.                 try {  
  30.                     if (containingBean != null) {  
  31.                         beanName = BeanDefinitionReaderUtils.generateBeanName(  
  32.                                 beanDefinition, this.readerContext.getRegistry(), true);  
  33.                     }  
  34.                     else {  
  35.                         beanName = this.readerContext.generateBeanName(beanDefinition);  
  36.                         // Register an alias for the plain bean class name, if still possible,  
  37.                         // if the generator returned the class name plus a suffix.  
  38.                         // This is expected for Spring 1.2/2.0 backwards compatibility.  
  39.                         String beanClassName = beanDefinition.getBeanClassName();  
  40.                         if (beanClassName != null &&  
  41.                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&  
  42.                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {  
  43.                             aliases.add(beanClassName);  
  44.                         }  
  45.                     }  
  46.                     if (logger.isDebugEnabled()) {  
  47.                         logger.debug("Neither XML 'id' nor 'name' specified - " +  
  48.                                 "using generated bean name [" + beanName + "]");  
  49.                     }  
  50.                 }  
  51.                 catch (Exception ex) {  
  52.                     error(ex.getMessage(), ele);  
  53.                     return null;  
  54.                 }  
  55.             }  
  56.             String[] aliasesArray = StringUtils.toStringArray(aliases);  
  57.             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);  
  58.         }  
  59.   
  60.         return null;  
  61.     }  


在具体生成BeanDefinition以后。我们举一个对property进行解析的例子来完成对整个BeanDefinition载入过程的分析,还是在类BeanDefinitionParserDelegate的代码中,它对BeanDefinition中的定义一层一层地进行解析,比如从属性元素集合到具体的每一个属性元素,然后才是对具体的属性值的处理。根据解析结果,对这些属性值的处理会封装成PropertyValue对象并设置到BeanDefinition对象中去,如以下代码清单所示。 

Java代码  收藏代码
  1. /** 
  2.  * 这里对指定bean元素的property子元素集合进行解析。 
  3.  */  
  4. public void parsePropertyElements(Element beanEle, BeanDefinition bd) {  
  5.     //遍历所有bean元素下定义的property元素  
  6.     NodeList nl = beanEle.getChildNodes();  
  7.     for (int i = 0; i < nl.getLength(); i++) {  
  8.         Node node = nl.item(i);  
  9.         if (node instanceof Element && DomUtils.nodeNameEquals(node, PROPERTY_ELEMENT)) {  
  10.             //在判断是property元素后对该property元素进行解析的过程  
  11.             parsePropertyElement((Element) node, bd);  
  12.         }  
  13.     }  
  14. }  
  15. public void parsePropertyElement(Element ele, BeanDefinition bd) {  
  16.     //这里取得property的名字  
  17.     String propertyName = ele.getAttribute(NAME_ATTRIBUTE);  
  18.     if (!StringUtils.hasLength(propertyName)) {  
  19.         error("Tag 'property' must have a 'name' attribute", ele);  
  20.         return;  
  21.     }  
  22.     this.parseState.push(new PropertyEntry(propertyName));  
  23.     try {  
  24.         //如果同一个bean中已经有同名的存在,则不进行解析,直接返回。也就是说,如果在同一个bean中有同名的property设置,那么起作用的只是第一个。  
  25.         if (bd.getPropertyValues().contains(propertyName)) {  
  26.             error("Multiple 'property' definitions for property '" + propertyName + "'", ele);  
  27.             return;  
  28.         }  
  29.         //这里是解析property值的地方,返回的对象对应对Bean定义的property属性设置的解析结果,这个解析结果会封装到PropertyValue对象中,然后设置到BeanDefinitionHolder中去。  
  30.         Object val = parsePropertyValue(ele, bd, propertyName);  
  31.         PropertyValue pv = new PropertyValue(propertyName, val);  
  32.         parseMetaElements(ele, pv);  
  33.         pv.setSource(extractSource(ele));  
  34.         bd.getPropertyValues().addPropertyValue(pv);  
  35.     }  
  36.     finally {  
  37.         this.parseState.pop();  
  38.     }  
  39. }  
  40. /** 
  41.  * 这里取得property元素的值,也许是一个list或其他。 
  42.  */  
  43. public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {  
  44.     String elementName = (propertyName != null) ?  
  45.                     "<property> element for property '" + propertyName + "'" :  
  46.                     "<constructor-arg> element";  
  47.   
  48.     // Should only have one child element: ref, value, list, etc.  
  49.     NodeList nl = ele.getChildNodes();  
  50.     Element subElement = null;  
  51.     for (int i = 0; i < nl.getLength(); i++) {  
  52.         Node node = nl.item(i);  
  53.         if (node instanceof Element && !DomUtils.nodeNameEquals(node, DESCRIPTION_ELEMENT) &&  
  54.                 !DomUtils.nodeNameEquals(node, META_ELEMENT)) {  
  55.             // Child element is what we're looking for.  
  56.             if (subElement != null) {  
  57.                 error(elementName + " must not contain more than one sub-element", ele);  
  58.             }  
  59.             else {  
  60.                 subElement = (Element) node;  
  61.             }  
  62.         }  
  63.     }  
  64.     //这里判断property的属性,是ref还是value,不允许同时是ref和value。  
  65.     boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);  
  66.     boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);  
  67.     if ((hasRefAttribute && hasValueAttribute) ||  
  68.             ((hasRefAttribute || hasValueAttribute) && subElement != null)) {  
  69.         error(elementName +  
  70.                 " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);  
  71.     }  
  72.     //如果是ref,创建一个ref的数据对象RuntimeBeanReference,这个对象封装了ref的信息。  
  73.     if (hasRefAttribute) {  
  74.         String refName = ele.getAttribute(REF_ATTRIBUTE);  
  75.         if (!StringUtils.hasText(refName)) {  
  76.             error(elementName + " contains empty 'ref' attribute", ele);  
  77.         }  
  78.         RuntimeBeanReference ref = new RuntimeBeanReference(refName);  
  79.         ref.setSource(extractSource(ele));  
  80.         return ref;  
  81.     } //如果是value,创建一个value的数据对象TypedStringValue ,这个对象封装了value的信息。  
  82.     else if (hasValueAttribute) {  
  83.         TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));  
  84.         valueHolder.setSource(extractSource(ele));  
  85.         return valueHolder;  
  86.     } //如果还有子元素,触发对子元素的解析  
  87.     else if (subElement != null) {  
  88.         return parsePropertySubElement(subElement, bd);  
  89.     }  
  90.     else {  
  91.         // Neither child element nor "ref" or "value" attribute found.  
  92.         error(elementName + " must specify a ref or value", ele);  
  93.         return null;  
  94.     }  
  95. }  


比如,再往下看,我们看到像List这样的属性配置是怎样被解析的,依然在BeanDefinitionParserDelegate中:返回的是一个List对象,这个List是Spring定义的ManagedList,作为封装List这类配置定义的数据封装,如以下代码清单所示。 

Java代码  收藏代码
  1. public List parseListElement(Element collectionEle, BeanDefinition bd) {  
  2.     String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);  
  3.     NodeList nl = collectionEle.getChildNodes();  
  4.     ManagedList<Object> target = new ManagedList<Object>(nl.getLength());  
  5.     target.setSource(extractSource(collectionEle));  
  6.     target.setElementTypeName(defaultElementType);  
  7.     target.setMergeEnabled(parseMergeAttribute(collectionEle));  
  8.     //具体的List元素的解析过程。  
  9.     parseCollectionElements(nl, target, bd, defaultElementType);  
  10.     return target;  
  11. }  
  12. protected void parseCollectionElements(  
  13.         NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {  
  14.     //遍历所有的元素节点,并判断其类型是否为Element。  
  15.     for (int i = 0; i < elementNodes.getLength(); i++) {  
  16.         Node node = elementNodes.item(i);  
  17.         if (node instanceof Element && !DomUtils.nodeNameEquals(node, DESCRIPTION_ELEMENT)) {  
  18.     //加入到target中去,target是一个ManagedList,同时触发对下一层子元素的解析过程,这是一个递归的调用。  
  19.             target.add(parsePropertySubElement((Element) node, bd, defaultElementType));  
  20.         }  
  21.     }  
  22. }  


经过这样一层一层的解析,我们在XML文件中定义的BeanDefinition就被整个给载入到了IoC容器中,并在容器中建立了数据映射。在IoC容器中建立了对应的数据结构,或者说可以看成是POJO对象在IoC容器中的映像,这些数据结构可以以AbstractBeanDefinition为入口,让IoC容器执行索引、查询和操作。 

在我的感觉中,对核心数据结构的定义和处理应该可以看成是一个软件的核心部分了。所以,这里的BeanDefinition的载入可以说是IoC容器的核心,如果说IoC容器是Spring的核心,那么这些BeanDefinition就是Spring的核心的核心了! 

呵呵,这部分代码数量不小,但如果掌握这条主线,其他都可以举一反三吧,就像我们掌握了操作系统启动的过程,以及在操作系统设计中的核心数据结构像进程数据结构,文件系统数据结构,网络协议数据结构的设计和处理一样,对整个系统的设计原理,包括移植,驱动开发和应用开发,是非常有帮助的!

 

让我们回到牛逼的delegate:

 

public BeanDefinition parseCustomElement(Element ele) {
		return parseCustomElement(ele, null);
	}

	public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

 

public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {

	/**
	 * The location to look for the mapping files. Can be present in multiple JAR files.
	 */
	public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

        public DefaultNamespaceHandlerResolver() {
		this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
	}

/**
	 * Locate the {@link NamespaceHandler} for the supplied namespace URI
	 * from the configured mappings.
	 * @param namespaceUri the relevant namespace URI
	 * @return the located {@link NamespaceHandler}, or {@code null} if none found
	 */
	public NamespaceHandler resolve(String namespaceUri) {
		Map<String, Object> handlerMappings = getHandlerMappings();
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;
			try {
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
				}
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
				namespaceHandler.init();
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			catch (ClassNotFoundException ex) {
				throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "] not found", ex);
			}
			catch (LinkageError err) {
				throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "]: problem with handler class file or dependent class", err);
			}
		}
	}
}

 我们自定义标签时,需要做的是在META-INF下建两个文件spring.handlers, spring.schemas,然后实现我们自己的NamespaceHandler和BeanDefinitionParser.

 

public class AopNamespaceHandler extends NamespaceHandlerSupport {

	/**
	 * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
	 * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
	 * and '{@code scoped-proxy}' tags.
	 */
	public void init() {
		// In 2.0 XSD as well as in 2.1 XSD.
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}

}

 #AspectJAutoProxyBeanDefinitionParser

public BeanDefinition parse(Element element, ParserContext parserContext) {
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		extendBeanDefinition(element, parserContext);
		return null;
	}

# AopNamespaceUtils

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) {

		BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
		registerComponentIfNecessary(beanDefinition, parserContext);
	}

 #AopConfigUtils

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}

private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if (currentPriority < requiredPriority) {
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			return null;
		}
		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                // 看这里!
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}

 另外,基于注解的配置,

<context:component-scan base-package="com.itlong.whatsmars.spring"/>

 处理过程参见ComponentScanBeanDefinitionParser,

public BeanDefinition parse(Element element, ParserContext parserContext) {
		String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

		// Actually scan for bean definitions and register them.
		ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
		Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

		return null;
	}

 

分享到:
评论
1 楼 IXHONG 2016-05-09  
互相注入有没有问题得看采用的何种注入方式

相关推荐

    springIoc实现原理

    **Spring Ioc 实现原理详解** Spring Ioc(Inversion of Control,控制反转)是Spring框架的核心特性之一,它改变了传统应用程序中对象的创建和管理方式。在传统的软件设计中,对象的创建和依赖关系的维护通常由...

    spring ioc和aop原理流程图(详细)

    5. **Bean的生命周期**:Spring容器对Bean的生命周期进行管理,包括初始化、正常使用、销毁等阶段,开发者可以自定义生命周期回调方法以进行特定操作。 ### Spring AOP **面向切面编程(AOP)** 是Spring的另一个...

    Spring IOC容器实现分析.pdf 下载

    本文将深入剖析Spring的IOC容器,理解其工作原理和重要功能,以帮助开发者更好地利用这一强大的工具。 一、IOC容器的概念 IOC,即控制反转,是面向对象编程中的一个设计原则,它的主要思想是将对象的创建和管理权...

    Spring IoC容器实现的结构分析

    总之,Spring IoC容器的实现是一个复杂而精巧的设计,它通过一系列组件协同工作,实现了对象的创建、装配和管理,为开发者提供了强大的依赖注入功能。无论是简单的应用还是复杂的分布式系统,Spring IoC容器都能有效...

    spring Ioc容器配置

    spring Ioc容器配置 IOC容器数据源配置 &lt;!-- 配置数据源 --&gt; &lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"&gt; &lt;value&gt;org.gjt.mm.mysql.Driver ...

    Spring IOC容器实现分析

    本文将基于Spring 2.0版本的源码,深入分析Spring IOC容器的内部实现机制,帮助读者更好地理解和使用这一强大的工具。 #### 二、核心概念与术语 在深入探讨Spring IOC容器之前,我们首先需要了解几个核心的概念: ...

    Spring实现原理及IoC容器的优点

    本篇文章将深入探讨Spring实现原理,阐述IoC容器的优点,并指导如何在Eclipse中创建一个Spring的Web应用。 首先,让我们理解Spring的核心——IoC容器。IoC是一种设计模式,它改变了对象获取依赖的方式。在传统的...

    Spring的IoC容器初始化源码解析

    Spring的IoC容器通过一系列精心设计的接口来实现其强大功能。下面详细介绍一些关键接口: ##### 4.1 BeanFactory系列接口 - **BeanFactory**:基础的容器接口,提供了最基本的bean管理功能。 - **...

    spring ioc容器部署实现

    ### Spring IoC容器部署实现详解 #### 一、Spring IoC容器概述 Spring框架的核心特性之一就是Inversion of Control(IoC),也被称为Dependency Injection(DI)。IoC容器是Spring框架的重要组成部分,它负责管理...

    手写一个SpringIoc容器

    本项目"手写一个SpringIoc容器"旨在模仿Spring的IOC(Inversion of Control,控制反转)功能,帮助开发者深入理解Spring的工作原理,提升对依赖注入(Dependency Injection)模式的认识。 在实现自定义的Spring IOC...

    模拟Spring的IoC容器实现注解自动装配

    在模拟实现时,我们可以创建一个`BeanDefinition`类来存储bean的信息,如类名、属性、依赖等。 接下来,我们要实现注解处理。Spring中的@Autowired注解可以用于字段、方法或参数级别,表示需要自动装配的依赖。在...

    Spring IOC实现原理demo

    在本文中,我们将深入探讨Spring IOC的实现原理,并通过一个简单的示例来展示其工作流程。 首先,我们需要理解什么是控制反转。在传统的编程模式中,程序员手动创建并管理对象,而Spring IOC则将这种控制权反转,由...

    SpringIOC原理实现

    6. **自定义实现Spring IOC**:虽然Spring提供了强大的IOC容器,但理解其工作原理有助于我们自定义实现。我们可以创建一个类来加载Bean定义,解析XML配置,然后使用反射创建和初始化对象,最后根据依赖关系图进行...

    Sping翻转控制器

    #### 一、Spring IoC 容器概述 ##### 1.1 控制反转(Inversion of Control, IoC)原理 - **定义**:控制反转是一种设计模式,用于降低代码之间的耦合度。在Spring框架中,IoC容器负责创建对象,注入依赖,配置和...

    Spring框架系列(7) - Spring IOC实现原理详解之IOC初始化流程.doc

    Spring 框架系列(7)- Spring IOC 实现原理详解之 IOC 初始化流程 本文将详细解释 Spring 框架中的 IOC(Inversion of Control,控制反转)实现原理之 IOC 初始化流程。IOC 是一种软件设计模式,用于将软件系统中...

    Spring中IoC优点与缺点解析

    IoC(Inversion of Control)是 Spring 框架中的一种设计模式,它的主要思想是将对象的创建和管理交给容器,从而解耦合对象之间的依赖关系。今天,我们将详细解析 IoC 的优点和缺点。 优点 1. 简化对象的创建:IoC ...

    详解Spring IOC 容器启动流程分析

    refresh 方法实现了 IOC 容器的启动逻辑,包括准备容器、加载 BeanDefinition、实例化单例 Bean、注册事件监听器等步骤。 首先,refresh 方法会首先准备容器,包括准备 BeanFactory、加载 BeanDefinition 等步骤。...

Global site tag (gtag.js) - Google Analytics