• Spring源码情操陶冶-PropertyPlaceholderBeanDefinitionParser注解配置解析器


    本文针对spring配置的context:property-placeholder作下简单的分析,承接前文Spring源码情操陶冶-自定义节点的解析

    spring配置文件应用

    <context:property-placeholder location="classpath:context.properties">
    

    PropertyPlaceholderBeanDefinitionParser-继承关系

    配置文件解析类的继承关系如下

    * AbstractBeanDefinitionParser
    	* AbstractSingleBeanDefinitionParser
    		* AbstractPropertyLoadingBeanDefinitionParser
    			* PropertyPlaceholderBeanDefinitionParser
    

    所以我们将从父类开始慢慢剖析,方便理解的全面

    AbstractBeanDefinitionParser-配置文件解析抽象类

    直接查看主方法parse()

    	public final BeanDefinition parse(Element element, ParserContext parserContext) {
    		//模板方法,供子类调用,包装成AbstractBeanDefinition对象
    		AbstractBeanDefinition definition = parseInternal(element, parserContext);
    		if (definition != null && !parserContext.isNested()) {
    			try {
    				//生成唯一id
    				String id = resolveId(element, definition, parserContext);
    				if (!StringUtils.hasText(id)) {
    					parserContext.getReaderContext().error(
    							"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
    									+ "' when used as a top-level tag", element);
    				}
    				String[] aliases = new String[0];
    				//解析context:property-holder的name属性,并支持,分隔
    				String name = element.getAttribute(NAME_ATTRIBUTE);
    				if (StringUtils.hasLength(name)) {
    					aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
    				}
    				//包装为BeanDefinitionHolder对象
    				BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
    				//注册到bean工厂中
    				registerBeanDefinition(holder, parserContext.getRegistry());
    				//执行事件
    				if (shouldFireEvents()) {
    					BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
    					//目前为空
    					postProcessComponentDefinition(componentDefinition);
    
                                            //保存至containingComponents栈中								
                                            parserContext.registerComponent(componentDefinition);
    				}
    			}
    			catch (BeanDefinitionStoreException ex) {
    				parserContext.getReaderContext().error(ex.getMessage(), element);
    				return null;
    			}
    		}
    		return definition;
    	}
    

    主要的工作由子类AbstractSingleBeanDefinitionParser#parseInternal()方法来处理

    AbstractSingleBeanDefinitionParser#parseInternal-初始解析操作

    此方法主要是初始化一些准备工作,并仍采用模板方法doParse()方法供子类去实现解析操作

    protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
    		//类似于StringBuilder帮助创建beanDefinition
    		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
    		//为null
    		String parentName = getParentName(element);
    		if (parentName != null) {
    			builder.getRawBeanDefinition().setParentName(parentName);
    		}
    		//子类可复写此方法,譬如PropertyOverrideBeanDefinitionParser/PropertyPlaceholderBeanDefinitionParser
    		Class<?> beanClass = getBeanClass(element);
    		if (beanClass != null) {
    			//设置beanClass
    			builder.getRawBeanDefinition().setBeanClass(beanClass);
    		}
    		else {
    			String beanClassName = getBeanClassName(element);
    			if (beanClassName != null) {
    				builder.getRawBeanDefinition().setBeanClassName(beanClassName);
    			}
    		}
    		builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
    		if (parserContext.isNested()) {
    			// Inner bean definition must receive same scope as containing bean.
    			builder.setScope(parserContext.getContainingBeanDefinition().getScope());
    		}
    		//是否需要随spring上下文设置lazy-init属性
    		if (parserContext.isDefaultLazyInit()) {
    			// Default-lazy-init applies to custom bean definitions as well.
    			builder.setLazyInit(true);
    		}
    		//供子类调用实现解析,并传入BeanDefinitionBuilder对象
    		doParse(element, parserContext, builder);
    		return builder.getBeanDefinition();
    	}
    

    AbstractPropertyLoadingBeanDefinitionParser#doParse-基础解析

    查看了下官方注释,对此类的解释是解析context:property-....节点的抽象类,此处可指PropertyOverrideBeanDefinitionParser/PropertyPlaceholderBeanDefinitionParser,可对此方法进行扩展,代码如下

    	@Override
    	protected void doParse(Element element, BeanDefinitionBuilder builder) {
    		//获取location属性,支持,分隔,表明指定读取配置文件的路径
    		String location = element.getAttribute("location");
    		if (StringUtils.hasLength(location)) {
    			String[] locations = StringUtils.commaDelimitedListToStringArray(location);
    			builder.addPropertyValue("locations", locations);
    		}
    		//获取properties-ref属性,设置properties文件的引用名
    		String propertiesRef = element.getAttribute("properties-ref");
    		if (StringUtils.hasLength(propertiesRef)) {
    			builder.addPropertyReference("properties", propertiesRef);
    		}
    		//获取file-encoding属性,文件解析字符编码集
    		String fileEncoding = element.getAttribute("file-encoding");
    		if (StringUtils.hasLength(fileEncoding)) {
    			builder.addPropertyValue("fileEncoding", fileEncoding);
    		}
    		//获取order属性,用于排序
    		String order = element.getAttribute("order");
    		if (StringUtils.hasLength(order)) {
    			builder.addPropertyValue("order", Integer.valueOf(order));
    		}
    		//获取ignore-resource-not-found属性,默认为false,表明找不到资源是否往外抛异常
    		builder.addPropertyValue("ignoreResourceNotFound",
    				Boolean.valueOf(element.getAttribute("ignore-resource-not-found")));
    		//获取local-override属性,默认为false,true表明先使用文件中的属性,找不到再使用环境变量中的属性
    		builder.addPropertyValue("localOverride",
    				Boolean.valueOf(element.getAttribute("local-override")));
    
    		builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    	}
    

    PropertyPlaceholderBeanDefinitionParser-抽象方法的扩展

    复写的抽象方法有两处,分别为getBeanClass()doParse()方法

    • getBeanClass(Element element)
      指定解析文件的beanClass,beanClass均是BeanFactoryPostProcessor接口的实现类,代码如下
    protected Class<?> getBeanClass(Element element) {
    		//获取system-properties-mode属性是否为ENVIRONMENT,是则采用PropertySourcesPlaceholderConfigurer.class
    		if (element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIB).equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) {
    			return PropertySourcesPlaceholderConfigurer.class;
    		}
    
    		//默认为PropertyPlaceholderConfigurer.class作为beanClass
    		return PropertyPlaceholderConfigurer.class;
    	}
    
    • doParse(Element element, BeanDefinitionBuilder builder)
      在父类的基础上再加几个属性判断
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
    		super.doParse(element, builder);
    		//获取ignore-unresolvable属性,默认为false,true代表解析不了属性不抛异常
    		builder.addPropertyValue("ignoreUnresolvablePlaceholders",
    				Boolean.valueOf(element.getAttribute("ignore-unresolvable")));
    		//获取system-properties-mode属性
    		String systemPropertiesModeName = element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIB);
    		if (StringUtils.hasLength(systemPropertiesModeName) &&
    				!systemPropertiesModeName.equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) {
    			builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_"+systemPropertiesModeName);
    		}
    	}
    

    小结

    1. PropertyPlaceholderBeanDefinitionParser的作用是初始化配置文件的解析策略,方便spring在invokeBeanFactoryPostProcessors()中解析${}属性使用,具体可看>>>Spring源码情操陶冶-AbstractApplicationContext#invokeBeanFactoryPostProcessors

    2. 真正的解析${}这样格式的类是PropertyPlaceholderConfigurer.class或者是PropertySourcesPlaceholderConfigurer.class,前者适合spring 3.0之前,后者适合spring 3.1之后;
      前者是默认的实现,后者如果指定local-override为true,则优先找寻本地属性(文件属性),找不到的情况下再找寻环境属性(Environment)

    3. context:property-placeholder的属性

    • location-文件资源路径,支持classpath路径,且可指定多个,以,分隔
    • file-encoding-文件读取编码
    • properties-ref-属性配置文件应用名
    • order-排序属性
    • ignore-resource-not-found-默认为false,表明找不到资源是否往外抛异常
    • local-override-默认为false,表示优先查找文件(Properties)的属性,不存在则再找寻本地的属性(System),反之则是先本地后文件
    • ignore-unresolvable-默认为false,true代表解析不了属性不抛异常
  • 相关阅读:
    HNOI 越狱
    中国剩余定理详解
    洛谷P1133 教主的花园
    poj2728 Desert King
    bzoj1503 郁闷的出纳员
    Code Forces 698A Vacations
    Loj 10211 sumdiv
    noip2010 乌龟棋
    洛谷P4141消失之物
    Code Forces 543A Writing Code
  • 原文地址:https://www.cnblogs.com/question-sky/p/6994024.html
Copyright © 2020-2023  润新知