001/* 002 * Copyright 2002-2011 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.beans.factory.config; 018 019import java.util.LinkedHashMap; 020import java.util.LinkedHashSet; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024 025import org.springframework.beans.MutablePropertyValues; 026import org.springframework.beans.PropertyValue; 027import org.springframework.util.Assert; 028import org.springframework.util.ObjectUtils; 029import org.springframework.util.StringValueResolver; 030 031/** 032 * Visitor class for traversing {@link BeanDefinition} objects, in particular 033 * the property values and constructor argument values contained in them, 034 * resolving bean metadata values. 035 * 036 * <p>Used by {@link PropertyPlaceholderConfigurer} to parse all String values 037 * contained in a BeanDefinition, resolving any placeholders found. 038 * 039 * @author Juergen Hoeller 040 * @author Sam Brannen 041 * @since 1.2 042 * @see BeanDefinition 043 * @see BeanDefinition#getPropertyValues 044 * @see BeanDefinition#getConstructorArgumentValues 045 * @see PropertyPlaceholderConfigurer 046 */ 047public class BeanDefinitionVisitor { 048 049 private StringValueResolver valueResolver; 050 051 052 /** 053 * Create a new BeanDefinitionVisitor, applying the specified 054 * value resolver to all bean metadata values. 055 * @param valueResolver the StringValueResolver to apply 056 */ 057 public BeanDefinitionVisitor(StringValueResolver valueResolver) { 058 Assert.notNull(valueResolver, "StringValueResolver must not be null"); 059 this.valueResolver = valueResolver; 060 } 061 062 /** 063 * Create a new BeanDefinitionVisitor for subclassing. 064 * Subclasses need to override the {@link #resolveStringValue} method. 065 */ 066 protected BeanDefinitionVisitor() { 067 } 068 069 070 /** 071 * Traverse the given BeanDefinition object and the MutablePropertyValues 072 * and ConstructorArgumentValues contained in them. 073 * @param beanDefinition the BeanDefinition object to traverse 074 * @see #resolveStringValue(String) 075 */ 076 public void visitBeanDefinition(BeanDefinition beanDefinition) { 077 visitParentName(beanDefinition); 078 visitBeanClassName(beanDefinition); 079 visitFactoryBeanName(beanDefinition); 080 visitFactoryMethodName(beanDefinition); 081 visitScope(beanDefinition); 082 visitPropertyValues(beanDefinition.getPropertyValues()); 083 ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues(); 084 visitIndexedArgumentValues(cas.getIndexedArgumentValues()); 085 visitGenericArgumentValues(cas.getGenericArgumentValues()); 086 } 087 088 protected void visitParentName(BeanDefinition beanDefinition) { 089 String parentName = beanDefinition.getParentName(); 090 if (parentName != null) { 091 String resolvedName = resolveStringValue(parentName); 092 if (!parentName.equals(resolvedName)) { 093 beanDefinition.setParentName(resolvedName); 094 } 095 } 096 } 097 098 protected void visitBeanClassName(BeanDefinition beanDefinition) { 099 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 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 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 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 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 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 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") 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 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"}) 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"}) 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"}) 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 */ 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}