spring的启动过程04.1-value注解替换过程
2017-03-03 10:06
435 查看
概述:
在上篇《 spring的启动过程04-bean后置处理器》文章中讲解了bean后置处理器的原理,这篇文章结合具体的处理器讲解spring@Value注解的处理过程。
spring容器会在两种场景用到properties文件的属性值,第一种替换XML文件中的占位符详情请查阅《spring的启动过程03.1-占位符替换过程-xml配置的参数》,第二种就是业务代码中采用@Value注解方式。
原理:
spring框架采用bean后置处理器来拦截bean属性值的注入,先看下AutowiredAnnotationBeanPostProcessor的静态类图。
核心方法触发点:
看下 属性替换的整个流程:
流程中用到了StringValueResolver类最终解析属性值,看下该类何时注入到bean工厂中
看下创建处理器的代码
1. PropertySourcesPlaceholderConfigurer
看下PropertyPlaceholderConfigurer类处理器调用的核心代码
看下PropertySourcesPlaceholderConfigurer类处理器调用的核心代码
客户端使用引入方式:
第一种:
该种方式默认的处理类为:org.springframework.context.support.PropertySourcesPlaceholderConfigurer
设置属性localOverride为true则location参数的优先级要大于系统变量。默认为false
第二种:
设置属性systemPropertiesMode为2标识系统参数优先级最高。默认为1
来看下上述两种方式参数的来源,从源码中可以看出优先级。
1. PropertyPlaceholderConfigurer
/**
* {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and
* {@linkplain #processProperties process} properties against the given bean factory.
* @throws BeanInitializationException if any properties cannot be loaded
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
Properties mergedProps = mergeProperties();
// Convert the merged properties, if necessary.
convertProperties(mergedProps);
// Let the subclass process the properties.
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}2. PropertySourcesPlaceholderConfigurer
@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;
}
总结:
spring通过bean工厂后置处理器处理beanDefinition对象中属性值占位符的替换功能并注册@value注解处理器到bean工厂中。在装饰bean属性的过程中会触发bean后置处理器处理@value注解并设置属性值,@value注解处理器从bean工厂中获得。
在上篇《 spring的启动过程04-bean后置处理器》文章中讲解了bean后置处理器的原理,这篇文章结合具体的处理器讲解spring@Value注解的处理过程。
spring容器会在两种场景用到properties文件的属性值,第一种替换XML文件中的占位符详情请查阅《spring的启动过程03.1-占位符替换过程-xml配置的参数》,第二种就是业务代码中采用@Value注解方式。
@Value(value = "${alias}") private String alias; @Value(value = "${password}") private String password;
原理:
spring框架采用bean后置处理器来拦截bean属性值的注入,先看下AutowiredAnnotationBeanPostProcessor的静态类图。
核心方法触发点:
/** * 创建Bean过程中设置属性值 * Populate the bean instance in the given BeanWrapper with the property values * from the bean definition. * @param beanName the name of the bean * @param mbd the bean definition for the bean * @param bw BeanWrapper with bean instance */ protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { PropertyValues pvs = mbd.getPropertyValues(); ..... boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE); if (hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); } } applyPropertyValues(beanName, mbd, bw, pvs); }遍历所有InstantiationAwareBeanPostProcessor实例设置属性字段值。
看下 属性替换的整个流程:
流程中用到了StringValueResolver类最终解析属性值,看下该类何时注入到bean工厂中
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); // N afe4 ew in Spring 3.0: resolve placeholders in embedded values such as annotation attributes. // 把value注解处理器加入到bean工厂,该处理器解析value注解注入值 beanFactoryToProcess.addEmbeddedValueResolver(valueResolver); }spring在执行bean工厂后置处理器的过程中会创建value标签解析器。负责创建该解析器的主要有
看下创建处理器的代码
1. PropertySourcesPlaceholderConfigurer
/** * Visit each bean definition in the given bean factory and attempt to replace ${...} property * placeholders with values from the given properties. */ @Override protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException { StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props); doProcessProperties(beanFactoryToProcess, valueResolver); }2. PropertySourcesPlaceholderConfigurer
/** * 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); }
看下PropertyPlaceholderConfigurer类处理器调用的核心代码
/** * Resolve the given placeholder using the given properties, performing * a system properties check according to the given mode. * <p>The default implementation delegates to {@code resolvePlaceholder * (placeholder, props)} before/after the system properties check. * <p>Subclasses can override this for custom resolution strategies, * including customized points for the system properties check. * @param placeholder the placeholder to resolve * @param props the merged properties of this configurer * @param systemPropertiesMode the system properties mode, * according to the constants in this class * @return the resolved value, of null if none * @see #setSystemPropertiesMode * @see System#getProperty * @see #resolvePlaceholder(String, java.util.Properties) */ protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) { String propVal = null; // 默认采用locations配置的文件属性 if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) { // 加载系统参数 propVal = resolveSystemProperty(placeholder); } if (propVal == null) { // 从locations中加载参数 propVal = resolvePlaceholder(placeholder, props); } if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) { // 如果从locations中没有发现参数,加载系统参数 propVal = resolveSystemProperty(placeholder); } return propVal; }
/** * Resolve the given key as JVM system property, and optionally also as * system environment variable if no matching system property has been found. * @param key the placeholder to resolve as system property key * @return the system property value, or {@code null} if not found * @see #setSearchSystemEnvironment * @see System#getProperty(String) * @see System#getenv(String) */ protected String resolveSystemProperty(String key) { try { String value = System.getProperty(key); if (value == null && this.searchSystemEnvironment) { value = System.getenv(key); } return value; } catch (Throwable ex) { return null; } }
看下PropertySourcesPlaceholderConfigurer类处理器调用的核心代码
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) { if (this.propertySources != null) { for (PropertySource<?> propertySource : this.propertySources) { if (debugEnabled) { logger.debug(String.format("Searching for key '%s' in [%s]", key, propertySource.getName())); } //获取参数值,参数值为env和配置的locations文件中的属性值 Object value = propertySource.getProperty(key); if (value != null) { Class<?> valueType = value.getClass(); if (resolveNestedPlaceholders && value instanceof String) { value = resolveNestedPlaceholders((String) value); } if (!this.conversionService.canConvert(valueType, targetValueType)) { } return this.conversionService.convert(value, targetValueType); } } } return null; }
客户端使用引入方式:
第一种:
<context:property-placeholder location="classpath:remote.properties" ignore-resource-not-found="true" ignore-unresolvable="true" />
该种方式默认的处理类为:org.springframework.context.support.PropertySourcesPlaceholderConfigurer
设置属性localOverride为true则location参数的优先级要大于系统变量。默认为false
第二种:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="ignoreUnresolvablePlaceholders" value="true" /> <property name="ignoreResourceNotFound" value="true" /> <property name="locations"> <list> <value>classpath:conf/*.properties</value> </list> </property> <property name="fileEncoding" value="UTF-8" /> <property name="order" value="-128" /> </bean>
设置属性systemPropertiesMode为2标识系统参数优先级最高。默认为1
来看下上述两种方式参数的来源,从源码中可以看出优先级。
1. PropertyPlaceholderConfigurer
/**
* {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and
* {@linkplain #processProperties process} properties against the given bean factory.
* @throws BeanInitializationException if any properties cannot be loaded
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
Properties mergedProps = mergeProperties();
// Convert the merged properties, if necessary.
convertProperties(mergedProps);
// Let the subclass process the properties.
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}2. PropertySourcesPlaceholderConfigurer
@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;
}
总结:
spring通过bean工厂后置处理器处理beanDefinition对象中属性值占位符的替换功能并注册@value注解处理器到bean工厂中。在装饰bean属性的过程中会触发bean后置处理器处理@value注解并设置属性值,@value注解处理器从bean工厂中获得。
相关文章推荐
- Spring启动后扫描解析注解的过程
- spring boot注解及启动过程
- 详解spring注解配置启动过程
- spring的启动过程03.1-占位符替换过程-xml配置的参数
- 详解spring注解配置启动过程_java
- spring启动过程之源码跟踪(续beanfactory)--spring Debug
- spring启动过程之源码跟踪(下)--spring Debug
- spring启动过程之源码跟踪(上)--spring Debug
- spring启动过程之源码跟踪(中)--spring Debug
- flex4 + spring + blazeds , 使用anonation(注解)机制,利用push技术的实现例子和过程。
- Spring3中@Value注解的使用
- flex4 + spring + blazeds , 使用anonation(注解)机制,利用push技术的实现例子和过程。
- 杂(关于spring容器启动时加载过程的探索)
- spring启动component-scan类扫描加载过程---源码分析
- spring 启动后执行自我操作(如加入数据进入缓存) 基于注解
- spring 使用注解启动定时器不执行
- 如何插手spring启动过程
- ubuntu 启动过程注解
- spring启动过程之源码跟踪(续beanfactory)--spring Debug