001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.core.env; 018 019import org.springframework.core.convert.ConversionException; 020import org.springframework.util.ClassUtils; 021 022/** 023 * {@link PropertyResolver} implementation that resolves property values against 024 * an underlying set of {@link PropertySources}. 025 * 026 * @author Chris Beams 027 * @author Juergen Hoeller 028 * @since 3.1 029 * @see PropertySource 030 * @see PropertySources 031 * @see AbstractEnvironment 032 */ 033public class PropertySourcesPropertyResolver extends AbstractPropertyResolver { 034 035 private final PropertySources propertySources; 036 037 038 /** 039 * Create a new resolver against the given property sources. 040 * @param propertySources the set of {@link PropertySource} objects to use 041 */ 042 public PropertySourcesPropertyResolver(PropertySources propertySources) { 043 this.propertySources = propertySources; 044 } 045 046 047 @Override 048 public boolean containsProperty(String key) { 049 if (this.propertySources != null) { 050 for (PropertySource<?> propertySource : this.propertySources) { 051 if (propertySource.containsProperty(key)) { 052 return true; 053 } 054 } 055 } 056 return false; 057 } 058 059 @Override 060 public String getProperty(String key) { 061 return getProperty(key, String.class, true); 062 } 063 064 @Override 065 public <T> T getProperty(String key, Class<T> targetValueType) { 066 return getProperty(key, targetValueType, true); 067 } 068 069 @Override 070 protected String getPropertyAsRawString(String key) { 071 return getProperty(key, String.class, false); 072 } 073 074 protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) { 075 if (this.propertySources != null) { 076 for (PropertySource<?> propertySource : this.propertySources) { 077 if (logger.isTraceEnabled()) { 078 logger.trace("Searching for key '" + key + "' in PropertySource '" + 079 propertySource.getName() + "'"); 080 } 081 Object value = propertySource.getProperty(key); 082 if (value != null) { 083 if (resolveNestedPlaceholders && value instanceof String) { 084 value = resolveNestedPlaceholders((String) value); 085 } 086 logKeyFound(key, propertySource, value); 087 return convertValueIfNecessary(value, targetValueType); 088 } 089 } 090 } 091 if (logger.isDebugEnabled()) { 092 logger.debug("Could not find key '" + key + "' in any property source"); 093 } 094 return null; 095 } 096 097 @Override 098 @Deprecated 099 public <T> Class<T> getPropertyAsClass(String key, Class<T> targetValueType) { 100 if (this.propertySources != null) { 101 for (PropertySource<?> propertySource : this.propertySources) { 102 if (logger.isTraceEnabled()) { 103 logger.trace(String.format("Searching for key '%s' in [%s]", key, propertySource.getName())); 104 } 105 Object value = propertySource.getProperty(key); 106 if (value != null) { 107 logKeyFound(key, propertySource, value); 108 Class<?> clazz; 109 if (value instanceof String) { 110 try { 111 clazz = ClassUtils.forName((String) value, null); 112 } 113 catch (Exception ex) { 114 throw new ClassConversionException((String) value, targetValueType, ex); 115 } 116 } 117 else if (value instanceof Class) { 118 clazz = (Class<?>) value; 119 } 120 else { 121 clazz = value.getClass(); 122 } 123 if (!targetValueType.isAssignableFrom(clazz)) { 124 throw new ClassConversionException(clazz, targetValueType); 125 } 126 @SuppressWarnings("unchecked") 127 Class<T> targetClass = (Class<T>) clazz; 128 return targetClass; 129 } 130 } 131 } 132 if (logger.isDebugEnabled()) { 133 logger.debug(String.format("Could not find key '%s' in any property source", key)); 134 } 135 return null; 136 } 137 138 /** 139 * Log the given key as found in the given {@link PropertySource}, resulting in 140 * the given value. 141 * <p>The default implementation writes a debug log message with key and source. 142 * As of 4.3.3, this does not log the value anymore in order to avoid accidental 143 * logging of sensitive settings. Subclasses may override this method to change 144 * the log level and/or log message, including the property's value if desired. 145 * @param key the key found 146 * @param propertySource the {@code PropertySource} that the key has been found in 147 * @param value the corresponding value 148 * @since 4.3.1 149 */ 150 protected void logKeyFound(String key, PropertySource<?> propertySource, Object value) { 151 if (logger.isDebugEnabled()) { 152 logger.debug("Found key '" + key + "' in PropertySource '" + propertySource.getName() + 153 "' with value of type " + value.getClass().getSimpleName()); 154 } 155 } 156 157 158 @SuppressWarnings("serial") 159 @Deprecated 160 private static class ClassConversionException extends ConversionException { 161 162 public ClassConversionException(Class<?> actual, Class<?> expected) { 163 super(String.format("Actual type %s is not assignable to expected type %s", 164 actual.getName(), expected.getName())); 165 } 166 167 public ClassConversionException(String actual, Class<?> expected, Exception ex) { 168 super(String.format("Could not find/load class %s during attempt to convert to %s", 169 actual, expected.getName()), ex); 170 } 171 } 172 173}