SpringIOC源码解析(三)-解析xml文件中的bean

SpringIOC源码解析(三)-解析xml文件中的bean

概要

  • BeanDefinitionReader
  • XmlBeanDefinitionReader
  • Spring对xml文件解析

BeanDefinitionReader

什么是BeanDefinitionReader?

用来读取BeanDefinition读取器

* Simple interface for bean definition readers.
* Specifies load methods with Resource and String location parameters.

主要方法

public interface BeanDefinitionReader {
    //返回Bean工厂以向其注册Bean定义。
    BeanDefinitionRegistry getRegistry();

    /**返回资源加载器以用于资源位置。可以检查ResourcePatternResolver接口并进行相应的转换,以针对给定的      资源模式加载多个资源。
    一个null返回值表明,绝对资源加载不适用于这个bean定义阅读器。

    这主要用于从bean定义资源中导入其他资源,例如,通过XML bean定义中的“ import”标记。但是,建议相对      于定义资源应用此类导入;只有明确的完整资源位置才会触发绝对资源加载。
    **/
    @Nullable
    ResourceLoader getResourceLoader();

    //返回用于Bean类的类加载器。
    @Nullable
    ClassLoader getBeanClassLoader();

    //返回BeanNameGenerator用于匿名Bean(未指定显式Bean名称)。
    BeanNameGenerator getBeanNameGenerator();

    //从指定的资源加载bean定义。
    int loadBeanDefinitions(Resource var1) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(Resource... var1) throws BeanDefinitionStoreException;

    //从指定的资源位置加载bean定义。
    //该位置也可以是位置模式,前提是此bean定义读取器的ResourceLoader是ResourcePatternResolver。
    int loadBeanDefinitions(String var1) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(String... var1) throws BeanDefinitionStoreException;
}

XmlBeanDefinitionReader

image-20220421122503686

XmlBeanDefinitionReader是用于对xml文件进行解析,形成BeanDefinition信息。

* Bean definition reader for XML bean definitions.
* Delegates the actual XML document reading to an implementation

Spring对xml文件解析

调用解析位置

AbstractApplicationContext#refresh#obtainFreshBeanFactory

	public void refresh() throws BeansException, IllegalStateException {
			...
			* 1、创建BeanFactory对象
			* 2、xml解析
			* 	传统标签解析:bean、import* 	自定义标签解析 如:<context:component-scan base-package="com.xiangxue.jack"/>
			* 	自定义标签解析流程:
			* 		a、根据当前解析标签的头信息找到对应的namespaceUri
			* 		b、加载spring所有jar中的spring.handlers文件。并建立映射关系
			* 		c、根据namespaceUri从映射关系中找到对应的实现了NamespaceHandler接口的类
			* 		d、调用类的init方法,init方法是注册了各种自定义标签的解析类
			* 		e、根据namespaceUri找到对应的解析类,然后调用paser方法完成标签解析
			*
			* 3、把解析出来的xml标签封装成BeanDefinition对象
			* */
			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			...
	}

obtainFreshBeanFactory

	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		//核心方法
		refreshBeanFactory();
		return getBeanFactory();
	}

refreshBeanFactory

	protected final void refreshBeanFactory() throws BeansException {
		//如果BeanFactory不为空,则清除BeanFactory和里面的实例
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//BeanFactory 实例工厂
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			//设置是否可以循环依赖 allowCircularReferences
			//是否允许使用相同名称重新注册不同的bean实现.
			customizeBeanFactory(beanFactory);
			//解析xml,并把xml中的标签封装成BeanDefinition对象
			loadBeanDefinitions(beanFactory);
			this.beanFactory = beanFactory;
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
  • 这里我们重点关注loadBeanDefinitions方法

loadBeanDefinitions

	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		//创建xml的解析器,这里是一个委托模式
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		//这里传一个this进去,因为ApplicationContext是实现了ResourceLoader接口的
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
		//主要看这个方法  
		loadBeanDefinitions(beanDefinitionReader);
	}
  • 这里new了一个 XmlBeanDefinitionReader,用于委托对xml文件的处理。将beanFactory传入是为了持有BeanDefinitionRegistry对象,用于注册解析出的BeanDefinition
  • 我们重点loadBeanDefinitions的另一个重载方法

loadBeanDefinitions

	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}
		//获取需要加载的xml配置文件
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			reader.loadBeanDefinitions(configLocations);
		}
	}
  • getConfigLocations() 用于获取配置文件路径,此路径设置为new ClassPathXmlApplicationContext("classpath:applicationContext.xml");传入的路径。

  • reader.loadBeanDefinitions(configLocations)最终会调用到XmlBeanDefinitionReader#loadBeanDefinitions,下面我们将分析此loadBeanDefinitions

loadBeanDefinitions

	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		...
		//获取Resource对象中的xml文件流对象
		try (InputStream inputStream = encodedResource.getResource().getInputStream()) {

			//InputSource是jdk中的sax xml文件解析对象
			InputSource inputSource = new InputSource(inputStream);
			if (encodedResource.getEncoding() != null) {
				inputSource.setEncoding(encodedResource.getEncoding());
			}
			//主要看这个方法 
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
		}
		...
	}
  • 我们调用new ClassPathXmlApplicationContext()可以传入多个路径形成路径数组,该路径数组的每个元素会在调用该方法的方法中作为参数循环调用此方法
  • doLoadBeanDefinitions(inputSource, encodedResource.getResource()) 此处最主要方法

doLoadBeanDefinitions

	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
			//把inputSource 封装成Document文件对象,这是jdk的API
			Document doc = doLoadDocument(inputSource, resource);

			//主要看这个方法,根据解析出来的document对象,拿到里面的标签元素封装成BeanDefinition
			int count = registerBeanDefinitions(doc, resource);
			...
		...
	}
  • doLoadDocument将数据Document文件对象
  • registerBeanDefinitions 根据解析出来的document对象,拿到里面的标签元素封装成BeanDefinition

registerBeanDefinitions

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//又来一记委托模式,BeanDefinitionDocumentReader委托这个类进行document的解析
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();

		//主要看这个方法,createReaderContext(resource) XmlReaderContext上下文,封装了XmlBeanDefinitionReader对象
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
  • documentReader 此处为XmlBeanDefinitionReader 委托BeanDefinitionDocumentReader进行document的解析

  • createReaderContext(resource) 创建了XmlReaderContext 。该对象持有XmlBeanDefinitionReaderNamespaceHandlerResolver

  • documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

registerBeanDefinitions

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
   this.readerContext = readerContext;
   //主要看这个方法,把root节点传进去
   doRegisterBeanDefinitions(doc.getDocumentElement());
}
  • 设置readerContext

  • doRegisterBeanDefinitions(doc.getDocumentElement()) 把root节点传进去

doRegisterBeanDefinitions

	protected void doRegisterBeanDefinitions(Element root) {
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);
		...
		preProcessXml(root);
		//主要看这个方法,标签具体解析过程
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

		this.delegate = parent;
	}

  • createDelegate(getReaderContext(), root, parent)创建BeanDefinitionParserDelegate委托,用于解析具体的Bean内容。

  • BeanDefinitionDocumentReader委托给BeanDefinitionParserDelegate 进行解析Bean的具体内容。

  • parseBeanDefinitions(root, this.delegate)具体解析过程。

parseBeanDefinitions

	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);
		}
	}
  • 我们重点关注默认标签解析parseDefaultElement

parseDefaultElement

	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		//解析document,封装成BeanDefinition
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		...
        //完成document到BeanDefinition对象转换后,对BeanDefinition对象进行缓存注册
		// Register the final decorated instance.
		BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        ...
	}
  • BeanDefinitionDocumentReader调用委托对象的parseBeanDefinitionElement方法将xml元素委托给BeanDefinitionParserDelegate 进行具体的解析Bean内容
  • BeanDefinitionReaderUtils.registerBeanDefinition 注册解析出的BeanDefinition
parseBeanDefinitionElement
	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)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

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

		//检查beanName是否重复
		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}

		//<bean>标签解析的核心方法
		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.isTraceEnabled()) {
						logger.trace("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;
	}
  • 形如<bean id="car" class="cn.xie.xml.CarFactory" name="BWM,Benz" .../> 解析出的 beanNameid属性为主,如果id属性为空,则为name属性,如果name属性解析出多个(xml中name,隔开)以第一个为主。

  • parseBeanDefinitionElement 标签解析的核心方法

  • 解析完标签返回BeanDefinitionHolder持有beanDefinition,beanName,aliases

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

		this.parseState.push(new BeanEntry(beanName));

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

		try {
			//创建GenericBeanDefinition对象
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);

			//解析bean标签的属性,并把解析出来的属性设置到BeanDefinition对象中
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

			//解析bean中的meta标签
			parseMetaElements(ele, bd);
			//解析bean中的lookup-method标签 
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			//解析bean中的replaced-method标签 
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

			//解析bean中的constructor-arg标签 
			parseConstructorArgElements(ele, bd);
			//解析bean中的property标签 
			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;
	}
  • createBeanDefinition(className, parent) 创建GenericBeanDefinition对象

  • 将各个标签依次解出并设置到BeanDefinition中

  • 解析完标签返回BeanDefinition

registerBeanDefinition
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		//完成BeanDefinition的注册,重点看,重要程度 5
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		//建立别名和 id的映射,这样就可以根据别名获取到id
		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}
  • registerBeanDefinitionBeanDefinition注册进BeanDefinitionRegistry(具体由DefaultListableBeanFactory实现)。
  • registerAliasaliases注册进AliasRegistry(具体由SimpleAliasRegistry实现)。
registerBeanDefinition
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    
	/** Map of bean definition objects, keyed by bean name. */
    //beanDefinitionMap缓存
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    
	/** List of bean definition names, in registration order. */
    //beanDefinitionNames
	private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
    
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
			...
				//把beanDefinition缓存到map中
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				//把beanName放到beanDefinitionNames list中,这个list着重记住,bean实例化的时候需要用到
				this.beanDefinitionNames.add(beanName);
        	...

	}
}
registerAlias
public class SimpleAliasRegistry implements AliasRegistry {

	/** Map from alias to canonical name. */
	private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);

	@Override
	public void registerAlias(String name, String alias) {

		synchronized (this.aliasMap) {
				...
				this.aliasMap.put(alias, name);
				...
			}
		}
	}
}

The End

至此完结!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值