`

spring4.0源码分析━━━(解析xml)

阅读更多

       最近比较闲,所以想研究下开源的框架,之前研究过基于NIO写的一个分布式框架,但是一直没有空写出心得体会。很快又忘记了,所以想把最近研究的spring开源框架写出来。其实每研究或者说读一个框架,都会受益良多。对自己的技术有很大的提升。

       今天先写spring的IOC。IOC的概念控制反转,我的体会是spring通过xml的配置,把一些属性的实例化本来是由我们自己程序做的事情交给了spring的IOC容器。不过这是最简单的,spring还帮我们做了其他很多的工作。不过我认为IOC最核心的工作也就是这个了。

       开始我读spring的源代码是根据下载的spring技术内幕pdf资料。由于我下载的是spring3.0的,而这个pdf是2.0的。然后又懵懵懂懂的读下去,遇到不懂的就看源代码和调试。这样下来几天了还没有什么进展。后面我想了一下,觉得不应该这样研究。应该是找到目标,然后再有目的性的研究。这样下来,效率高多了。虽然我以前研究也都是根据问题然后有目的性的研究,但是这些都没有很好的规范。所以执行起来还是比较乱。

       今天先从以下两个角度细分

  • 怎么读取xml配置文件的
  • 怎么设置其属性的

      读取配置文件的入口是XmlBeanDefinitionReader这个类。其中有一个这样的方法

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			int validationMode = getValidationModeForResource(resource);
			Document doc = this.documentLoader.loadDocument(
					inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
			return registerBeanDefinitions(doc, resource);
		}

其中documentLoader是其属性,并有初始化。再到loadDocument方法代码如下:

 * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
	 * XML parser.
	 */
	public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
		if (logger.isDebugEnabled()) {
			logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
		}
		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
		return builder.parse(inputSource);
	}

	/**
	 * Create the {@link DocumentBuilderFactory} instance.
	 * @param validationMode the type of validation: {@link XmlValidationModeDetector#VALIDATION_DTD DTD}
	 * or {@link XmlValidationModeDetector#VALIDATION_XSD XSD})
	 * @param namespaceAware whether the returned factory is to provide support for XML namespaces
	 * @return the JAXP DocumentBuilderFactory
	 * @throws ParserConfigurationException if we failed to build a proper DocumentBuilderFactory
	 */
	protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
			throws ParserConfigurationException {

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(namespaceAware);

		if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
			factory.setValidating(true);

			if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
				// Enforce namespace aware for XSD...
				factory.setNamespaceAware(true);
				try {
					factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
				}
				catch (IllegalArgumentException ex) {
					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(ex);
					throw pcex;
				}
			}
		}

		return factory;
	}

	/**
	 * Create a JAXP DocumentBuilder that this bean definition reader
	 * will use for parsing XML documents. Can be overridden in subclasses,
	 * adding further initialization of the builder.
	 * @param factory the JAXP DocumentBuilderFactory that the DocumentBuilder
	 * should be created with
	 * @param entityResolver the SAX EntityResolver to use
	 * @param errorHandler the SAX ErrorHandler to use
	 * @return the JAXP DocumentBuilder
	 * @throws ParserConfigurationException if thrown by JAXP methods
	 */
	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;
	}

 

从中可以看出spring解析xml并没有用到像dom4j和jdom这样的框架,而是直接用java的API方式。其中创建DocumentBuilderFactory的时候设置validating为true,就是说读取的时候会验证xml的配置正确性。其中又是根据xsd的模式。这里可能有很多其他的同学会遇到过这样的问题:有时候spring的项目运行很好,但是断网的情况下却出错了。其实就是spring的验证在作怪。当然默认情况下spring肯定是不会上网去验证了,肯定是放xsd放在其中的一个地方。那么放在什么地方呢?docBuilder.setEntityResolver(entityResolver);就在这个地方。如下entitiResolver是是返回一个InputSource类。

InputSource source = super.resolveEntity(publicId, systemId);
		if (source == null && systemId != null) {
			String resourcePath = null;
			try {
				String decodedSystemId = URLDecoder.decode(systemId);
				String givenUrl = new URL(decodedSystemId).toString();
				String systemRootUrl = new File("").toURL().toString();
				// Try relative to resource base if currently in system root.
				if (givenUrl.startsWith(systemRootUrl)) {
					resourcePath = givenUrl.substring(systemRootUrl.length());
				}
			}

 很明显看得到如果souce为空的时候就会通过URL读取xsd文件,其中的systemId为http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

http://www.springframework.org/schema/tool/spring-tool-3.0.xsd

等。所以没有网,那肯定访问不了就会出错了。

其中spring的默认实现是到PluggableSchemaResolver类的

private Map<String, String> getSchemaMappings() {
		if (this.schemaMappings == null) {
			synchronized (this) {
				if (this.schemaMappings == null) {
					if (logger.isDebugEnabled()) {
						logger.debug("Loading schema mappings from [" + this.schemaMappingsLocation + "]");
					}
					try {
						Properties mappings =
								PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader);
						if (logger.isDebugEnabled()) {
							logger.debug("Loaded schema mappings: " + mappings);
						}
						Map<String, String> schemaMappings = new ConcurrentHashMap<String, String>();
						CollectionUtils.mergePropertiesIntoMap(mappings, schemaMappings);
						this.schemaMappings = schemaMappings;
					}
					catch (IOException ex) {
						throw new IllegalStateException(
								"Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex);
					}
				}
			}
		}
		return this.schemaMappings;
	}

 初始化了这些systemId对应的xsd位置。其中的DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas"。这是静态的不可变的类成员变量。所以spring会读取classpath路径下所有jar下META-INF/spring.schemas。而其中org.springframework.beans-3.0.5.RELEASE.jar中spring.schemas内容如下:

http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd

 

所以现在就可以理解到为什么会出现那个错误了。可能是你classpath路径下没有这些文件,所以到网上去找,结果就出错了。其中还有一个地方也是和这个类似,那就是aop,spring对每个xml的命名空间会有不同的类来解析。而读到其中的文件为spring.handlers。

 以上为xml的验证分析。

 

      验证通过,就是怎么读取xml然后的问题了。而这里会最终到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)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

 其中可以看到如果解析xml中Element的命名空间是默认的和其他的是不同的处理方式。而默认即为http://www.springframework.org/schema/beans,也就是标签为<bean>的Element。如果是<aop>,<tx>等则就会是不同的处理,这里的也就是上面提到的在META-INF/spring.handlers读配置文件信息了。如是<aop>的话则是一下的内容http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

如果是像aop,tx(事务)这些标签的话会到这个方法,在BeanDefinitionParserDelegate类中,这其中的handler就是上面配置文件的AopNamespaceHandler了。

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));
	}

 

      这里把重点先放到bean的读取上。aop的等到下次讲解aop才详细讲解。还记得书上好像有说程序就是算法+数据结构。

     以前我一直会把算法放在最前面。这就有点像面向过程一样,有时候会比较快,面对较小的程序。但是如果程序复杂起来的话,先看数据结构反而会更加的容易。所以我先暂时研究一下<bean>,这个对应的类的数据结构,等下看代码起来也就更加的容易了。<bean>都会对应这个类AbstractBeanDefinition。而这个类会在DefaultListableBeanFactory类中有一个private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();  线程安全的Map,然后通过BeanFactory类getBean的时候实际都是访问到这个map,拿到BeanDefinition初始化并且设置好属性。所以理解BeanDefinition数据结构是至关重要的。其数据结构如下:

/**
	 * Constant for the default scope name: "", equivalent to singleton status
	 * but to be overridden from a parent bean definition (if applicable).
	 */
	public static final String SCOPE_DEFAULT = "";


	/**
	 * Constant that indicates no autowiring at all.
	 * @see #setAutowireMode
	 */
	public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;

	/**
	 * Constant that indicates autowiring bean properties by name.
	 * @see #setAutowireMode
	 */
	public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;

	/**
	 * Constant that indicates autowiring bean properties by type.
	 * @see #setAutowireMode
	 */
	public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;

	/**
	 * Constant that indicates autowiring a constructor.
	 * @see #setAutowireMode
	 */
	public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;

	/**
	 * Constant that indicates determining an appropriate autowire strategy
	 * through introspection of the bean class.
	 * @see #setAutowireMode
	 * @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
	 * use annotation-based autowiring for clearer demarcation of autowiring needs.
	 */
	@Deprecated
	public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;


	/**
	 * Constant that indicates no dependency check at all.
	 * @see #setDependencyCheck
	 */
	public static final int DEPENDENCY_CHECK_NONE = 0;

	/**
	 * Constant that indicates dependency checking for object references.
	 * @see #setDependencyCheck
	 */
	public static final int DEPENDENCY_CHECK_OBJECTS = 1;

	/**
	 * Constant that indicates dependency checking for "simple" properties.
	 * @see #setDependencyCheck
	 * @see org.springframework.beans.BeanUtils#isSimpleProperty
	 */
	public static final int DEPENDENCY_CHECK_SIMPLE = 2;

	/**
	 * Constant that indicates dependency checking for all properties
	 * (object references as well as "simple" properties).
	 * @see #setDependencyCheck
	 */
	public static final int DEPENDENCY_CHECK_ALL = 3;


	private volatile Object beanClass;

	private String scope = SCOPE_DEFAULT;

	private boolean singleton = true;

	private boolean prototype = false;

	private boolean abstractFlag = false;

	private boolean lazyInit = false;

	private int autowireMode = AUTOWIRE_NO;

	private int dependencyCheck = DEPENDENCY_CHECK_NONE;

	private String[] dependsOn;

	private boolean autowireCandidate = true;

	private boolean primary = false;

	private final Map<String, AutowireCandidateQualifier> qualifiers =
			new LinkedHashMap<String, AutowireCandidateQualifier>(0);

	private boolean nonPublicAccessAllowed = true;

	private boolean lenientConstructorResolution = true;

	private ConstructorArgumentValues constructorArgumentValues;

	private MutablePropertyValues propertyValues;

	private MethodOverrides methodOverrides = new MethodOverrides();

	private String factoryBeanName;

	private String factoryMethodName;

	private String initMethodName;

	private String destroyMethodName;

	private boolean enforceInitMethod = true;

	private boolean enforceDestroyMethod = true;

	private boolean synthetic = false;

	private int role = BeanDefinition.ROLE_APPLICATION;

	private String description;

	private Resource resource;

 

看到这个其实发现不就是<bean>配置文件中的属性么,lazyInit,autowireMode等这些,完全对。其中最重要的又是标红色的部分了。它即为其中<property>标签的数据结构了。MutablePropertyValues类中会有一个这样的成员变量,private final List<PropertyValue> propertyValueList;而PropertyValue的数据结构如下:

private final String name;

	private final Object value;

	private Object source;

	private boolean optional = false;

	private boolean converted = false;

	private Object convertedValue;

	/** Package-visible field that indicates whether conversion is necessary */
	volatile Boolean conversionNecessary;

	/** Package-visible field for caching the resolved property path tokens */
	volatile Object resolvedTokens;

	/** Package-visible field for caching the resolved PropertyDescriptor */
	volatile PropertyDescriptor resolvedDescriptor;

 这就很像property的类型了。其中name就是name了,Object即为value或者ref了。至于为什么不直接用一个map保存bean里面的属性呢?官方的解释是用PropertyValue更多的灵活性,并能以一种优化的方式处理索引的属性等。可能是多了这里的PropertyValue多了几个属性吧。

       了解到xml的验证读取和<bean>标签对应的数据结构后,还没有说怎么读xml,怎么生成这些BeanDefinition的呢。这里就省略前面的分析,生成BeanDefinition是在BeanDefinitionParserDelegate这个类,其实spring的类命名还是很好的,看到就知道这个是BeanDefinition解析的代表,那肯定是跟BeanDefinition有着莫大的关系了。其实每个生成的BeanDefinition就都是在这个类产生的。在这个方法中

public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {

		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);
			}
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);

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

 

其中看到会根据class的属性和parent的属性创建一个AbstractBeanDefinition类。实际是创建的GenericBeanDefinition,而GenericBeanDefinition类是继承了AbstractBeanDefinition的。其中又调用了自己本身的parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);方法,这个方法介绍实例化这些bean的属性的,像重要的scope,abstract,lazy-init,autowire,depends-on,init-method,factory-method等。如果没有配置就会是默认的方式了。再重要的就是解析值了。
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 (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
				parsePropertyElement((Element) node, bd);
			}
		}
	}
 解析值的时候会调用上面的这个方法。看到会循环找到标签为property的标签。然后解析
 
/**
	 * Parse a property element.
	 */
	public void parsePropertyElement(Element ele, BeanDefinition bd) {
		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 {
			if (bd.getPropertyValues().contains(propertyName)) {
				error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
				return;
			}
			Object val = parsePropertyValue(ele, bd, propertyName);
			PropertyValue pv = new PropertyValue(propertyName, val);
			parseMetaElements(ele, pv);
			pv.setSource(extractSource(ele));
			bd.getPropertyValues().addPropertyValue(pv);
		}
		finally {
			this.parseState.pop();
		}
	}
 其中看到如果已经有了一个属性,再发现有就会直接返回,所以如果一个bean配置了两个name为p1的话,那起作用的只是第一个。其中Object val = parsePropertyValue(ele, bd, propertyName);又会调用到本类的很多方法,如果是property标签中直接有value那就最简单的方式,否则可能会有子标签,list,set,map这些。读者可自行看BeanDefinitionParseDelege类中的相应方法了。上面还可以看到得到value后创建了PropertyValue类。然后通过AbstractBeanDefinition类getPropertyValues().addPropertyValue(pv)到这里也就全部解析完了。
public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
		if (!isDefaultNamespace(ele)) {
			return parseNestedCustomElement(ele, bd);
		}
		else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
			BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
			if (nestedBd != null) {
				nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
			}
			return nestedBd;
		}
		else if (nodeNameEquals(ele, REF_ELEMENT)) {
			// A generic reference to any name of any bean.
			String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
			boolean toParent = false;
			if (!StringUtils.hasLength(refName)) {
				// A reference to the id of another bean in the same XML file.
				refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
				if (!StringUtils.hasLength(refName)) {
					// A reference to the id of another bean in a parent context.
					refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
					toParent = true;
					if (!StringUtils.hasLength(refName)) {
						error("'bean', 'local' or 'parent' is required for <ref> element", ele);
						return null;
					}
				}
			}
			if (!StringUtils.hasText(refName)) {
				error("<ref> element contains empty target attribute", ele);
				return null;
			}
			RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
			ref.setSource(extractSource(ele));
			return ref;
		}
		else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
			return parseIdRefElement(ele);
		}
		else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
			return parseValueElement(ele, defaultValueType);
		}
		else if (nodeNameEquals(ele, NULL_ELEMENT)) {
			// It's a distinguished null value. Let's wrap it in a TypedStringValue
			// object in order to preserve the source location.
			TypedStringValue nullHolder = new TypedStringValue(null);
			nullHolder.setSource(extractSource(ele));
			return nullHolder;
		}
		else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
			return parseArrayElement(ele, bd);
		}
		else if (nodeNameEquals(ele, LIST_ELEMENT)) {
			return parseListElement(ele, bd);
		}
		else if (nodeNameEquals(ele, SET_ELEMENT)) {
			return parseSetElement(ele, bd);
		}
		else if (nodeNameEquals(ele, MAP_ELEMENT)) {
			return parseMapElement(ele, bd);
		}
		else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
			return parsePropsElement(ele);
		}
		else {
			error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
			return null;
		}
	}
到这里BeanDefinition就通过解析xml生成了,那是怎么放到DefaultListableBeanFactory类中的呢。前面也说过这些BeanDefinition最终是会放在这个类中一个map对象中。
 /** Map of bean definition objects, keyed by bean name */
 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
 
这个方法是至关重要的,是怎么通过XmlBeanDefinitionReader解析xml,最终到BeanDefinitionParserDelegate去解析bean标签的呢?其中的方式为
XmlBeanDefinitionReader---DefaultBeanDefinitionDocumentReader--BeanDefinitionParserDelegate。
/**
	 * 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));
		}
	}
 
其中在DefaultBeanDefinitionDocumentReader解析bean标签时会调用上面的方法,而通过BeanDefinitionParserDelegate解析生成的是BeanDefinitionHolder,它实际上有三个属性的。
private final BeanDefinition beanDefinition;

	private final String beanName;

	private final String[] aliases;
 
 
而在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类,他有实现BeanDefinitionRegistry做这个接口。这个接口的内容为下。而registry是通过XmlReaderContext这个类得到的。而XmlReaderContext从名称上解释是读xml的上下文。是起到一个承上启下的作用,它包含了一个XmlBeanDefinitionReader类和NamespaceHandlerResolver类。而这个NamespaceHandlerResolver就是如果标签不是默认名称空间的时候就派上用场了。
public interface BeanDefinitionRegistry extends AliasRegistry {

	/**
	 * Register a new bean definition with this registry.
	 * Must support RootBeanDefinition and ChildBeanDefinition.
	 * @param beanName the name of the bean instance to register
	 * @param beanDefinition definition of the bean instance to register
	 * @throws BeanDefinitionStoreException if the BeanDefinition is invalid
	 * or if there is already a BeanDefinition for the specified bean name
	 * (and we are not allowed to override it)
	 * @see RootBeanDefinition
	 * @see ChildBeanDefinition
	 */
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;

	/**
	 * Remove the BeanDefinition for the given name.
	 * @param beanName the name of the bean instance to register
	 * @throws NoSuchBeanDefinitionException if there is no such bean definition
	 */
	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	/**
	 * Return the BeanDefinition for the given bean name.
	 * @param beanName name of the bean to find a definition for
	 * @return the BeanDefinition for the given name (never <code>null</code>)
	 * @throws NoSuchBeanDefinitionException if there is no such bean definition
	 */
	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	/**
	 * Check if this registry contains a bean definition with the given name.
	 * @param beanName the name of the bean to look for
	 * @return if this registry contains a bean definition with the given name
	 */
	boolean containsBeanDefinition(String beanName);

	/**
	 * Return the names of all beans defined in this registry.
	 * @return the names of all beans defined in this registry,
	 * or an empty array if none defined
	 */
	String[] getBeanDefinitionNames();

	/**
	 * Return the number of beans defined in the registry.
	 * @return the number of beans defined in the registry
	 */
	int getBeanDefinitionCount();

	/**
	 * Determine whether the given bean name is already in use within this registry,
	 * i.e. whether there is a local bean or alias registered under this name.
	 * @param beanName the name to check
	 * @return whether the given bean name is already in use
	 */
	boolean isBeanNameInUse(String beanName);

}
 
 
         写了这么多,可能有点乱。但是自己还是明白了。可能看官觉得比较乱。这里用一个其他的方式。其实可以用这种方式使用spring,照样不会报错。这样使用可能理解起来就更加的好了。发现创建XmlBeanDefinitionReader 的时候会带了DefaultListableBeanFactory 进去,所以这个reader就一直是随身携带的IOC容器去解析xml,这样也更加的容易解析一个bean就向容器放一个bean。读者可通过下面这个一步步看spring可能就简单明了多了。
@org.junit.Test
	public void testWebXmlFactory(){
		IBankSecurityDao dao ;
		Resource resource = new ClassPathResource("beanFactory.xml");
		DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
		reader.loadBeanDefinitions(resource);
		dao =(BankSecurityDaoImpl) factory.getBean("Dao");
		dao.add("a");
	}
 
分享到:
评论
1 楼 wabiaozia 2018-02-04  
厉害厉害 

相关推荐

Global site tag (gtag.js) - Google Analytics