最近比较闲,所以想研究下开源的框架,之前研究过基于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"); }