1 /* 2 * Copyright 2002-2011 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.beans.factory.config; 18 19 import java.util.LinkedHashMap; 20 import java.util.LinkedHashSet; 21 import java.util.List; 22 import java.util.Map; 23 import java.util.Set; 24 25 import org.springframework.beans.MutablePropertyValues; 26 import org.springframework.beans.PropertyValue; 27 import org.springframework.util.Assert; 28 import org.springframework.util.ObjectUtils; 29 import org.springframework.util.StringValueResolver; 30 31 /** 32 * Visitor class for traversing {@link BeanDefinition} objects, in particular 33 * the property values and constructor argument values contained in them, 34 * resolving bean metadata values. 35 * 36 * <p>Used by {@link PropertyPlaceholderConfigurer} to parse all String values 37 * contained in a BeanDefinition, resolving any placeholders found. 38 * 39 * @author Juergen Hoeller 40 * @author Sam Brannen 41 * @since 1.2 42 * @see BeanDefinition 43 * @see BeanDefinition#getPropertyValues 44 * @see BeanDefinition#getConstructorArgumentValues 45 * @see PropertyPlaceholderConfigurer 46 */ 47 public class BeanDefinitionVisitor { 48 49 private StringValueResolver valueResolver; 50 51 52 /** 53 * Create a new BeanDefinitionVisitor, applying the specified 54 * value resolver to all bean metadata values. 55 * @param valueResolver the StringValueResolver to apply 56 */ BeanDefinitionVisitor(StringValueResolver valueResolver)57 public BeanDefinitionVisitor(StringValueResolver valueResolver) { 58 Assert.notNull(valueResolver, "StringValueResolver must not be null"); 59 this.valueResolver = valueResolver; 60 } 61 62 /** 63 * Create a new BeanDefinitionVisitor for subclassing. 64 * Subclasses need to override the {@link #resolveStringValue} method. 65 */ BeanDefinitionVisitor()66 protected BeanDefinitionVisitor() { 67 } 68 69 70 /** 71 * Traverse the given BeanDefinition object and the MutablePropertyValues 72 * and ConstructorArgumentValues contained in them. 73 * @param beanDefinition the BeanDefinition object to traverse 74 * @see #resolveStringValue(String) 75 */ visitBeanDefinition(BeanDefinition beanDefinition)76 public void visitBeanDefinition(BeanDefinition beanDefinition) { 77 visitParentName(beanDefinition); 78 visitBeanClassName(beanDefinition); 79 visitFactoryBeanName(beanDefinition); 80 visitFactoryMethodName(beanDefinition); 81 visitScope(beanDefinition); 82 visitPropertyValues(beanDefinition.getPropertyValues()); 83 ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues(); 84 visitIndexedArgumentValues(cas.getIndexedArgumentValues()); 85 visitGenericArgumentValues(cas.getGenericArgumentValues()); 86 } 87 visitParentName(BeanDefinition beanDefinition)88 protected void visitParentName(BeanDefinition beanDefinition) { 89 String parentName = beanDefinition.getParentName(); 90 if (parentName != null) { 91 String resolvedName = resolveStringValue(parentName); 92 if (!parentName.equals(resolvedName)) { 93 beanDefinition.setParentName(resolvedName); 94 } 95 } 96 } 97 visitBeanClassName(BeanDefinition beanDefinition)98 protected void visitBeanClassName(BeanDefinition beanDefinition) { 99 String beanClassName = beanDefinition.getBeanClassName(); 100 if (beanClassName != null) { 101 String resolvedName = resolveStringValue(beanClassName); 102 if (!beanClassName.equals(resolvedName)) { 103 beanDefinition.setBeanClassName(resolvedName); 104 } 105 } 106 } 107 visitFactoryBeanName(BeanDefinition beanDefinition)108 protected void visitFactoryBeanName(BeanDefinition beanDefinition) { 109 String factoryBeanName = beanDefinition.getFactoryBeanName(); 110 if (factoryBeanName != null) { 111 String resolvedName = resolveStringValue(factoryBeanName); 112 if (!factoryBeanName.equals(resolvedName)) { 113 beanDefinition.setFactoryBeanName(resolvedName); 114 } 115 } 116 } 117 visitFactoryMethodName(BeanDefinition beanDefinition)118 protected void visitFactoryMethodName(BeanDefinition beanDefinition) { 119 String factoryMethodName = beanDefinition.getFactoryMethodName(); 120 if (factoryMethodName != null) { 121 String resolvedName = resolveStringValue(factoryMethodName); 122 if (!factoryMethodName.equals(resolvedName)) { 123 beanDefinition.setFactoryMethodName(resolvedName); 124 } 125 } 126 } 127 visitScope(BeanDefinition beanDefinition)128 protected void visitScope(BeanDefinition beanDefinition) { 129 String scope = beanDefinition.getScope(); 130 if (scope != null) { 131 String resolvedScope = resolveStringValue(scope); 132 if (!scope.equals(resolvedScope)) { 133 beanDefinition.setScope(resolvedScope); 134 } 135 } 136 } 137 visitPropertyValues(MutablePropertyValues pvs)138 protected void visitPropertyValues(MutablePropertyValues pvs) { 139 PropertyValue[] pvArray = pvs.getPropertyValues(); 140 for (PropertyValue pv : pvArray) { 141 Object newVal = resolveValue(pv.getValue()); 142 if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) { 143 pvs.add(pv.getName(), newVal); 144 } 145 } 146 } 147 visitIndexedArgumentValues(Map<Integer, ConstructorArgumentValues.ValueHolder> ias)148 protected void visitIndexedArgumentValues(Map<Integer, ConstructorArgumentValues.ValueHolder> ias) { 149 for (ConstructorArgumentValues.ValueHolder valueHolder : ias.values()) { 150 Object newVal = resolveValue(valueHolder.getValue()); 151 if (!ObjectUtils.nullSafeEquals(newVal, valueHolder.getValue())) { 152 valueHolder.setValue(newVal); 153 } 154 } 155 } 156 visitGenericArgumentValues(List<ConstructorArgumentValues.ValueHolder> gas)157 protected void visitGenericArgumentValues(List<ConstructorArgumentValues.ValueHolder> gas) { 158 for (ConstructorArgumentValues.ValueHolder valueHolder : gas) { 159 Object newVal = resolveValue(valueHolder.getValue()); 160 if (!ObjectUtils.nullSafeEquals(newVal, valueHolder.getValue())) { 161 valueHolder.setValue(newVal); 162 } 163 } 164 } 165 166 @SuppressWarnings("rawtypes") resolveValue(Object value)167 protected Object resolveValue(Object value) { 168 if (value instanceof BeanDefinition) { 169 visitBeanDefinition((BeanDefinition) value); 170 } 171 else if (value instanceof BeanDefinitionHolder) { 172 visitBeanDefinition(((BeanDefinitionHolder) value).getBeanDefinition()); 173 } 174 else if (value instanceof RuntimeBeanReference) { 175 RuntimeBeanReference ref = (RuntimeBeanReference) value; 176 String newBeanName = resolveStringValue(ref.getBeanName()); 177 if (!newBeanName.equals(ref.getBeanName())) { 178 return new RuntimeBeanReference(newBeanName); 179 } 180 } 181 else if (value instanceof RuntimeBeanNameReference) { 182 RuntimeBeanNameReference ref = (RuntimeBeanNameReference) value; 183 String newBeanName = resolveStringValue(ref.getBeanName()); 184 if (!newBeanName.equals(ref.getBeanName())) { 185 return new RuntimeBeanNameReference(newBeanName); 186 } 187 } 188 else if (value instanceof Object[]) { 189 visitArray((Object[]) value); 190 } 191 else if (value instanceof List) { 192 visitList((List) value); 193 } 194 else if (value instanceof Set) { 195 visitSet((Set) value); 196 } 197 else if (value instanceof Map) { 198 visitMap((Map) value); 199 } 200 else if (value instanceof TypedStringValue) { 201 TypedStringValue typedStringValue = (TypedStringValue) value; 202 String stringValue = typedStringValue.getValue(); 203 if (stringValue != null) { 204 String visitedString = resolveStringValue(stringValue); 205 typedStringValue.setValue(visitedString); 206 } 207 } 208 else if (value instanceof String) { 209 return resolveStringValue((String) value); 210 } 211 return value; 212 } 213 visitArray(Object[] arrayVal)214 protected void visitArray(Object[] arrayVal) { 215 for (int i = 0; i < arrayVal.length; i++) { 216 Object elem = arrayVal[i]; 217 Object newVal = resolveValue(elem); 218 if (!ObjectUtils.nullSafeEquals(newVal, elem)) { 219 arrayVal[i] = newVal; 220 } 221 } 222 } 223 224 @SuppressWarnings({"unchecked", "rawtypes"}) visitList(List listVal)225 protected void visitList(List listVal) { 226 for (int i = 0; i < listVal.size(); i++) { 227 Object elem = listVal.get(i); 228 Object newVal = resolveValue(elem); 229 if (!ObjectUtils.nullSafeEquals(newVal, elem)) { 230 listVal.set(i, newVal); 231 } 232 } 233 } 234 235 @SuppressWarnings({"unchecked", "rawtypes"}) visitSet(Set setVal)236 protected void visitSet(Set setVal) { 237 Set newContent = new LinkedHashSet(); 238 boolean entriesModified = false; 239 for (Object elem : setVal) { 240 int elemHash = (elem != null ? elem.hashCode() : 0); 241 Object newVal = resolveValue(elem); 242 int newValHash = (newVal != null ? newVal.hashCode() : 0); 243 newContent.add(newVal); 244 entriesModified = entriesModified || (newVal != elem || newValHash != elemHash); 245 } 246 if (entriesModified) { 247 setVal.clear(); 248 setVal.addAll(newContent); 249 } 250 } 251 252 @SuppressWarnings({"unchecked", "rawtypes"}) visitMap(Map<?, ?> mapVal)253 protected void visitMap(Map<?, ?> mapVal) { 254 Map newContent = new LinkedHashMap(); 255 boolean entriesModified = false; 256 for (Map.Entry entry : mapVal.entrySet()) { 257 Object key = entry.getKey(); 258 int keyHash = (key != null ? key.hashCode() : 0); 259 Object newKey = resolveValue(key); 260 int newKeyHash = (newKey != null ? newKey.hashCode() : 0); 261 Object val = entry.getValue(); 262 Object newVal = resolveValue(val); 263 newContent.put(newKey, newVal); 264 entriesModified = entriesModified || (newVal != val || newKey != key || newKeyHash != keyHash); 265 } 266 if (entriesModified) { 267 mapVal.clear(); 268 mapVal.putAll(newContent); 269 } 270 } 271 272 /** 273 * Resolve the given String value, for example parsing placeholders. 274 * @param strVal the original String value 275 * @return the resolved String value 276 */ resolveStringValue(String strVal)277 protected String resolveStringValue(String strVal) { 278 if (this.valueResolver == null) { 279 throw new IllegalStateException("No StringValueResolver specified - pass a resolver " + 280 "object into the constructor or override the 'resolveStringValue' method"); 281 } 282 String resolvedValue = this.valueResolver.resolveStringValue(strVal); 283 // Return original String if not modified. 284 return (strVal.equals(resolvedValue) ? strVal : resolvedValue); 285 } 286 287 } 288