首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 开源软件 >

Spring源码分析之配置文件解析(2)

2013-03-14 
Spring源码分析之配置文件解析(二)??}??SetEncodedResource currentResources this.resourcesCurrentl

Spring源码分析之配置文件解析(二)
??}

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

这个方法里最核心的操作是doLoadBeanDefinitions(inputSource, encodedResource.getResource())
/**
? * Actually load bean definitions from the specified XML file.
? * @param inputSource the SAX InputSource to read from
? * @param resource the resource descriptor for the XML file
? * @return the number of bean definitions found
? * @throws BeanDefinitionStoreException in case of loading or parsing errors
? */
?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);
??}
??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);
??}
?}

DocumentLoader类主要负责把一个XML文档解析成一个Document对象,注册beanDefinitions的工作交给registerBeanDefinitions方法了.

/**
? * Register the bean definitions contained in the given DOM document.
? * Called by <code>loadBeanDefinitions</code>.
? * <p>Creates a new instance of the parser class and invokes
? * <code>registerBeanDefinitions</code> on it.
? * @param doc the DOM document
? * @param resource the resource descriptor (for context information)
? * @return the number of bean definitions found
? * @throws BeanDefinitionStoreException in case of parsing errors
? * @see #loadBeanDefinitions
? * @see #setDocumentReaderClass
? * @see BeanDefinitionDocumentReader#registerBeanDefinitions
? */
?public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
??// Read document based on new BeanDefinitionDocumentReader SPI.
??BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
??int countBefore = getRegistry().getBeanDefinitionCount();
??documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
??return getRegistry().getBeanDefinitionCount() - countBefore;
?}

实际上XmlBeanDefinitionReader把解析注册的工作又转交给BeanDefinitionDocumentReader来完成了。

这里getRegistry()实际上返回的是上一节介绍的生成的DefaultListableBeanFactory,这个工厂实际上是在生成XmlBeanDefinitionReader对象时通过构造方法入参传入的。

这里的createReaderContext(Resource resource)应该特别注意一下,这个上下文中包含了很多重要的信息,以提供给BeanDefinitionDocumentReader来使用。

/**
? * Create the {@link XmlReaderContext} to pass over to the document reader.
? */
?protected XmlReaderContext createReaderContext(Resource resource) {
??if (this.namespaceHandlerResolver == null) {
???this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
??}
??return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
????this.sourceExtractor, this, this.namespaceHandlerResolver);
?}

二:我们来看看DefaultBeanDefinitionDocumentReader是如何来registerBeanDefinitions的.

/**
? * Parses bean definitions according to the "spring-beans" DTD.
? * <p>Opens a DOM Document; then initializes the default settings
? * specified at <code>&lt;beans&gt;</code> level; then parses
? * the contained bean definitions.
? */
?public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
??this.readerContext = readerContext;

??logger.debug("Loading bean definitions");
??Element root = doc.getDocumentElement();

??BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);

??preProcessXml(root);
??parseBeanDefinitions(root, delegate);
??postProcessXml(root);
?}

我们继续来看一下核心操作parseDefaultElement(ele,delegate)方法。
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);
??}
?}
Xml配置文件中的bean的解析注册就在processBeanDefinition中了。
/**
? * 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));
??}
?}
Spring把每一个Bean结点解析之后,生成底层的数据结构BeanDefinition,然后存储在BeanDefinitionHold中,很明显具体的真正的解析工作放在parseBeanDefinitionElement方法中,解析完成后则调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())把beanDefinition注册到beanFactory中。下面,我们来看一下核心方法parseBeanDefinitionElement的源码.
三:我们来看看BeanDefinitionParserDelegate的parseBeanDefinitionElement(Element ele)是如何来解析的.
? public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
??return parseBeanDefinitionElement(ele, null);
?}

?public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
??String id = ele.getAttribute(ID_ATTRIBUTE);
??String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

??List<String> aliases = new ArrayList<String>();
??if (StringUtils.hasLength(nameAttr)) {
???String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
???aliases.addAll(Arrays.asList(nameArr));
??}

??String beanName = id;
??if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
???beanName = aliases.remove(0);
???if (logger.isDebugEnabled()) {
????logger.debug("No XML 'id' specified - using '" + beanName +
??????"' as bean name and " + aliases + " as aliases");
???}
??}

??if (containingBean == null) {
???checkNameUniqueness(beanName, aliases, ele);
??}

??AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
??if (beanDefinition != null) {
???if (!StringUtils.hasText(beanName)) {
????try {
?????if (containingBean != null) {
??????beanName = BeanDefinitionReaderUtils.generateBeanName(
????????beanDefinition, this.readerContext.getRegistry(), true);
?????}
?????else {
??????beanName = this.readerContext.generateBeanName(beanDefinition);
??????// Register an alias for the plain bean class name, if still possible,
??????// if the generator returned the class name plus a suffix.
??????// This is expected for Spring 1.2/2.0 backwards compatibility.
??????String beanClassName = beanDefinition.getBeanClassName();
??????if (beanClassName != null &&
????????beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
????????!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
???????aliases.add(beanClassName);
??????}
?????}
?????if (logger.isDebugEnabled()) {
??????logger.debug("Neither XML 'id' nor 'name' specified - " +
????????"using generated bean name [" + beanName + "]");
?????}
????}
????catch (Exception ex) {
?????error(ex.getMessage(), ele);
?????return null;
????}
???}
???String[] aliasesArray = StringUtils.toStringArray(aliases);
???return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
??}

??return null;
?}
在这里我们看到对id,name等属性的解析,最核心的操作为??? AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
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;
?}
在这段代码中,你可清楚的看到对bean组件相关属性和子标记的解析和处理(根据方法的命名).
四:在这里,可以重点关注一下ParseState
public final class ParseState {

?/**
? * Tab character used when rendering the tree-style representation.
? */
?private static final char TAB = '\t';

?/**
? * Internal {@link Stack} storage.
? */
?private final Stack state;


?/**
? * Create a new <code>ParseState</code> with an empty {@link Stack}.
? */
?public ParseState() {
??this.state = new Stack();
?}

?/**
? * Create a new <code>ParseState</code> whose {@link Stack} is a {@link Object#clone clone}
? * of that of the passed in <code>ParseState</code>.
? */
?private ParseState(ParseState other) {
??this.state = (Stack) other.state.clone();
?}


?/**
? * Add a new {@link Entry} to the {@link Stack}.
? */
?public void push(Entry entry) {
??this.state.push(entry);
?}

?/**
? * Remove an {@link Entry} from the {@link Stack}.
? */
?public void pop() {
??this.state.pop();
?}

?/**
? * Return the {@link Entry} currently at the top of the {@link Stack} or
? * <code>null</code> if the {@link Stack} is empty.
? */
?public Entry peek() {
??return (Entry) (this.state.empty() ? null : this.state.peek());
?}

?/**
? * Create a new instance of {@link ParseState} which is an independent snapshot
? * of this instance.
? */
?public ParseState snapshot() {
??return new ParseState(this);
?}


?/**
? * Returns a tree-style representation of the current <code>ParseState</code>.
? */
?@Override
?public String toString() {
??StringBuilder sb = new StringBuilder();
??for (int x = 0; x < this.state.size(); x++) {
???if (x > 0) {
????sb.append('\n');
????for (int y = 0; y < x; y++) {
?????sb.append(TAB);
????}
????sb.append("-> ");
???}
???sb.append(this.state.get(x));
??}
??return sb.toString();
?}


?/**
? * Marker interface for entries into the {@link ParseState}.
? */
?public interface Entry {

?}

}
这个类用了比较巧妙的方式来跟踪解析过程中的相关步骤,用户详见源码和API。
Simple Stack-based structure for tracking the logical position during a parsing process. entries are added to the stack at each point during the parse phase in a reader-specific manner.
Calling toString() will render a tree-style view of the current logical position in the parse phase. This representation is intended for use in error messages.

热点排行