赞
踩
目录
2.PropertySourcesPlaceholderConfigurer实现bean工厂后处理器
3.PropertyPlaceholderHelper帮助类解析逻辑
在java spring项目中,我们经常使用到${}占位符实现属性值的获取,最常见的就是使用@Value("${propsKey}")的方式完成bean属性值的注入,如下:
- @Value("${appkey}")
- private String appkey;
下面重点分析一下spring中占位符解析的源码实现;
PropertySourcesPlaceholderConfigurer:从environment(包括properties文件以及注解PropertySource解析的属性)以及本地加载属性中解析占位符
PropertyPlaceholderConfigurer:从Properties和系统属性中解析占位符
目前,Spring容器中默认初始化的是PropertySourcesPlaceholderConfigurer
2个类具体的类继承层次如下:
可以看出,占位符解析类都是继承BeanFactoryPostProcessor bean工厂后处理器,作用就是对BeanDefinition中的占位符${}进行解析;
下面看一下PropertySourcesPlaceholderConfigurer具体的占位符解析过程;
具体实现逻辑如下:
- /**
- * 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
- @Nullable
- 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;
- }
这里看到主要分为三步:
构造属性源,主要包含environment以及本地加载的属性
构造属性解析器PropertySourcesPropertyResolver
解析beanDefinition中的占位符
PropertySourcesPropertyResolver根据propertySources查找指定key的属性,属性值可能也包含了占位符,这里对属性值再次进行了一次占位符解析,实现逻辑如下:
- @Nullable
- protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
- if (this.propertySources != null) {
- for (PropertySource<?> propertySource : this.propertySources) {
- if (logger.isTraceEnabled()) {
- logger.trace("Searching for key '" + key + "' in PropertySource '" +
- propertySource.getName() + "'");
- }
- Object value = propertySource.getProperty(key);
- if (value != null) {
- if (resolveNestedPlaceholders && value instanceof String) {
- value = resolveNestedPlaceholders((String) value);
- }
- logKeyFound(key, propertySource, value);
- return convertValueIfNecessary(value, targetValueType);
- }
- }
- }
- if (logger.isTraceEnabled()) {
- logger.trace("Could not find key '" + key + "' in any property source");
- }
- return null;
- }
解析beanDefinition中的占位符主要用到了BeanDefinitionVisitor对象:
- 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);
- }
其中visitBeanDefinition方法对BeanDefinition中的属性进行占位符解析,如下:
- /**
- * Traverse the given BeanDefinition object and the MutablePropertyValues
- * and ConstructorArgumentValues contained in them.
- * @param beanDefinition the BeanDefinition object to traverse
- * @see #resolveStringValue(String)
- */
- public void visitBeanDefinition(BeanDefinition beanDefinition) {
- visitParentName(beanDefinition);
- visitBeanClassName(beanDefinition);
- visitFactoryBeanName(beanDefinition);
- visitFactoryMethodName(beanDefinition);
- visitScope(beanDefinition);
- if (beanDefinition.hasPropertyValues()) {
- visitPropertyValues(beanDefinition.getPropertyValues());
- }
- if (beanDefinition.hasConstructorArgumentValues()) {
- ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
- visitIndexedArgumentValues(cas.getIndexedArgumentValues());
- visitGenericArgumentValues(cas.getGenericArgumentValues());
- }
- }
- /**
- * Resolve the given String value, for example parsing placeholders.
- * @param strVal the original String value
- * @return the resolved String value
- */
- @Nullable
- protected String resolveStringValue(String strVal) {
- if (this.valueResolver == null) {
- throw new IllegalStateException("No StringValueResolver specified - pass a resolver " +
- "object into the constructor or override the 'resolveStringValue' method");
- }
- String resolvedValue = this.valueResolver.resolveStringValue(strVal);
- // Return original String if not modified.
- return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
- }
默认的占位符前后缀分别为${ 和 } ,比较核心的字符串解析逻辑都在PropertyPlaceholderHelper的方法parseStringValue中,如下:
- protected String parseStringValue(
- String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
-
- int startIndex = value.indexOf(this.placeholderPrefix);
- if (startIndex == -1) {
- return value;
- }
-
- StringBuilder result = new StringBuilder(value);
- while (startIndex != -1) {
- int endIndex = findPlaceholderEndIndex(result, startIndex);
- if (endIndex != -1) {
- String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
- String originalPlaceholder = placeholder;
- if (visitedPlaceholders == null) {
- visitedPlaceholders = new HashSet<>(4);
- }
- if (!visitedPlaceholders.add(originalPlaceholder)) {
- throw new IllegalArgumentException(
- "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
- }
- // Recursive invocation, parsing placeholders contained in the placeholder key.
- placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
- // Now obtain the value for the fully resolved key...
- String propVal = placeholderResolver.resolvePlaceholder(placeholder);
- if (propVal == null && this.valueSeparator != null) {
- int separatorIndex = placeholder.indexOf(this.valueSeparator);
- if (separatorIndex != -1) {
- String actualPlaceholder = placeholder.substring(0, separatorIndex);
- String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
- propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
- if (propVal == null) {
- propVal = defaultValue;
- }
- }
- }
- if (propVal != null) {
- // Recursive invocation, parsing placeholders contained in the
- // previously resolved placeholder value.
- propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
- result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
- if (logger.isTraceEnabled()) {
- logger.trace("Resolved placeholder '" + placeholder + "'");
- }
- startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
- }
- else if (this.ignoreUnresolvablePlaceholders) {
- // Proceed with unprocessed value.
- startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
- }
- else {
- throw new IllegalArgumentException("Could not resolve placeholder '" +
- placeholder + "'" + " in value \"" + value + "\"");
- }
- visitedPlaceholders.remove(originalPlaceholder);
- }
- else {
- startIndex = -1;
- }
- }
- return result.toString();
- }
这里自顶而下对占位符进行解析,支持占位符的嵌套,同时能够检测占位符间的循环引用;
对解析出来的占位符,再利用PlaceholderResolver对占位符key进行解析和字符串替换;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。