一步一步学习spring(四)——Ioc之Bean的注入详解

   首先,要明白,ApplicationContext扩展了ResourceLoader,它和XmlBeanFactory不一样,在容器启动的时候,不需要向ApplicationContext传入Resource,只需传入Spring的资源文件名,ApplicationContext会自动加载资源文件名对应的Resource。并且你可以同时配置多个资源文件一起传给ApplicationContext。
    
   在容器启动时,开始了容器中Bean的注入过程,具体详细注入过程,网上一大堆,为大家推荐个java eye中比较经典的IOC注入分析吧,网址是 :
  http://www.iteye.com/wiki/Spring-source/1226-Spring源代码解析(一):IOC容器
  
   在此,我主要给大家分析下在对资源文件中属性Bean的详细解析过程。
   spring中xml资源的解析,主要是由XmlBeanDefinitionReader完成,该类是在ApplicationContext容器启动时初始化的。解析的详细过程是BeanDefinitionParserDelegate代理完成的。
  
  BeanDefinitionParserDelegate中parseBeanDefinitionElement方法:
 
 
       /**
	 * Parse the bean definition itself, without regard to name or aliases. May return
	 * <code>null</code> if problems occured during the parse of the bean definition.
	 */
	public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {
       /*
        * parserState是一个栈,里面只能存放ParseState.Entry类型的元素,所以,要想将String类型的beanName存入栈中,\
        * 必须将beanName封装成Entry元素
        */
		this.parseState.push(new BeanEntry(beanName));

		String className = null;
		if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
		}

		try {
			String parent = null;
			if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
				parent = ele.getAttribute(PARENT_ATTRIBUTE);
			}
			/*
			 * 其实就是new一个BeanDefinition实例,并赋值此BeanDefinition的className以及parent属性值
			 */
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);

			/*
			 * 对"<id="" name="" scope="" ..>"配置形式进行解析,解析出该Bean的一些生命周期、
			 * 是否延迟加载、自动装配方式等属性值,并且赋值给上面生成的BeanDefinition实例
			 */
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			//解析Bean的"<description ../>"属性
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            
			parseMetaElements(ele, bd);
			//解析"<look-up ..>"形式的配置,得出的值存入的先前生成的Beandefinition的methoOverrides属性中
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			//解析"<replace ..>"形式的配置,得出的值存入的先前生成的Beandefinition的methoOverrides属性中
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            
			/*
			 * spring中的Ioc注入有三种方式:构造方法注入、set方法注入和接口注入,以下则为
			 * 几种注入方式的代码
			 */
			
			//构造方法注入处理
			parseConstructorArgElements(ele, bd);
			//set方法注入的处理(比较常用的注入)
			parsePropertyElements(ele, bd);
			parseQualifierElements(ele, bd);

			bd.setResource(this.readerContext.getResource());
			bd.setSource(extractSource(ele));

			return bd;
		}
		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;
	}


下面,将分析下set注入形式的解析处理,该处理是parseBeanDefinitionElement方法中完成的,具体如下:

/**
	 * Parse property sub-elements of the given bean element.
	 */
	public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
		NodeList nl = beanEle.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (node instanceof Element && DomUtils.nodeNameEquals(node, PROPERTY_ELEMENT)) {
				//解析主要方法
				parsePropertyElement((Element) node, bd);
			}
		}
	}

 
  parsePropertyElement(Element ele, BeanDefinition bd)方法代码如下:
 
   /**
	 * Parse a property element.
	 */
	public void parsePropertyElement(Element ele, BeanDefinition bd) {
		//解析<proiperty name=..>中name属性值
		String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
		if (!StringUtils.hasLength(propertyName)) {
			error("Tag 'property' must have a 'name' attribute", ele);
			return;
		}
		this.parseState.push(new PropertyEntry(propertyName));
		try {
			//如果该BeanDefinition中已经存在了一个与此name相同的属性值,则抛出异常
			if (bd.getPropertyValues().contains(propertyName)) {
				error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
				return;
			}
			//解析该propertyName对应的值,也许只是一个普通的string,也许是个List列表,也许是该BeanDefinition依赖的一个Bean(对应"ref="形式的配置)
			Object val = parsePropertyValue(ele, bd, propertyName);
			//封装成PropertyValue
			PropertyValue pv = new PropertyValue(propertyName, val);
			//解析原始元素
			parseMetaElements(ele, pv);
			pv.setSource(extractSource(ele));
			/*
			 * 将解析完成的PropertyValue存入该BeanDefinition的PropertyValues中,
			 * 有两个作用: 1.容器启动后,加载该BeanDefinition对应的Bean时,需要设置该Bean的属性值
			 *            2.避免一个BeanDefiniton有多个相同的propertyName
			 */
			bd.getPropertyValues().addPropertyValue(pv);
		}
		finally {
			this.parseState.pop();
		}
	}

   都说程序员学习三部曲:what how why. 其实,我们在知道使用、知道配置spring的时候,我们应该还有尽自己最大努力做到 why。知其然,也要知其所以然。
分析源码是痛苦的,但对于初级程序员来说,收获也是巨大的,我们不仅可以更深一层次的掌握java技术,而且可以学学高手的编码规范以及重构技术。

你可能感兴趣的:(spring,bean,IOC)