概述
在上一篇解析自定义命名空间的标签 中,我们已经知道解析自定义命名空间的标签需要用到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) {
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
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 ;
if (shouldParseNameAsAliases()) {
String name = element.getAttribute("name" );
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
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 builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);
if (parentName != null ) {
builder.getRawBeanDefinition().setParentName(parentName);
}
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()) {
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) {
if ("ENVIRONMENT" .equals(element.getAttribute("system-properties-mode" ))) {
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);
}
if (element.hasAttribute("value-separator" )) {
builder.addPropertyValue("valueSeparator" , element.getAttribute("value-separator" ));
}
if (element.hasAttribute("trim-values" )) {
builder.addPropertyValue("trimValues" , element.getAttribute("trim-values" ));
}
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);
}
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));
}
builder.addPropertyValue("ignoreResourceNotFound" ,
Boolean.valueOf(element.getAttribute("ignore-resource-not-found" )));
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);
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的共同点。