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);
		}
	}
小结
PropertyPlaceholderBeanDefinitionParser的作用是初始化配置文件的解析策略,方便spring在
invokeBeanFactoryPostProcessors()中解析${}属性使用,具体可看>>>Spring源码情操陶冶-AbstractApplicationContext#invokeBeanFactoryPostProcessors
真正的解析
${}这样格式的类是PropertyPlaceholderConfigurer.class或者是PropertySourcesPlaceholderConfigurer.class,前者适合spring 3.0之前,后者适合spring 3.1之后;
前者是默认的实现,后者如果指定local-override为true,则优先找寻本地属性(文件属性),找不到的情况下再找寻环境属性(Environment)
context:property-placeholder的属性
- location-文件资源路径,支持classpath路径,且可指定多个,以,分隔
- file-encoding-文件读取编码
- properties-ref-属性配置文件应用名
- order-排序属性
- ignore-resource-not-found-默认为false,表明找不到资源是否往外抛异常
- local-override-默认为false,表示优先查找文件(Properties)的属性,不存在则再找寻本地的属性(System),反之则是先本地后文件
- ignore-unresolvable-默认为false,true代表解析不了属性不抛异常
Spring源码情操陶冶-PropertyPlaceholderBeanDefinitionParser注解配置解析器的更多相关文章
- Spring源码情操陶冶-AnnotationConfigBeanDefinitionParser注解配置解析器
		本文承接前文Spring源码情操陶冶-自定义节点的解析,分析spring中的context:annotation-config节点如何被解析 源码概览 对BeanDefinitionParser接口的 ... 
- Spring源码情操陶冶-ComponentScanBeanDefinitionParser文件扫描解析器
		承接前文Spring源码情操陶冶-自定义节点的解析,本文讲述spring通过context:component-scan节点干了什么事 ComponentScanBeanDefinitionParse ... 
- Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器
		aop-Aspect Oriented Programming,面向切面编程.根据百度百科的解释,其通过预编译方式和运行期动态代理实现程序功能的一种技术.主要目的是为了程序间的解耦,常用于日志记录.事 ... 
- Spring源码情操陶冶-自定义节点的解析
		本文承接前文Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,特开辟出一块新地来啃啃这块有意思的骨头 自定义节 ... 
- SpringMVC源码情操陶冶-ResourcesBeanDefinitionParser静态资源解析器
		解析mvc:resources节点,控制对静态资源的映射访问 查看官方注释 /** * {@link org.springframework.beans.factory.xml.BeanDefinit ... 
- Spring源码情操陶冶-AOP之Advice通知类解析与使用
		阅读本文请先稍微浏览下上篇文章Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器,本文则对aop模式的通知类作简单的分析 入口 根据前文讲解,我们知道通知类的 ... 
- Spring源码情操陶冶-tx:advice解析器
		承接Spring源码情操陶冶-自定义节点的解析.本节关于事务进行简单的解析 spring配置文件样例 简单的事务配置,对save/delete开头的方法加事务,get/find开头的设置为不加事务只读 ... 
- Spring源码情操陶冶#task:executor解析器
		承接Spring源码情操陶冶-自定义节点的解析.线程池是jdk的一个很重要的概念,在很多的场景都会应用到,多用于处理多任务的并发处理,此处借由spring整合jdk的cocurrent包的方式来进行深 ... 
- Spring源码情操陶冶-AbstractApplicationContext#finishBeanFactoryInitialization
		承接前文Spring源码情操陶冶-AbstractApplicationContext#registerListeners 约定web.xml配置的contextClass为默认值XmlWebAppl ... 
随机推荐
- 基于Windows下python环境变量配置
			方法和Java环境变量配置是一样的,不懂的请移步这里 虽然这样说,还是唠唠叨叨几句吧QAQ 默认情况下,在windows下安装python之后,系统并不会自动添加相应的环境变量.此时不能在命令行直接使 ... 
- 2017"百度之星"程序设计大赛 - 复赛1001&&HDU 6144 Arithmetic of Bomb【java大模拟】
			Arithmetic of Bomb Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ... 
- [bzoj3191] [JLOI2013]卡牌游戏
			概率DP. 首先由题解可得>_<,胜出概率只与剩余人数.与庄家的相对位置有关. 所以设f[i][j]表示剩下i个人,从庄家开始第j个人的胜利概率... 根据卡牌一通乱搞即可... #inc ... 
- node.js和express创建服务器
			创建web服务器 一. 使用node.js创建服务器. 使用express创建http服务. 监控服务器的变化. 二. 初始化配置文件:npm init -y 使用typescript编写,导入nod ... 
- ceph -s集群报错too many PGs per OSD
			背景 集群状态报错,如下: # ceph -s cluster 1d64ac80-21be-430e-98a8-b4d8aeb18560 health HEALTH_WARN <-- 报错的地方 ... 
- asp.net网站管理工具 遇到错误。请返回上一页并重试。
			原因:项目的路径里有“#”号. 
- 织梦中data文件夹是存放什么内容的
			dede(织梦)的data文件夹下的文件及文件夹也不少,我们来一个一个的介绍下. 1. admin文件夹 admin文件夹 管理员用到的文件夹,一般是后台的配置文件. 第一个文件,idc.txt 配置 ... 
- webkit
			HTML, 从HTML文档的开始到结束排列: <meta name="viewport" content="width=device-width, initial- ... 
- bug 对应
			异常1:not-null property references a null or transient value解决方法:将“一对多”关系中的“一”方,not-null设置为false http: ... 
- Mysql Nested-Loop Join Algorithms
			MySQL在多表之间执行join时,利用一种nested-loop algorithm 或者其变种:(嵌套循环) Nested-Loop Join Algorithm 一个简单的嵌套循环连 ... 
