001/* 002 * Copyright 2002-2020 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; 018 019import java.beans.PropertyDescriptor; 020import java.lang.reflect.Method; 021import java.security.AccessControlContext; 022import java.security.AccessController; 023import java.security.PrivilegedAction; 024import java.security.PrivilegedActionException; 025import java.security.PrivilegedExceptionAction; 026 027import org.springframework.core.ResolvableType; 028import org.springframework.core.convert.Property; 029import org.springframework.core.convert.TypeDescriptor; 030import org.springframework.lang.Nullable; 031import org.springframework.util.ReflectionUtils; 032 033/** 034 * Default {@link BeanWrapper} implementation that should be sufficient 035 * for all typical use cases. Caches introspection results for efficiency. 036 * 037 * <p>Note: Auto-registers default property editors from the 038 * {@code org.springframework.beans.propertyeditors} package, which apply 039 * in addition to the JDK's standard PropertyEditors. Applications can call 040 * the {@link #registerCustomEditor(Class, java.beans.PropertyEditor)} method 041 * to register an editor for a particular instance (i.e. they are not shared 042 * across the application). See the base class 043 * {@link PropertyEditorRegistrySupport} for details. 044 * 045 * <p><b>NOTE: As of Spring 2.5, this is - for almost all purposes - an 046 * internal class.</b> It is just public in order to allow for access from 047 * other framework packages. For standard application access purposes, use the 048 * {@link PropertyAccessorFactory#forBeanPropertyAccess} factory method instead. 049 * 050 * @author Rod Johnson 051 * @author Juergen Hoeller 052 * @author Rob Harrop 053 * @author Stephane Nicoll 054 * @since 15 April 2001 055 * @see #registerCustomEditor 056 * @see #setPropertyValues 057 * @see #setPropertyValue 058 * @see #getPropertyValue 059 * @see #getPropertyType 060 * @see BeanWrapper 061 * @see PropertyEditorRegistrySupport 062 */ 063public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper { 064 065 /** 066 * Cached introspections results for this object, to prevent encountering 067 * the cost of JavaBeans introspection every time. 068 */ 069 @Nullable 070 private CachedIntrospectionResults cachedIntrospectionResults; 071 072 /** 073 * The security context used for invoking the property methods. 074 */ 075 @Nullable 076 private AccessControlContext acc; 077 078 079 /** 080 * Create a new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards. 081 * Registers default editors. 082 * @see #setWrappedInstance 083 */ 084 public BeanWrapperImpl() { 085 this(true); 086 } 087 088 /** 089 * Create a new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards. 090 * @param registerDefaultEditors whether to register default editors 091 * (can be suppressed if the BeanWrapper won't need any type conversion) 092 * @see #setWrappedInstance 093 */ 094 public BeanWrapperImpl(boolean registerDefaultEditors) { 095 super(registerDefaultEditors); 096 } 097 098 /** 099 * Create a new BeanWrapperImpl for the given object. 100 * @param object the object wrapped by this BeanWrapper 101 */ 102 public BeanWrapperImpl(Object object) { 103 super(object); 104 } 105 106 /** 107 * Create a new BeanWrapperImpl, wrapping a new instance of the specified class. 108 * @param clazz class to instantiate and wrap 109 */ 110 public BeanWrapperImpl(Class<?> clazz) { 111 super(clazz); 112 } 113 114 /** 115 * Create a new BeanWrapperImpl for the given object, 116 * registering a nested path that the object is in. 117 * @param object the object wrapped by this BeanWrapper 118 * @param nestedPath the nested path of the object 119 * @param rootObject the root object at the top of the path 120 */ 121 public BeanWrapperImpl(Object object, String nestedPath, Object rootObject) { 122 super(object, nestedPath, rootObject); 123 } 124 125 /** 126 * Create a new BeanWrapperImpl for the given object, 127 * registering a nested path that the object is in. 128 * @param object the object wrapped by this BeanWrapper 129 * @param nestedPath the nested path of the object 130 * @param parent the containing BeanWrapper (must not be {@code null}) 131 */ 132 private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl parent) { 133 super(object, nestedPath, parent); 134 setSecurityContext(parent.acc); 135 } 136 137 138 /** 139 * Set a bean instance to hold, without any unwrapping of {@link java.util.Optional}. 140 * @param object the actual target object 141 * @since 4.3 142 * @see #setWrappedInstance(Object) 143 */ 144 public void setBeanInstance(Object object) { 145 this.wrappedObject = object; 146 this.rootObject = object; 147 this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject); 148 setIntrospectionClass(object.getClass()); 149 } 150 151 @Override 152 public void setWrappedInstance(Object object, @Nullable String nestedPath, @Nullable Object rootObject) { 153 super.setWrappedInstance(object, nestedPath, rootObject); 154 setIntrospectionClass(getWrappedClass()); 155 } 156 157 /** 158 * Set the class to introspect. 159 * Needs to be called when the target object changes. 160 * @param clazz the class to introspect 161 */ 162 protected void setIntrospectionClass(Class<?> clazz) { 163 if (this.cachedIntrospectionResults != null && this.cachedIntrospectionResults.getBeanClass() != clazz) { 164 this.cachedIntrospectionResults = null; 165 } 166 } 167 168 /** 169 * Obtain a lazily initialized CachedIntrospectionResults instance 170 * for the wrapped object. 171 */ 172 private CachedIntrospectionResults getCachedIntrospectionResults() { 173 if (this.cachedIntrospectionResults == null) { 174 this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass()); 175 } 176 return this.cachedIntrospectionResults; 177 } 178 179 /** 180 * Set the security context used during the invocation of the wrapped instance methods. 181 * Can be null. 182 */ 183 public void setSecurityContext(@Nullable AccessControlContext acc) { 184 this.acc = acc; 185 } 186 187 /** 188 * Return the security context used during the invocation of the wrapped instance methods. 189 * Can be null. 190 */ 191 @Nullable 192 public AccessControlContext getSecurityContext() { 193 return this.acc; 194 } 195 196 197 /** 198 * Convert the given value for the specified property to the latter's type. 199 * <p>This method is only intended for optimizations in a BeanFactory. 200 * Use the {@code convertIfNecessary} methods for programmatic conversion. 201 * @param value the value to convert 202 * @param propertyName the target property 203 * (note that nested or indexed properties are not supported here) 204 * @return the new value, possibly the result of type conversion 205 * @throws TypeMismatchException if type conversion failed 206 */ 207 @Nullable 208 public Object convertForProperty(@Nullable Object value, String propertyName) throws TypeMismatchException { 209 CachedIntrospectionResults cachedIntrospectionResults = getCachedIntrospectionResults(); 210 PropertyDescriptor pd = cachedIntrospectionResults.getPropertyDescriptor(propertyName); 211 if (pd == null) { 212 throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName, 213 "No property '" + propertyName + "' found"); 214 } 215 TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd); 216 if (td == null) { 217 td = cachedIntrospectionResults.addTypeDescriptor(pd, new TypeDescriptor(property(pd))); 218 } 219 return convertForProperty(propertyName, null, value, td); 220 } 221 222 private Property property(PropertyDescriptor pd) { 223 GenericTypeAwarePropertyDescriptor gpd = (GenericTypeAwarePropertyDescriptor) pd; 224 return new Property(gpd.getBeanClass(), gpd.getReadMethod(), gpd.getWriteMethod(), gpd.getName()); 225 } 226 227 @Override 228 @Nullable 229 protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) { 230 PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName); 231 return (pd != null ? new BeanPropertyHandler(pd) : null); 232 } 233 234 @Override 235 protected BeanWrapperImpl newNestedPropertyAccessor(Object object, String nestedPath) { 236 return new BeanWrapperImpl(object, nestedPath, this); 237 } 238 239 @Override 240 protected NotWritablePropertyException createNotWritablePropertyException(String propertyName) { 241 PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass()); 242 throw new NotWritablePropertyException(getRootClass(), getNestedPath() + propertyName, 243 matches.buildErrorMessage(), matches.getPossibleMatches()); 244 } 245 246 @Override 247 public PropertyDescriptor[] getPropertyDescriptors() { 248 return getCachedIntrospectionResults().getPropertyDescriptors(); 249 } 250 251 @Override 252 public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException { 253 BeanWrapperImpl nestedBw = (BeanWrapperImpl) getPropertyAccessorForPropertyPath(propertyName); 254 String finalPath = getFinalPath(nestedBw, propertyName); 255 PropertyDescriptor pd = nestedBw.getCachedIntrospectionResults().getPropertyDescriptor(finalPath); 256 if (pd == null) { 257 throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName, 258 "No property '" + propertyName + "' found"); 259 } 260 return pd; 261 } 262 263 264 private class BeanPropertyHandler extends PropertyHandler { 265 266 private final PropertyDescriptor pd; 267 268 public BeanPropertyHandler(PropertyDescriptor pd) { 269 super(pd.getPropertyType(), pd.getReadMethod() != null, pd.getWriteMethod() != null); 270 this.pd = pd; 271 } 272 273 @Override 274 public ResolvableType getResolvableType() { 275 return ResolvableType.forMethodReturnType(this.pd.getReadMethod()); 276 } 277 278 @Override 279 public TypeDescriptor toTypeDescriptor() { 280 return new TypeDescriptor(property(this.pd)); 281 } 282 283 @Override 284 @Nullable 285 public TypeDescriptor nested(int level) { 286 return TypeDescriptor.nested(property(this.pd), level); 287 } 288 289 @Override 290 @Nullable 291 public Object getValue() throws Exception { 292 Method readMethod = this.pd.getReadMethod(); 293 if (System.getSecurityManager() != null) { 294 AccessController.doPrivileged((PrivilegedAction<Object>) () -> { 295 ReflectionUtils.makeAccessible(readMethod); 296 return null; 297 }); 298 try { 299 return AccessController.doPrivileged((PrivilegedExceptionAction<Object>) 300 () -> readMethod.invoke(getWrappedInstance(), (Object[]) null), acc); 301 } 302 catch (PrivilegedActionException pae) { 303 throw pae.getException(); 304 } 305 } 306 else { 307 ReflectionUtils.makeAccessible(readMethod); 308 return readMethod.invoke(getWrappedInstance(), (Object[]) null); 309 } 310 } 311 312 @Override 313 public void setValue(@Nullable Object value) throws Exception { Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ? 315 ((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() : 316 this.pd.getWriteMethod()); 317 if (System.getSecurityManager() != null) { 318 AccessController.doPrivileged((PrivilegedAction<Object>) () -> { 319 ReflectionUtils.makeAccessible(writeMethod); 320 return null; 321 }); 322 try { 323 AccessController.doPrivileged((PrivilegedExceptionAction<Object>) 324 () -> writeMethod.invoke(getWrappedInstance(), value), acc); 325 } 326 catch (PrivilegedActionException ex) { 327 throw ex.getException(); 328 } 329 } 330 else { 331 ReflectionUtils.makeAccessible(writeMethod); 332 writeMethod.invoke(getWrappedInstance(), value); 333 } 334 } 335 } 336 337}