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