Spring4.3.x 浅析xml配置的解析过程(6)——解析context命名空间之property-placeholder和property-override标签
来源:互联网 发布:程序员的数学 线性代数 编辑:程序博客网 时间:2024/06/13 09:42
概述
在上一篇解析自定义命名空间的标签 中,我们已经知道解析自定义命名空间的标签需要用到NamespaceHandler接口的实现类,并且知道spring是如何获取命名空间对应的命名空间处理器对象的。因此我们很容易就能在spring-context包下的META-INF/spring.handlers文件中找到http://www.springframework.org/schema/context命名空间(即本文说的context命名空间)的处理器org.springframework.context.config.ContextNamespaceHandler,下面是ContextNamespaceHandler类的源码。
public class ContextNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); }}
ContextNamespaceHandler 实现了NamespaceHandler接口的init方法来为context命名空间下的标签注册解析器BeanDefinitionParser对象。
Spring context命名空间有property-placeholder、property-override、annotation-config、component-scan、load-time-weaver、spring-configured、mbean-export和mbean-server 8个标签。这8个标签都有一个BeanDefinitionParser实现类与之对应。这一节我们分别探讨property-placeholder和property-override标签的解析。
解析property-placeholder标签
property-placeholder标签用于加载property属性文件。如果bean的<property>value值与属性文件中的某个值相同,那么,默认情况下,定义<property>的value值可以使用表达式“${key}”,其中key为属性文件中=左边的字符串。
property-placeholder标签对应的BeanDefinitionParser实现类是PropertyPlaceholderBeanDefinitionParser,下面是这个类的继承结构。
AbstractBeanDefinitionParser是PropertyPlaceholderBeanDefinitionParser类的一个抽象父类,它实现了BeanDefinitionParser接口的parse(Element element, ParserContext parserContext)方法,代码如下。
@Override public final BeanDefinition parse(Element element, ParserContext parserContext) { // 调用抽象方法parseInternal(Element element, ParserContext parserContext) // 这个方法有子类实现,把解析指定Element对象的任务交给子类完成 AbstractBeanDefinition definition = parseInternal(element, parserContext); if (definition != null && !parserContext.isNested()) { try { // 解析并未bean生成一个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 = null; // 检测是否应该把name属性作为bean的别名,默认为true // 子类可以重写shouldParseNameAsAliases()来决定 if (shouldParseNameAsAliases()) { // 获取bean的别名 String name = element.getAttribute("name"); if (StringUtils.hasLength(name)) { aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); } } // 创建BeanDefinitionHolder对象 BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); // 注册BeanDefintion registerBeanDefinition(holder, parserContext.getRegistry()); if (shouldFireEvents()) { BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); postProcessComponentDefinition(componentDefinition); parserContext.registerComponent(componentDefinition); } } catch (BeanDefinitionStoreException ex) { parserContext.getReaderContext().error(ex.getMessage(), element); return null; } } return definition; }
AbstractBeanDefinitionParser的parse方法,首先把解析节点的任务交给子类来完成,子类需要实现parseInternal(Element element, ParserContext parserContext)方法并返回一个AbstractBeanDefinition 对象;然后根据需要设置bean的id和别名;最后创建并注册BeanDefinitionHolder对象。下面我们重点看parseInternal方法,在PropertyPlaceholderBeanDefinitionParser的继承体系中,parseInternal方法的实现在AbstractSingleBeanDefinitionParser类中,代码如下。
@Override protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { // 创建BeanDefinitionBuilder对象,这个对象只是代理了一个GenericBeanDefinition对象 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); String parentName = getParentName(element); if (parentName != null) { builder.getRawBeanDefinition().setParentName(parentName); } // 获取要实例化bean的类对象,默认为null,一般由子类提供。 Class<?> beanClass = getBeanClass(element); if (beanClass != null) { 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()) { // 嵌套的bean定义,必须和外层的bean在同一个作用域 builder.setScope(parserContext.getContainingBeanDefinition().getScope()); } if (parserContext.isDefaultLazyInit()) { // 默认为延迟加载 builder.setLazyInit(true); } // 把继续解析标签的任务交给子类 doParse(element, parserContext, builder); return builder.getBeanDefinition(); }
AbstractSingleBeanDefinitionParser的parseInternal方法创建了一个BeanDefinitionBuilder对象,这个对象只是代理了GenericBeanDefinition对象,以简化对GenericBeanDefinition对象的操作,然后就是一些基本的配置,这在代码中已经体现了。parseInternal也并未对节点做实质性的操作,它只调用为子类创建的一些钩子方法,这些方法有:
用于获取要实例化bean的类(Class)对象或者类全名称的方法:
protected Class<?> getBeanClass(Element element) { return null; } protected String getBeanClassName(Element element) { return null; }
以及用于进一步解析节点的方法
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { doParse(element, builder); } protected void doParse(Element element, BeanDefinitionBuilder builder) { }
property-placeholder标签的解析器PropertyPlaceholderBeanDefinitionParser类重写了上面2组方法中的getBeanClass(Element element)方法和doParse(Element element, BeanDefinitionBuilder builder) 方法。
下面是getBeanClass(Element element)方法的源码。
@Override protected Class<?> getBeanClass(Element element) { // 从spring3.1开始system-properties-mode属性的默认值就为ENVIRONMENT,不再是FALLBACK if ("ENVIRONMENT".equals(element.getAttribute("system-properties-mode"))) { // PropertySourcesPlaceholderConfigurer对象可以从上下文的环境对象中获取属性值 return PropertySourcesPlaceholderConfigurer.class; } return PropertyPlaceholderConfigurer.class; }
说明:PropertyPlaceholderConfigurer和PropertySourcesPlaceholderConfigurer都是抽象类PlaceholderConfigurerSupport的直接子类,见总结。
下面是doParse(Element element, BeanDefinitionBuilder builder)方法的源码。
@Override protected void doParse(Element element, BeanDefinitionBuilder builder) { super.doParse(element, builder); builder.addPropertyValue("ignoreUnresolvablePlaceholders", Boolean.valueOf(element.getAttribute("ignore-unresolvable"))); String systemPropertiesModeName = element.getAttribute("system-properties-mode"); if (StringUtils.hasLength(systemPropertiesModeName) && !systemPropertiesModeName.equals("system-properties-mode")) { builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_" + systemPropertiesModeName); } // 指定一个分隔符用于分隔默认值,默认为英文冒号: // 比如${user.name:chyohn},如果user.name没有在属性文件中定义,则使用默认值chyohn // 假设设置的分隔符为英文?,则上面的定义应该为${name?choyhn} if (element.hasAttribute("value-separator")) { builder.addPropertyValue("valueSeparator", element.getAttribute("value-separator")); } // 设置是否允许trim获取到的属性值,默认为false if (element.hasAttribute("trim-values")) { builder.addPropertyValue("trimValues", element.getAttribute("trim-values")); } // 指定一个值用于表示null,比如指定的为hasNull, // 如果某一个属性值定义的时候为haveNull,那么生成的bean的这个属性真正值就是null if (element.hasAttribute("null-value")) { builder.addPropertyValue("nullValue", element.getAttribute("null-value")); } }
PropertyPlaceholderBeanDefinitionParser类的doParse方法首先调用父类AbstractPropertyLoadingBeanDefinitionParser的doParse方法,然后根据节点配置的属性值来修改PlaceholderConfigurerSupport的属性值。这里我们在继续看看AbstractPropertyLoadingBeanDefinitionParser的doParse方法源代码。
@Override protected void doParse(Element element, BeanDefinitionBuilder builder) { // 获取属性文件的地址参数 String location = element.getAttribute("location"); if (StringUtils.hasLength(location)) { // 如果有多个属性文件,每个属性文件可以使用英文逗号隔开。 String[] locations = StringUtils.commaDelimitedListToStringArray(location); builder.addPropertyValue("locations", locations); } // 获取指定的Properties对象的bean名称 // 如果local-override属性为true,这里设置的properties将覆盖属性文件中的内容 String propertiesRef = element.getAttribute("properties-ref"); if (StringUtils.hasLength(propertiesRef)) { builder.addPropertyReference("properties", propertiesRef); } // 获取属性文件编码 String fileEncoding = element.getAttribute("file-encoding"); if (StringUtils.hasLength(fileEncoding)) { builder.addPropertyValue("fileEncoding", fileEncoding); } // 设置排序,即优先级 String order = element.getAttribute("order"); if (StringUtils.hasLength(order)) { builder.addPropertyValue("order", Integer.valueOf(order)); } // 设置是否忽略指定的属性文件不存在的错误,true为是 builder.addPropertyValue("ignoreResourceNotFound", Boolean.valueOf(element.getAttribute("ignore-resource-not-found"))); // 一般来说,属性文件的内容更后加载。 // 如果localOverride为true,那么PropertiesLoaderSupport的localProperties内容就会覆盖属性文件中相同key的的内容。 // 如果标签是property-placeholder且localOverride为true,上下文的环境对象中的数据也会覆盖属性文件中相同key的内容。 // 默认为false builder.addPropertyValue("localOverride", Boolean.valueOf(element.getAttribute("local-override"))); builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); }
解析property-override标签
property-override标签的作用是为xml配置文件中的bean的属性指定最终结果。
property-override标签的解析器类为PropertyOverrideBeanDefinitionParser,这个类和property-placeholder标签的解析器类PropertyPlaceholderBeanDefinitionParser一样是AbstractPropertyLoadingBeanDefinitionParser类的直接子类,并且PropertyOverrideBeanDefinitionParser重写了getBeanClass(Element element)和doParse(Element element, BeanDefinitionBuilder builder)方法,代码如下。
@Override protected Class<?> getBeanClass(Element element) { return PropertyOverrideConfigurer.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder builder) { super.doParse(element, builder); // 设置忽略不正确的键,也就是忽略格式不正确的键,或者是.左边的bean名称不存在的键 // 默认为false,表示不忽略 builder.addPropertyValue("ignoreInvalidKeys", Boolean.valueOf(element.getAttribute("ignore-unresolvable"))); }
关于AbstractPropertyLoadingBeanDefinitionParser类在前面已经探讨过了,关于这段代码也就没有什么要多说的。
总结
(1)property-override和property-placeholder的不同点。
功能不一样,property-override标签的作用是为xml配置文件中的bean的属性指定最终结果;而property-placeholder标签的作用是把xml配置文件中bean 的<property>标签的value值替换成正真的值,而且<property>标签的value值必须符合特定的表达式格式,默认为“${key}”,其中key为属性文件中的key。
属性文件内容要求不一样,property-override标签加载的properties文件中的key的格式有严格的要求,必须为“bean名称.bean属性”。如果属性ignore-unresolvable的值为false,那么属性文件中的bean名称必须在当前容器中能找到对应的bean。
(2)property-override和property-placeholder的共同点。
两者都是以properties文件作为数据来源。
两者的解析器BeanDefinitionParser类都继承自AbstractPropertyLoadingBeanDefinitionParser类。因此它们共有AbstractPropertyLoadingBeanDefinitionParser及其父类中所处理的标签属性,并且这些属性在两个标签中具有相同的作用。这其实都归于它们所代表的工厂后处理器都继承了PropertiesLoaderSupport类,具体看下面的继承结构图,其中第一个是property-override的,后面两个是property-placeholder的。
- Spring4.3.x 浅析xml配置的解析过程(6)——解析context命名空间之property-placeholder和property-override标签
- Spring4.3.x 浅析xml配置的解析过程(7)——解析context命名空间之annotation-config标签
- Spring4.3.x 浅析xml配置的解析过程(8)——解析context命名空间之component-scan标签
- Spring4.3.x 浅析xml配置的解析过程(9)——解析aop命名空间之config标签
- Spring4.3.x 浅析xml配置的解析过程(5)——解析自定义命名空间的标签
- Spring4.3.x 浅析xml配置的解析过程(10)——解析aop命名空间之aspectj-autoproxy标签
- Spring4.3.x 浅析xml配置的解析过程(11)——解析aop命名空间之scoped-proxy标签
- Spring4.3.x 浅析xml配置的解析过程(4)——解析bean标签及其所有子标签
- Spring配置<context:property-placeholder>标签解析
- context:property-placeholder 标签解析
- Spring4.3.x 浅析xml配置的解析过程(1)——使用XmlBeanDefinitionReader解析xml配置
- Spring4.3.x 浅析xml配置的解析过程(2)——使用ResourceLoader创建Resource对象
- Spring4.3.x 浅析xml配置的解析过程(3)——使用DocumentLoader创建Document对象
- Spring的properties解析【context:property-placeholder】
- spring中context:property-placeholder解析
- spring 标签:context:property-placeholder
- property-placeholder源码解析
- springmvc之context:property-placeholder
- 资源文件无法识别
- day03
- asp.net 使用Ueditor过程中出现的问题及解决办法
- mapReduce大量小文件的优化策略
- OPENSSL库的使用-AES篇
- Spring4.3.x 浅析xml配置的解析过程(6)——解析context命名空间之property-placeholder和property-override标签
- Java之CyclicBarrier使用
- html5多媒体标签之video标签
- mapreduce原理
- Android Studio生成keystore签名文件
- 洛谷 P1417 烹调方案
- TensorFlow--学习笔记
- OPENSSL库的使用-RSA篇
- 流程用例之OmniGraffle