Spring3.2 运行时环境
2016 年 02 月 15 日
spring

    在日常中,经常会碰到一些环境相关的术语,也有说是上下文的,Maven中通过profile抽象出了环境配置,使开发人员可以通过不同的profile来定义不同的build上下文Spring中也有环境一说,通过接口Enviroment来抽象,其随着Spring IoC容器的初始化而建立起来,本文将探究一番Spring运行时环境的相关细节。

  • 接口定义

  • Spring中将容器环境抽象为了Environment接口:

    public interface Environment extends PropertyResolver {
    
    	/**
    	 * 获取容器激活的profile
    	 */
    	String[] getActiveProfiles();
    
    	/**
    	 * 获取容器默认激活的profile
    	 */
    	String[] getDefaultProfiles();
    
    	/**
    	 * 某profile是否激活,可以使用!作非逻辑,如
    	 * env.acceptsProfiles("p1", "!p2"),若p1激活或者p2未激活,将返回true
    	 */
    	boolean acceptsProfiles(String... profiles);
    }
        

    可以看到Environment定义了有关获取profile的方法,并且继承了PropertyResolver接口,该接口定义了从具体的属性源中解析属性的功能:

    public interface PropertyResolver {
    
    	/**
    	 * 检查是否有某属性
    	 */
    	boolean containsProperty(String key);
    
    	/**
    	 * 获取某属性
    	 */
    	String getProperty(String key);
    
    	/**
    	 * 获取某属性,不存在则返回defaultValue
    	 */
    	String getProperty(String key, String defaultValue);
    
    	/**
    	 * 获取某属性,其值类型为T
    	 */
    	<T> T getProperty(String key, Class<T> targetType);
    
    	/**
    	 * 获取某属性,其值类型为T,不存在则返回defaultValue
    	 */
    	<T> T getProperty(String key, Class<T> targetType, T defaultValue);
    
    	/**
    	 * 获取某属性,并转换为对应的Class对象,失败会抛ConversionException
    	 */
    	<T> Class<T> getPropertyAsClass(String key, Class<T> targetType);
    
    	/**
    	 * 获取某属性,不存在抛出IllegalStateException
    	 */
    	String getRequiredProperty(String key) throws IllegalStateException;
    
    	/**
    	 * 获取某属性,其值类型为T,不存在抛出IllegalStateException
    	 */
    	<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
    
    	/**
    	 * 解析占位符${...}
    	 */
    	String resolvePlaceholders(String text);
    
    	/**
    	 * 解析占位符${...},不存在抛出IllegalArgumentException
    	 */
    	String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
    }
        

    可见Spring环境主要由两个组件组成:配置(profile)属性(property)profile只是一个名称字符串,可对Bean容器中的Bean进行逻辑分组,即在定义Bean时,可以指定该Bean归入到某些profile中。Property则是大多应用所常见的,Spring中这些属性可以来自properties文件JVM系统属性系统环境变量JNDIServlet上下文参数Properties对象等。

  • Environment实现

  • 先看下Environment继承树,比较简单直观:

  • ConfigurableEnvironment

  • ConfigurableEnvironment主要提供对Spring容器环境的一些配置功能,并暴露了重要的getPropertySources()方法,用户可以对环境中的属性源进行更新操作等:

    public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
    
    	/**
    	 * 设置激活的profile
    	 */
    	void setActiveProfiles(String... profiles);
    
    	/**
    	 * 添加激活的profile
    	 */
    	void addActiveProfile(String profile);
    
    	/**
    	 * 设置默认激活的profile
    	 */
    	void setDefaultProfiles(String... profiles);
    
    	/**
    	 * 获取属性源对象,通过该对象可以对属性源更新,重要的是通过addFirst,addLast等可定义属性源的优先级
    	 */
    	MutablePropertySources getPropertySources();
    
    	/**
    	 * 获取系统环境变量
    	 */
    	Map<String, Object> getSystemEnvironment();
    
    	/**
    	 * 获取系统属性
    	 */
    	Map<String, Object> getSystemProperties();
    
    	/**
    	 * 1. 将parent环境激活的profile,默认的profile,属性源追加到当前child环境对象;
    	 * 2. 若parent和child具有同名的属性源,child的属性源将被保留,parent的属性源将丢弃;
    	 * 3. 激活和默认的profile会去掉重复的;
    	 * 4. parent环境环境始终是不变的,任何merge后对parent环境的变化将不会影响到child环境
    	 */
    	void merge(ConfigurableEnvironment parent);
    }
        
  • AbstractEnvironment

  • AbstractEnvironment作为Environment的基础实现,可以设置激活的profile默认激活的profile,具体子类主要需要提供默认的属性源对象,通过customizePropertySources()来定制属性源,而客户端应该通过getPropertySources()来定制属性源:

     public abstract class AbstractEnvironment implements ConfigurableEnvironment {
    
    	/**
    	 * 是否忽略系统变量System.getenv(),默认为false
    	 */
    	public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";
    
    	/**
    	 * 激活的profile属性名,值可以为逗号隔开的字符串
    	 */
    	public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
    
    	/**
    	 * 默认激活的profile属性名,值可以为逗号隔开的字符串
    	 */
    	public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
    
    	/**
    	 * 默认的profile名称
    	 */
    	protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
    
    	protected final Log logger = LogFactory.getLog(getClass());
    
    	// 存放激活的profile集,通过LinkedHashSet保证有序且具有优先级
    	private final Set<String> activeProfiles = new LinkedHashSet<String>();
    
    	// 存放默认的profile集,通过LinkedHashSet保证有序且具有优先级
    	private final Set<String> defaultProfiles = new LinkedHashSet<String>(getReservedDefaultProfiles());
    
    	// 属性源对象
    	private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
    
    	// 属性解析器,内部通过迭代属性源来获取属性
    	private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
    
    	public AbstractEnvironment() {
    		// 由子类定制属性源
    		customizePropertySources(this.propertySources);
    		if (this.logger.isDebugEnabled()) {
    			this.logger.debug(format(
    					"Initialized %s with PropertySources %s", getClass().getSimpleName(), this.propertySources));
    		}
    	}
    
    	/**
    	 * 由子类定制属性源
    	 */
    	protected void customizePropertySources(MutablePropertySources propertySources) {
    	}
    
    	/**
    	 * 获取默认的profile名称集
    	 */
    	protected Set<String> getReservedDefaultProfiles() {
    		return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME);
    	}
    
    
    	//---------------------------------------------------------------------
    	//ConfigurableEnvironment接口实现
    	//---------------------------------------------------------------------
    
    	/**
    	 * 获取激活的profile集
    	 */
    	public String[] getActiveProfiles() {
    		return StringUtils.toStringArray(doGetActiveProfiles());
    	}
    
    	/**
    	 * 从属性源中获取激活的profile集
    	 */
    	protected Set<String> doGetActiveProfiles() {
    		synchronized (this.activeProfiles) {
    			if (this.activeProfiles.isEmpty()) {
    				// 在属性源中找Key为spring.profiles.active的属性
    				String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
    				if (StringUtils.hasText(profiles)) {
    					// 逗号隔开的字符串,重新设置激活的profile
    					setActiveProfiles(commaDelimitedListToStringArray(trimAllWhitespace(profiles)));
    				}
    			}
    			return this.activeProfiles;
    		}
    	}
    
    	/**
    	 * 重新设置激活的profile
    	 */
    	public void setActiveProfiles(String... profiles) {
    		Assert.notNull(profiles, "Profile array must not be null");
    		synchronized (this.activeProfiles) {
    			// 清空之前激活的profiles
    			this.activeProfiles.clear();
    			for (String profile : profiles) {
    				// 验证profile名字: 非空,不能仅包含空格,不能以!开头
    				validateProfile(profile);
    				this.activeProfiles.add(profile);
    			}
    		}
    	}
    
    	/**
    	 * 添加激活的profile
    	 */
    	public void addActiveProfile(String profile) {
    		validateProfile(profile);
    		doGetActiveProfiles();
    		synchronized (this.activeProfiles) {
    			this.activeProfiles.add(profile);
    		}
    	}
    
    	/**
    	 * 获取默认的profile集
    	 */ 
    	public String[] getDefaultProfiles() {
    		return StringUtils.toStringArray(doGetDefaultProfiles());
    	}
    
    	/**
    	 * 获取默认的profile集,从属性源中获取Key为spring.profiles.default的值
    	 */
    	protected Set<String> doGetDefaultProfiles() {
    		synchronized (this.defaultProfiles) {
    			if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {
    				String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
    				if (StringUtils.hasText(profiles)) {
    					setDefaultProfiles(commaDelimitedListToStringArray(trimAllWhitespace(profiles)));
    				}
    			}
    			return this.defaultProfiles;
    		}
    	}
    
    	/**
    	 * 重新设置默认profile集
    	 */
    	public void setDefaultProfiles(String... profiles) {
    		Assert.notNull(profiles, "Profile array must not be null");
    		synchronized (this.defaultProfiles) {
    			this.defaultProfiles.clear();
    			for (String profile : profiles) {
    				validateProfile(profile);
    				this.defaultProfiles.add(profile);
    			}
    		}
    	}
    
    	/**
    	 * 判断某profile是否激活,若以!开头,则判断是否没有激活
    	 */
    	public boolean acceptsProfiles(String... profiles) {
    		Assert.notEmpty(profiles, "Must specify at least one profile");
    		for (String profile : profiles) {
    			if (StringUtils.hasLength(profile) && profile.charAt(0) == '!') {
    				if (!isProfileActive(profile.substring(1))) {
    					return true;
    				}
    			} else if (isProfileActive(profile)) {
    				return true;
    			}
    		}
    		return false;
    	}
    
    	/**
    	 * 判断当前激活的profiles集中或默认激活的profiles中是否包含该profile
    	 */
    	protected boolean isProfileActive(String profile) {
    		validateProfile(profile);
    		Set<String> currentActiveProfiles = doGetActiveProfiles();
    		return (currentActiveProfiles.contains(profile) ||
    				(currentActiveProfiles.isEmpty() && doGetDefaultProfiles().contains(profile)));
    	}
    
    	/**
    	 * 验证profile名称: 非空,不能仅包含空格,不能以!开头
    	 */
    	protected void validateProfile(String profile) {
    		if (!StringUtils.hasText(profile)) {
    			throw new IllegalArgumentException("Invalid profile [" + profile + "]: must contain text");
    		}
    		if (profile.charAt(0) == '!') {
    			throw new IllegalArgumentException("Invalid profile [" + profile + "]: must not begin with ! operator");
    		}
    	}
    
    	/**
    	 * 暴露内部的属性源,用户可以自由定制属性源
    	 */
    	public MutablePropertySources getPropertySources() {
    		return this.propertySources;
    	}
    
    	/**
    	 * 获取系统环境变量
    	 */ 
    	public Map<String, Object> getSystemEnvironment() {
    		if (suppressGetenvAccess()) {
    			// 若Spring容易已经忽略系统环境变量
    			return Collections.emptyMap();
    		}
    		try {
    			// 返回系统变量Map对象
    			return (Map) System.getenv();
    		} catch (AccessControlException ex) {
    			return (Map) new ReadOnlySystemAttributesMap() {
    				@Override
    				protected String getSystemAttribute(String attributeName) {
    					try {
    						return System.getenv(attributeName);
    					} catch (AccessControlException ex) {
    						if (logger.isInfoEnabled()) {
    							logger.info(format("Caught AccessControlException when accessing system " +
    									"environment variable [%s]; its value will be returned [null]. Reason: %s",
    									attributeName, ex.getMessage()));
    						}
    						return null;
    					}
    				}
    			};
    		}
    	}
    
    	/**
    	 * Spring容器是否忽略环境变量,默认false
    	 * 用户可以在应用classpath下配置spring.properties文件定制该属性
    	 */
    	protected boolean suppressGetenvAccess() {
    		return SpringProperties.getFlag(IGNORE_GETENV_PROPERTY_NAME);
    	}
    
    	/**
    	 * 获取系统属性Map对象
    	 */
    	public Map<String, Object> getSystemProperties() {
    		try {
    			return (Map) System.getProperties();
    		}
    		catch (AccessControlException ex) {
    			return (Map) new ReadOnlySystemAttributesMap() {
    				@Override
    				protected String getSystemAttribute(String attributeName) {
    					try {
    						return System.getProperty(attributeName);
    					} catch (AccessControlException ex) {
    						if (logger.isInfoEnabled()) {
    							logger.info(format("Caught AccessControlException when accessing system " +
    									"property [%s]; its value will be returned [null]. Reason: %s",
    									attributeName, ex.getMessage()));
    						}
    						return null;
    					}
    				}
    			};
    		}
    	}
    
    	/**
    	 * 合并环境对象:包括属性源,激活profile集,默认profile集
    	 */
    	public void merge(ConfigurableEnvironment parent) {
    		// 合并属性源,通过addLast保证child属性源优先级更高
    		for (PropertySource<?> ps : parent.getPropertySources()) {
    			if (!this.propertySources.contains(ps.getName())) {
    				this.propertySources.addLast(ps);
    			}
    		}
    		// 合并激活的profile集
    		String[] parentActiveProfiles = parent.getActiveProfiles();
    		if (!ObjectUtils.isEmpty(parentActiveProfiles)) {
    			synchronized (this.activeProfiles) {
    				for (String profile : parentActiveProfiles) {
    					this.activeProfiles.add(profile);
    				}
    			}
    		}
    		// 合并默认的profile集
    		String[] parentDefaultProfiles = parent.getDefaultProfiles();
    		if (!ObjectUtils.isEmpty(parentDefaultProfiles)) {
    			synchronized (this.defaultProfiles) {
    				// 移除掉default,parent里也有
    				this.defaultProfiles.remove(RESERVED_DEFAULT_PROFILE_NAME);
    				for (String profile : parentDefaultProfiles) {
    					this.defaultProfiles.add(profile);
    				}
    			}
    		}
    	}
    
    
    	//---------------------------------------------------------------------
    	// ConfigurablePropertyResolver接口实现,均分发给PropertyResolver处理
    	//---------------------------------------------------------------------
    
    	/**
    	 * 转换服务
    	 */
    	public ConfigurableConversionService getConversionService() {
    		return this.propertyResolver.getConversionService();
    	}
    	public void setConversionService(ConfigurableConversionService conversionService) {
    		this.propertyResolver.setConversionService(conversionService);
    	}
    
    	/**
    	 * 设置占位符前缀,默认${
    	 */
    	public void setPlaceholderPrefix(String placeholderPrefix) {
    		this.propertyResolver.setPlaceholderPrefix(placeholderPrefix);
    	}
    
    	/*
    	 * 设置占位符后缀,默认}
    	 */
    	public void setPlaceholderSuffix(String placeholderSuffix) {
    		this.propertyResolver.setPlaceholderSuffix(placeholderSuffix);
    	}
    
    	public void setValueSeparator(String valueSeparator) {
    		this.propertyResolver.setValueSeparator(valueSeparator);
    	}
    
    	public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders) {
    		this.propertyResolver.setIgnoreUnresolvableNestedPlaceholders(ignoreUnresolvableNestedPlaceholders);
    	}
    
    	public void setRequiredProperties(String... requiredProperties) {
    		this.propertyResolver.setRequiredProperties(requiredProperties);
    	}
    
    	public void validateRequiredProperties() throws MissingRequiredPropertiesException {
    		this.propertyResolver.validateRequiredProperties();
    	}
    
    
    	//---------------------------------------------------------------------
    	// PropertyResolver接口实现
    	//---------------------------------------------------------------------
    
    	@Override
    	public boolean containsProperty(String key) {
    		return this.propertyResolver.containsProperty(key);
    	}
    
    	public String getProperty(String key) {
    		return this.propertyResolver.getProperty(key);
    	}
    
    	public String getProperty(String key, String defaultValue) {
    		return this.propertyResolver.getProperty(key, defaultValue);
    	}
    
    	public <T> T getProperty(String key, Class<T> targetType) {
    		return this.propertyResolver.getProperty(key, targetType);
    	}
    
    	public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
    		return this.propertyResolver.getProperty(key, targetType, defaultValue);
    	}
    
    	public <T> Class<T> getPropertyAsClass(String key, Class<T> targetType) {
    		return this.propertyResolver.getPropertyAsClass(key, targetType);
    	}
    
    	public String getRequiredProperty(String key) throws IllegalStateException {
    		return this.propertyResolver.getRequiredProperty(key);
    	}
    
    	public <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException {
    		return this.propertyResolver.getRequiredProperty(key, targetType);
    	}
    
    	public String resolvePlaceholders(String text) {
    		return this.propertyResolver.resolvePlaceholders(text);
    	}
    
    	public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
    		return this.propertyResolver.resolveRequiredPlaceholders(text);
    	}
    }   
        
  • StandardEnvironment

  • StandardEnvironment比较简单,主要定制了系统属性系统环境这两个属性源:

    public class StandardEnvironment extends AbstractEnvironment {
    
    	/**
    	 * 系统变量属性源名称
    	 */
    	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
    
    	/**
    	 * 系统属性源名称
    	 */ 
    	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
    
    	/**
    	 * 定制属性源
    	 */
    	@Override
    	protected void customizePropertySources(MutablePropertySources propertySources) {
    		// 定制系统属性源
    		propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
    		// 系统变量属性源
    		propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
    	}
    }
        
  • StandardServletEnvironment

  • StandardServletEnvironment从其名称,是针对Web容器的一个环境对象:

    public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
    
    	/**
    	 * Servlet上下文属性参数属性源
    	 */
    	public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
    
    	/** 
    	 * Servlet配置参数属性源
    	 */
    	public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
    
    	/** 
    	 * jndi属性
    	 */
    	public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";
    
    	@Override
    	protected void customizePropertySources(MutablePropertySources propertySources) {
    		// Servlet配置参数属性源名称
    		propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
    		// Servlet上下文属性源名称
    		propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
    		if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
    			// JNDI属性源
    			propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
    		}
    		// 调用StandardEnvironment初始化系统属性和系统环境变量
    		super.customizePropertySources(propertySources);
    	}
    
    	public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig) {
    		// 真实初始化Servlet的属性源
    		WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
    	}
    }    
        
  • Environment初始化

  • Spring IoC容器实现中可以知道Environment的初始化发生在Bean加载之前,并在解析配置文件占位符时被实例化:

  • Environment使用

  • 直接使用Environment

  • 日常开发中,若想使用Environment,可以通过依赖注入或Spring中普遍的Aware回调方式:

    @Autowired
    private Environment env;
        
    public class MyService implements EnvironmentAware {
    
        @Override
        public void setEnvironment(Environment env) {
            System.out.println("EnvironmentAware callback");
        }
    }
        
  • profile使用

  • 之前介绍IoC容器初始化时,在DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions()中加载XML配置文件元素时,会根据当前容器激活的环境进行加载:

    // DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions()
    protected void doRegisterBeanDefinitions(Element root) {
    	// 获取beans元素的profile属性
    	String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
    	if (StringUtils.hasText(profileSpec)) {
    		String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
    				profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    		// 若与容器激活环境不一致,则忽略			
    		if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
    			return;
    		}
    	}
    	// ...
    }	
        

    从前文可知,用户可以通过spring.profile.active属性激活当前容器环境,这将便于开发人员在不同的profile环境中配置不同的bean:

    // 设置容器当前环境为dev,那么profile=dev的beans元素将被解析
    System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, "dev");
        
    <beans profile="dev">
        <bean class="service.EchoService" />
    </beans>
    
    <!-- 该beans将被忽略 -->
    <beans profile="prod">
        <bean class="service.EchoService2" />
    </beans>
        
  • 属性源

  • 上文将了Spring容器环境两个组件中的profile组件,下面将介绍其另一个组件property(属性)

  • 定制属性源

  • 通常开发中,需要定制自己的一些属性源,如一些properties文件等,可以通过PropertySourcesPlaceholderConfigurer配置自定义的properties属性文件:

    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:/demo.properties</value>
            </list>
        </property>
    </bean>
        

    PropertyPlaceholderConfigurer本质是一个BeanFactoryPostProcessor,在Spring IoC容器实现中说到过,BeanFactoryPostProcessor会在所有Bean加载完之后Bean实例化之前被调用,即在AbstractApplicationContext.invokeBeanFactoryPostProcessors()中调用:

    private void invokeBeanFactoryPostProcessors(
    		Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
    	for (BeanFactoryPostProcessor postProcessor : postProcessors) {
    		postProcessor.postProcessBeanFactory(beanFactory);
    	}
    }
    
    // PropertyPlaceholderConfigurer.postProcessBeanFactory()
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    	try {
    		// 合并属性文件中的KV
    		Properties mergedProps = mergeProperties();
    
    		// 作一些必要的转换处理
    		convertProperties(mergedProps);
    
    		processProperties(beanFactory, mergedProps);
    	}
    	catch (IOException ex) {
    		throw new BeanInitializationException("Could not load properties", ex);
    	}
    }
    
    // PropertyPlaceholderConfigurer.processProperties()
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
    		throws BeansException {
    	// 占位符解析对象	
    	StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
    	doProcessProperties(beanFactoryToProcess, valueResolver);
    }
    
    // PlaceholderConfigurerSupport.doProcessProperties()
    protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
    			StringValueResolver valueResolver) {
    
    	BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
    
    	String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
    	for (String curName : beanNames) {
    		if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
    			BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
    			try {
    				// 解析BeanDefinition中的占位符属性
    				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);
    }
    
    // BeanDefinitionVisitor.visitBeanDefinition()
    public void visitBeanDefinition(BeanDefinition beanDefinition) {
    	visitParentName(beanDefinition);
    	visitBeanClassName(beanDefinition);
    	visitFactoryBeanName(beanDefinition);
    	visitFactoryMethodName(beanDefinition);
    	visitScope(beanDefinition);
    	visitPropertyValues(beanDefinition.getPropertyValues());
    	ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
    	visitIndexedArgumentValues(cas.getIndexedArgumentValues());
    	visitGenericArgumentValues(cas.getGenericArgumentValues());
    }
        

    通过上述BeanFactoryPostProcessor的处理,BeanDefinition中的属性的值若包含占位符,将被替换为属性源中对应的属性值,当然这只是解析了bean配置中的属性,如:

    <bean id="helloService" class="service.HelloService" lazy-init="true" autowire="byType">    
     	<property name="serviceName" value="${serviceName}"/>
     </bean>
        

    而对于通过注解方式定义的属性,将会AutowiredAnnotationBeanPostProcessor注解处理器中的postProcessPropertyValues方法中注入,如:

    @Value("${appName}")
    private String appName;
    
    // AbstractBeanFactory.resolveEmbeddedValue()
    public String resolveEmbeddedValue(String value) {
    	String result = value;
    	for (StringValueResolver resolver : this.embeddedValueResolvers) {
    		if (result == null) {
    			return null;
    		}
    		// 利用属性源解析占位符属性
    		result = resolver.resolveStringValue(result);
    	}
    	return result;
    }
        

    除了通过PropertyPlaceholderConfigurer注入属性,还可以通过Spring提供的util标签达到该目的:

    <?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"
           xmlns:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="
    		http://www.springframework.org/schema/beans
    		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    		http://www.springframework.org/schema/context
    		http://www.springframework.org/schema/context/spring-context-3.2.xsd
    		http://www.springframework.org/schema/util
    		http://www.springframework.org/schema/context/spring-util-3.2.xsd" profile="dev">    
    
    	...
    
    	<util:properties id="app" location="app.properties" />
    
    	...
    </beans>    
        
    // app.properties
    mode=dev
        
    @Value(value = "#{app.mode}")
    private String mode;
        

    PropertyPlaceholderConfigurer不同,上面这种util:properties方式会在容器中对应注册一个名称为app的PropertiesFactoryBean,最终在注入mode字段时会通过SPEL表达式来解析。

    以上则是Spring运行时环境相关的一些细节,其通过profile机制使得开发人员可以在Spring容器中隔离不同的配置,和通过属性源定制,能在程序中注入各种配置的属性,甚至通过定制PropertyPlaceholderConfigurer,可以实现类似中心化配置的功能。

好人,一生平安。