码迷,mamicode.com
首页 > 编程语言 > 详细

spring源码分析之<context:property-placeholder/>和<property-override/>

时间:2016-08-09 09:27:30      阅读:718      评论:0      收藏:0      [点我收藏+]

标签:

在一个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 &lt;context:property-override/&gt; 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

spring源码分析之<context:property-placeholder/>和<property-override/>

标签:

原文地址:http://www.cnblogs.com/davidwang456/p/5737826.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!