在一個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