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.lang.reflect.Field; 020import java.util.HashMap; 021import java.util.Map; 022 023import org.springframework.core.ResolvableType; 024import org.springframework.core.convert.TypeDescriptor; 025import org.springframework.util.ReflectionUtils; 026 027/** 028 * {@link ConfigurablePropertyAccessor} implementation that directly accesses 029 * instance fields. Allows for direct binding to fields instead of going through 030 * JavaBean setters. 031 * 032 * <p>As of Spring 4.2, the vast majority of the {@link BeanWrapper} features have 033 * been merged to {@link AbstractPropertyAccessor}, which means that property 034 * traversal as well as collections and map access is now supported here as well. 035 * 036 * <p>A DirectFieldAccessor's default for the "extractOldValueForEditor" setting 037 * is "true", since a field can always be read without side effects. 038 * 039 * @author Juergen Hoeller 040 * @author Stephane Nicoll 041 * @since 2.0 042 * @see #setExtractOldValueForEditor 043 * @see BeanWrapper 044 * @see org.springframework.validation.DirectFieldBindingResult 045 * @see org.springframework.validation.DataBinder#initDirectFieldAccess() 046 */ 047public class DirectFieldAccessor extends AbstractNestablePropertyAccessor { 048 049 private final Map<String, FieldPropertyHandler> fieldMap = new HashMap<String, FieldPropertyHandler>(); 050 051 052 /** 053 * Create a new DirectFieldAccessor for the given object. 054 * @param object the object wrapped by this DirectFieldAccessor 055 */ 056 public DirectFieldAccessor(Object object) { 057 super(object); 058 } 059 060 /** 061 * Create a new DirectFieldAccessor for the given object, 062 * registering a nested path that the object is in. 063 * @param object the object wrapped by this DirectFieldAccessor 064 * @param nestedPath the nested path of the object 065 * @param parent the containing DirectFieldAccessor (must not be {@code null}) 066 */ 067 protected DirectFieldAccessor(Object object, String nestedPath, DirectFieldAccessor parent) { 068 super(object, nestedPath, parent); 069 } 070 071 072 @Override 073 protected FieldPropertyHandler getLocalPropertyHandler(String propertyName) { 074 FieldPropertyHandler propertyHandler = this.fieldMap.get(propertyName); 075 if (propertyHandler == null) { 076 Field field = ReflectionUtils.findField(getWrappedClass(), propertyName); 077 if (field != null) { 078 propertyHandler = new FieldPropertyHandler(field); 079 this.fieldMap.put(propertyName, propertyHandler); 080 } 081 } 082 return propertyHandler; 083 } 084 085 @Override 086 protected DirectFieldAccessor newNestedPropertyAccessor(Object object, String nestedPath) { 087 return new DirectFieldAccessor(object, nestedPath, this); 088 } 089 090 @Override 091 protected NotWritablePropertyException createNotWritablePropertyException(String propertyName) { 092 PropertyMatches matches = PropertyMatches.forField(propertyName, getRootClass()); 093 throw new NotWritablePropertyException(getRootClass(), getNestedPath() + propertyName, 094 matches.buildErrorMessage(), matches.getPossibleMatches()); 095 } 096 097 098 private class FieldPropertyHandler extends PropertyHandler { 099 100 private final Field field; 101 102 public FieldPropertyHandler(Field field) { 103 super(field.getType(), true, true); 104 this.field = field; 105 } 106 107 @Override 108 public TypeDescriptor toTypeDescriptor() { 109 return new TypeDescriptor(this.field); 110 } 111 112 @Override 113 public ResolvableType getResolvableType() { 114 return ResolvableType.forField(this.field); 115 } 116 117 @Override 118 public TypeDescriptor nested(int level) { 119 return TypeDescriptor.nested(this.field, level); 120 } 121 122 @Override 123 public Object getValue() throws Exception { 124 try { 125 ReflectionUtils.makeAccessible(this.field); 126 return this.field.get(getWrappedInstance()); 127 } 128 129 catch (IllegalAccessException ex) { 130 throw new InvalidPropertyException(getWrappedClass(), 131 this.field.getName(), "Field is not accessible", ex); 132 } 133 } 134 135 @Override 136 public void setValue(Object object, Object value) throws Exception { 137 try { 138 ReflectionUtils.makeAccessible(this.field); 139 this.field.set(object, value); 140 } 141 catch (IllegalAccessException ex) { 142 throw new InvalidPropertyException(getWrappedClass(), this.field.getName(), 143 "Field is not accessible", ex); 144 } 145 } 146 } 147 148}