在一个spring xml配置文件中,NamespaceHandler是DefaultBeanDefinitionDocumentReader用来处理自定义命名空间的基础接口。其层次结构如下:
<context>为开头的标签统一在ContextNamespaceHandler中进行解析,ContextNamespaceHandler继承了NamespaceHandlerSupport,实现了NamespaceHandler接口。其解析的标签有:
@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()); }
本文主要讲property-placeholder和property-override。
annotation-config和component-scan在这篇文章中进行了讲解,其它在后面的序列中会讲到。
1. property-placeholder
解析流程图如下:
解析过程如下:
>>DefaultBeanDefinitionDocumentReader读取xml配置文件,调用BeanDefinitionParserDelegate代理的parseCustomElement方法
>>BeanDefinitionParserDelegate调用NamespaceHandler的parse方法解析配置文件。
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); 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)); }
>>NamespaceHandler调用NamespaceHandlerSupport的parse方法
/** * Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is * registered for that {@link Element}. */ @Override public BeanDefinition parse(Element element, ParserContext parserContext) { return findParserForElement(element, parserContext).parse(element, parserContext); }
NamespaceHandlerSupport调用AbstractBeanDefinitionParser的parse方法,解析id和name
@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 = new String[0]; String name = element.getAttribute(NAME_ATTRIBUTE); 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; }
>>调用AbstractSingleBeanDefinitionParser的parseInternal方法
/** * Creates a {@link BeanDefinitionBuilder} instance for the * {@link #getBeanClass bean Class} and passes it to the * {@link #doParse} strategy method. * @param element the element that is to be parsed into a single BeanDefinition * @param parserContext the object encapsulating the current state of the parsing process * @return the BeanDefinition resulting from the parsing of the supplied {@link Element} * @throws IllegalStateException if the bean {@link Class} returned from * {@link #getBeanClass(org.w3c.dom.Element)} is {@code null} * @see #doParse */ @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()) { // Inner bean definition must receive same scope as containing bean. builder.setScope(parserContext.getContainingBeanDefinition().getScope()); } if (parserContext.isDefaultLazyInit()) { // Default-lazy-init applies to custom bean definitions as well. builder.setLazyInit(true); } doParse(element, parserContext, builder); return builder.getBeanDefinition(); }
>>调用
PropertyPlaceholderBeanDefinitionParser
的doParse方法
/** * Parser for the {@code <context:property-placeholder/>} element. * * @author Juergen Hoeller * @author Dave Syer * @author Chris Beams * @since 2.5 */ class PropertyPlaceholderBeanDefinitionParser extends AbstractPropertyLoadingBeanDefinitionParser { private static final String SYSTEM_PROPERTIES_MODE_ATTRIB = "system-properties-mode"; private static final String SYSTEM_PROPERTIES_MODE_DEFAULT = "ENVIRONMENT"; @Override protected Class<?> getBeanClass(Element element) { // As of Spring 3.1, the default value of system-properties-mode has changed from // 'FALLBACK' to 'ENVIRONMENT'. This latter value indicates that resolution of // placeholders against system properties is a function of the Environment and // its current set of PropertySources if (element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIB).equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) { return PropertySourcesPlaceholderConfigurer.class; } // the user has explicitly specified a value for system-properties-mode. Revert // to PropertyPlaceholderConfigurer to ensure backward compatibility. return PropertyPlaceholderConfigurer.class; } @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_ATTRIB); if (StringUtils.hasLength(systemPropertiesModeName) && !systemPropertiesModeName.equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) { builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_"+systemPropertiesModeName); } } }
其中,
PropertyPlaceholderConfigurer
PropertyPlaceholderConfigurer是PlaceholderConfigurerSupport的实现类,解析占位符setLoaction、setProperties设置的local、properties和系统属性。
从spring3.1 org.springframework.context.support.PropertySourcesPlaceholderConfigurer请比这个实现被更广泛的使用。因为PropertySourcesPlaceholderConfigurer灵活的使用org.springframework.core.env.Environment,并且它的org.springframework.core.env.PropertySource机制也在spring 3.1中可用了。
但PropertyPlaceholderConfigurer在以下场景还是比较有优势的:
spring-context模块api不可用(例如仅仅使用spring的BeanFactory),不能像ApplicationContext那样使用。
使用setSystemPropertiesMode(int)或者SetSystemPropertiesModeName(String)设置属性的已经存在的配置。鼓励开发者放弃使用这些设置方式,而不是通过容器的Environment来配置property source;然而,这些功能的维护仍然需要继续使用propertyPlaceholderConfigurer。
PropertySourcesPlaceholderConfigurer
PropertySourcesPlaceholderConfigurer是org.springframework.beans.factory.config.PlaceholderConfigurerSupport的特殊实现,它解析bean定义内的${..}占位符和@Value注解的当前spring的Environment和PropertySource集合中的属性值。
这个类用来替换spring3.1应用及以后的PropertyPlaceholderConfigurer。默认情况下,它在spring-context-3.1.xsd中支持<property-placeholder>,同时为了保证spring-context的向后兼容性,3.0版本默认值仍然是PropertyPlaceholderConfigurer。
任意本地变量(如通过setProperties或者通过setLocation设置的)作为propertySource增加,本地属性的搜索优先级基于setLocalOverride设置的localOverride属性的值,localOverride默认值为false,即本地属性在所有Environment属性source之后被搜索。PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是 BeanFactoryPostProcessor接口的一个实现。
/** * {@inheritDoc} * <p>Processing occurs by replacing ${...} placeholders in bean definitions by resolving each * against this configurer's set of {@link PropertySources}, which includes: * <ul> * <li>all {@linkplain org.springframework.core.env.ConfigurableEnvironment#getPropertySources * environment property sources}, if an {@code Environment} {@linkplain #setEnvironment is present} * <li>{@linkplain #mergeProperties merged local properties}, if {@linkplain #setLocation any} * {@linkplain #setLocations have} {@linkplain #setProperties been} * {@linkplain #setPropertiesArray specified} * <li>any property sources set by calling {@link #setPropertySources} * </ul> * <p>If {@link #setPropertySources} is called, <strong>environment and local properties will be * ignored</strong>. This method is designed to give the user fine-grained control over property * sources, and once set, the configurer makes no assumptions about adding additional sources. */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (this.propertySources == null) { this.propertySources = new MutablePropertySources(); if (this.environment != null) { this.propertySources.addLast( new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) { @Override public String getProperty(String key) { return this.source.getProperty(key); } } ); } try { PropertySource<?> localPropertySource = new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties()); if (this.localOverride) { this.propertySources.addFirst(localPropertySource); } else { this.propertySources.addLast(localPropertySource); } } catch (IOException ex) { throw new BeanInitializationException("Could not load properties", ex); } } processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources)); this.appliedPropertySources = this.propertySources; }
调用继续处理
/** * Visit each bean definition in the given bean factory and attempt to replace ${...} property * placeholders with values from the given properties. */ protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, final ConfigurablePropertyResolver propertyResolver) throws BeansException { propertyResolver.setPlaceholderPrefix(this.placeholderPrefix); propertyResolver.setPlaceholderSuffix(this.placeholderSuffix); propertyResolver.setValueSeparator(this.valueSeparator); StringValueResolver valueResolver = new StringValueResolver() { @Override public String resolveStringValue(String strVal) { String resolved = ignoreUnresolvablePlaceholders ? propertyResolver.resolvePlaceholders(strVal) : propertyResolver.resolveRequiredPlaceholders(strVal); return (resolved.equals(nullValue) ? null : resolved); } }; doProcessProperties(beanFactoryToProcess, valueResolver); }
继续
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, StringValueResolver valueResolver) { BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver); String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames(); for (String curName : beanNames) { // Check that we're not parsing our own bean definition, // to avoid failing on unresolvable placeholders in properties file locations. if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) { BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName); try { visitor.visitBeanDefinition(bd); } catch (Exception ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex); } } } // New in Spring 2.5: resolve placeholders in alias target names and aliases as well. beanFactoryToProcess.resolveAliases(valueResolver); // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes. beanFactoryToProcess.addEmbeddedValueResolver(valueResolver); }
2. property-override
解析流程图
解析过程
>>其它流程和
>>调用
PropertyOverrideBeanDefinitionParser
的doParse方法
/**
* Parser for the <context:property-override/> element.
*
* @author Juergen Hoeller
* @author Dave Syer
* @since 2.5.2
*/
class PropertyOverrideBeanDefinitionParser extends AbstractPropertyLoadingBeanDefinitionParser {
@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")));
}
}
小结
The PropertyPlaceholderConfigurer allows properties to be pulled in to the application context file.示例:
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:database.properties</value> </list> </property> </bean> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${db.user}"/> <property name="password" value="${db.password}"/> ... </bean>
The PropertyOverrideConfigurer works in the opposite way, pushing properties from property files into properties in the context file (without having to specifically define a place holder – e.g. ${db.user}).
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer"> <property name="location" value="classpath:beanOverride.cfg" /> </bean> <bean id="person" class="com.concretepage.Person" > <property name="name" value="Ram"/> <property name="age" value="20"/> <property name="location" value="Varanasi"/> </bean> </beans>
参考文献
【1】http://www.summa.com/blog/2009/04/20/6-tips-for-managing-property-files-with-spring
【2】http://www.concretepage.com/spring/example_propertyoverrideconfigurer_spring
微信公众号: 架构师日常笔记 欢迎关注!