Spring source code learning record--loading beans

 

public static void main( String[] args )
    {
    	ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    }

    This is the simplest code for spring loading bean configuration. The general process of spring loading bean is as follows


 The main role of the loadBeanDefinitions method

    1) Read the configuration file

    2) Prevent the configuration file from importing itself from causing infinite loop loading

 

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}
		
		//Get the resource loaded in the current thread
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		//If the resource is already loaded, throw an exception (only when importing your own configuration file will cause an exception)
		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());
				}
				/**
				 * When doLoadBeanDefinitions parses the configuration file, it will recursively call the loadBeanDefinitions method when it encounters the import tag
				 * At this time, the currently loaded configuration file will not be cleared
				 */
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			//The currently loaded resource file will be cleared from resourcesCurrentlyBeingLoaded every time it is loaded,
			// This way you can reload the configuration file later
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
				System.out.println(this.resourcesCurrentlyBeingLoaded.get());
			}
		}
	}
 

 

  doLoadBeanDefinitions:

  Parse the configuration file to get the Document object

 

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			Document doc = doLoadDocument(inputSource, resource);
			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);
		}
	}
 

 

 registerBeanDefinitions

 

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//Use DefaultBeanDefinitionDocumentReader to instantiate BeanDefinitionDocumentReader
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//Record the number of beans that have been loaded
		int countBefore = getRegistry().getBeanDefinitionCount();
		//Load and register beans
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//return the number of beans loaded this time
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
 

 

 

The real method of loading and registering beans is in DefaultBeanDefinitionDocumentReader

 

Parsing of default tags

 

 

	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		// Parse import tags
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(it);
		}
		// Parse the alias tag
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		// Parse the bean tag
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		//parse the beans tag
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(he);
		}
	}
 

 

Bean tag parsing


 
(1) First, entrust the parseBeanDefinitionElement method of the BeanDefinitionDelegate class to parse the element, and return

An instance of type BeanDefinitionHolder bdHolder. After this method, the bdHolder instance already contains various attributes configured in the configuration file, such as class, name, id, alias and other attributes

 (2) When the returned bdHolder is not empty, if there is a custom attribute under the child node of the default label, the custom label needs to be parsed

 (3) After the parsing is completed, the parsed bdHolder needs to be registered. Similarly, the registration operation is delegated to the registerBeanDefinition method of BeanDefinitionReaderUtils

 (4) Finally, a response event is issued to notify the relevant monitor that the bean has been loaded.

 

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		//parse each element
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement (ele);
		if (bdHolder != null) {
			//如果子节点存在自定义标签,需要再次解析
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// 注册bean.
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// 发出响应事件.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

 

 

/**
	 * containingBean参数,这个参数用来区分当前解析的bean是否是内部bean,
	 *	如果是非空值说明是内部bean,内部bean和非内部bean解析时只做了两件不同的事情:
	 *	1、id生成策略;2、内部bean需要继承其外部bean的scope。.
	 */
	@Nullable
	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		List<String> aliases = new ArrayList<>();
		if (StringUtils.hasLength(nameAttr)) {
			//将name以分号或者逗号进行分割
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			//分割后的name作为别名
			aliases.addAll(Arrays.asList(nameArr));
		}

		String beanName = id;
		/**
		 * 如果没有设置id,且别名不为空,则用第一个别名作为beanName
		 */
		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) {
			//beanName,aliases唯一性检查,并且将beanName,aliases加入到当前已使用名称集合中usedNames
			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;
	}

 

相关的类

XmlBeanDefinitionReader
DefaultBeanDefinitionDocumentReader
BeanDefinitionParserDelegate
BeanDefinitionReaderUtils

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326414743&siteId=291194637