zoukankan      html  css  js  c++  java
  • Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

    前言-阅读源码有利于陶冶情操,本文承接前文Spring源码情操陶冶-AbstractApplicationContext#obtainFreshBeanFactory

    前文提到最关键的地方是解析bean xml配置文件,废话不多说,直接上代码清单

    //正如官方注释所说,解析import标签、alias标签、bean标签和自定义的标签
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    		//检查<beans>根标签的命名空间是否为空或者是http://www.springframework.org/schema/beans
    		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)) 
    						//解析import标签、alias标签、bean标签、内置<beans>标签
    						parseDefaultElement(ele, delegate);
    					}
    					else {
    						//解析自定义标签,例如<context:component-scan basePackage="" />
    						delegate.parseCustomElement(ele);
    					}
    				}
    			}
    		}
    		else {
    			delegate.parseCustomElement(root);
    		}
    	}
    

    那我们根据两个部分来解读对bean xml文件的解析,分别为DefaultBeanDefinitionDocumentReader#parseDefaultElementBeanDefinitionParserDelegate#parseCustomElement

    DefaultBeanDefinitionDocumentReader#parseDefaultElement

    解析import标签、alias标签、bean标签,代码清单如下

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    		//import标签解析
    		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
    			importBeanDefinitionResource(ele);
    		}
    		//alias标签解析
    		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
    			processAliasRegistration(ele);
    		}
    		//bean标签解析
    		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
    			processBeanDefinition(ele, delegate);
    		}
    		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
    			// recurse 递归
    			doRegisterBeanDefinitions(ele);
    		}
    	}
    

    三种解析具体方法
    1.DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource
    import标签解析代码比较长,逻辑倒是很简单,代码清单如下

    	protected void importBeanDefinitionResource(Element ele) {
    		//获取<import>标签的resource属性值
    		String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
    		//resource不允许为空
    		if (!StringUtils.hasText(location)) {
    			getReaderContext().error("Resource location must not be empty", ele);
    			return;
    		}
    
    		// Resolve system properties: e.g. "${user.dir}"
    		//这里强调下,对于<import>标签,其会从System.getProperties()和System.getenv()中属性获取,优先System.getProperties()
    		//即优先系统变量,环境变量次之
    		location = environment.resolveRequiredPlaceholders(location);
    
    		Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
    
    		// Discover whether the location is an absolute or relative URI
    		//resource属性支持URL模式,找到相应的资源并进行加载
    		boolean absoluteLocation = false;
    		try {
    			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
    		}
    		catch (URISyntaxException ex) {
    			// cannot convert to an URI, considering the location relative
    			// unless it is the well-known Spring prefix "classpath*:"
    		}
    
    		// Absolute or relative?
    		if (absoluteLocation) {
    			try {
    				int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
    				if (logger.isDebugEnabled()) {
    					logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
    				}
    			}
    			catch (BeanDefinitionStoreException ex) {
    				getReaderContext().error(
    						"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
    			}
    		}
    		else {
    			// No URL -> considering resource location as relative to the current file.
    			try {
    				int importCount;
    				//相对路径,相对于当前文件
    				Resource relativeResource = getReaderContext().getResource().createRelative(location);
    				if (relativeResource.exists()) {
    					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
    					actualResources.add(relativeResource);
    				}
    				else {
    					String baseLocation = getReaderContext().getResource().getURL().toString();
    					importCount = getReaderContext().getReader().loadBeanDefinitions(
    							StringUtils.applyRelativePath(baseLocation, location), actualResources);
    				}
    				if (logger.isDebugEnabled()) {
    					logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
    				}
    			}
    			catch (IOException ex) {
    				getReaderContext().error("Failed to resolve current resource location", ele, ex);
    			}
    			catch (BeanDefinitionStoreException ex) {
    				getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
    						ele, ex);
    			}
    		}
    		//可以忽略以下操作
    		Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
    		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
    	}
    

    2.DefaultBeanDefinitionDocumentReader#processAliasRegistration
    主要目的就是缓存全局别名

    	protected void processAliasRegistration(Element ele) {
    		//这里确保<alias>标签的name、alias值不为空 e.g. <alias name="key" alias="value">
    		String name = ele.getAttribute(NAME_ATTRIBUTE);
    		String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
    		boolean valid = true;
    		if (!StringUtils.hasText(name)) {
    			getReaderContext().error("Name must not be empty", ele);
    			valid = false;
    		}
    		if (!StringUtils.hasText(alias)) {
    			getReaderContext().error("Alias must not be empty", ele);
    			valid = false;
    		}
    		if (valid) {
    			try {
    				//全局设置,查阅后其会保存到DefaultListableFactory的父级类`SimpleAliasRegistry#aliasMap`中
    				getReaderContext().getRegistry().registerAlias(name, alias);
    			}
    			catch (Exception ex) {
    				getReaderContext().error("Failed to register alias '" + alias +
    						"' for bean with name '" + name + "'", ele, ex);
    			}
    			getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
    		}
    	}
    

    3.DefaultBeanDefinitionDocumentReader#processBeanDefinition
    <bean>标签的解析处理

    	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    		//初始化bean标签的相应内容
    		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    		if (bdHolder != null) {
    			//对bean标签中的属性和子标签,进行相应的具体解析,例如property、constructor-args
    			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));
    		}
    	}
    

    BeanDefinitionParserDelegate#parseBeanDefinitionElement

    解析bean标签,也许会返回null在出现解析错误的时候,代码清单如下

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
    		//获取<bean>的 id属性
    		String id = ele.getAttribute(ID_ATTRIBUTE);
    		//如果有name属性,则获取name,并且可有多个name,以,;为分隔符
    		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    
    		List<String> aliases = new ArrayList<String>();
    		if (StringUtils.hasLength(nameAttr)) {
    			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    			aliases.addAll(Arrays.asList(nameArr));
    		}
    
    		String beanName = id;
    		//当id属性不存在且name属性有值时,默认使用第一个name值
    		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");
    			}
    		}
    		//确保beanName的唯一性,即在应用使用前不允许有两个的beanName一致
    		if (containingBean == null) {
    			checkNameUniqueness(beanName, aliases, ele);
    		}
    		//解析除id、name属性的情况
    		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    		if (beanDefinition != null) {
    			//省略部分代码
    			...
    			...
    			String[] aliasesArray = StringUtils.toStringArray(aliases);
    			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    		}
    
    		return null;
    	}
    

    限于篇幅太长,其余的情况我们就不讲解了,有兴趣的可以自行前往,但在此根据读到的源码对bean解析进行总结

    1. parent属性代表父类,这里表示的是父类的唯一id名

    2. 不允许有singleton属性;可有scope属性;
      可有abstract属性,值可为true/false;
      可有lazyinit属性,值为default则沿用beans标签中的lazyinit,否则为true/false;
      可有autowire属性,值为default则沿用beans标签的autowirebyName表示按照名称注入、byType按照类型注入、constructor表明按照构造函数注入、autodetect表明自动检测(官方不建议使用)、默认则不使用注入;
      可有dependency-check属性,值有allobjectssimple,默认为不检测;
      可有depends-on属性,可有多值,以,;为分隔符;
      可有autowire-candidate属性,值可为true/false/default/空字符串
      可有primary属性,值为true/false;
      可有init-method属性,初始化方法,对应可有destroy-method属性;
      可有facroty-method属性,工厂方法;
      可有factory-bean属性

    3. bean标签下可有description标签,表示对此bean的描述;
      也可有meta标签;
      可有lookup-method标签;
      可有replaced-method标签;
      可有constructor-arg标签;
      可有property标签,其中valueref属性只可选用一个;
      可有qualifier标签,其下可有attribute标签,同@Qualifier注解

    4. 所有的bean信息都由BeanDefinitionHolder对象来保存,其中的BeanDefinition包含了一个bean标签的所有可能内容

    BeanDefinitionParserDelegate#parseCustomElement

    解析自定义的标签节点,例如<context:component-scan><tx:annotation-driven>等,简单看下代码清单

        //containingBd此处为null
        public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    		//获取命名空间
    		String namespaceUri = getNamespaceURI(ele);
    		//从map集合中获取NamespaceHandler接口
    		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));
    	}
    

    按照上述的注释,我们需要查看下namespaceURI对应的NamespaceHandler到底是如何获取的,真实调用的是DefaultNamespaceHandlerResolver类,稍微分析下此类


    构造函数

    	public DefaultNamespaceHandlerResolver() {
    		//第二个参数值为 META-INF/spring.handlers
    		this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
    	}
    	
    	//此处所用的是handlerMappingLocation,指的是handler接口类加载的文件资源
    	public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
    		Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
    		this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
    		this.handlerMappingsLocation = handlerMappingsLocation;
    	}
    

    resolve解析方法

    		public NamespaceHandler resolve(String namespaceUri) {
    		//调用的是PropertiesLoaderUtils.loadAllProperties读取所有classpath下的'META-INF/spring.handlers'文件,保存至Map集合
    		Map<String, Object> handlerMappings = getHandlerMappings();
    		Object handlerOrClassName = handlerMappings.get(namespaceUri);
    		if (handlerOrClassName == null) {
    			return null;
    		}
    		else if (handlerOrClassName instanceof NamespaceHandler) {
    			return (NamespaceHandler) handlerOrClassName;
    		}
    		else {
    			String className = (String) handlerOrClassName;
    			try {
    				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
    				//声明的handler必须是NamespaceHandler的实现类
    				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
    					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
    							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
    				}
    				//实例化Handler类
    				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
    				//初始化
    				namespaceHandler.init();
    				//缓存下
    				handlerMappings.put(namespaceUri, namespaceHandler);
    				return namespaceHandler;
    			}
    			catch (ClassNotFoundException ex) {
    				throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
    						namespaceUri + "] not found", ex);
    			}
    			catch (LinkageError err) {
    				throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
    						namespaceUri + "]: problem with handler class file or dependent class", err);
    			}
    		}
    	}
    

    限于篇幅太长,关于bean解析以及custom的具体解析未来会分条详细介绍,敬请期待

    下节预告

    Spring源码情操陶冶-AbstractApplicationContext#prepareBeanFactory

  • 相关阅读:
    ios开发之--把秒转换为天时分秒
    网络爬虫的类型
    网络爬虫的组成
    为什么要学网络爬虫
    什么是网络爬虫
    Windows 下安装 Python3
    Linux 下安装 Python3
    HTTP 代理
    HTTP Cookies
    爬虫的基本原理
  • 原文地址:https://www.cnblogs.com/question-sky/p/6706362.html
Copyright © 2011-2022 走看看