javascript
聊聊 Spring 核心特性中的数据绑定 (DataBinder)
前面的話
Spring 的核心特性包括 IOC 容器、事件、資源管理、國際化、校驗、數據綁定、類型轉換、EL 表達式、AOP。其他特性可以輕易的在網絡上找到很多資料,而數據綁定這個特性即便在 Spring 官網描述卻也不太多。這是因為數據綁定主要應用于 Spring 內部,對于用戶而言直接使用的場景并不多。如果想要深入理解 Spring 內部的運行機制,數據綁定是必須了解的一塊內容。
理解數據綁定
數據綁定允許將用戶輸入的內容動態綁定到應用程序中的領域模型中。對于 Spring 而言,用于輸入具體的場景主要包括 xml 中 bean 的定義、web 環境下的請求參數。
數據綁定在 Spring 中的核心類是 org.springframework.validation.DataBinder,可以看到這個類位于 validation 包中,數據綁定時也往往伴隨著參數校驗。先看看如何手動使用這個類。
@Data public class LoginDTO {private String username;private String password; }public class App {public static void main(String[] args) {LoginDTO dto = new LoginDTO();Map<String, Object> input = new HashMap<>();input.put("username", "hkp");input.put("password", "123");PropertyValues propertyValues = new MutablePropertyValues(input);DataBinder dataBinder = new DataBinder(dto, "loginDTO");dataBinder.bind(propertyValues);// LoginDTO(username=hkp, password=123)System.out.println(dto);} }DataBinder#bind 是 DataBinder 類的核心方法,它接收 PropertyValues 類型的參數,并將參數中的數據設置到對象的屬性中。
PropertyValues
PropertyValues 可以簡單的理解為 Map,它包含更多的屬性信息。接口核心方法定義如下。
public interface PropertyValues extends Iterable<PropertyValue>PropertyValue[] getPropertyValues();PropertyValue getPropertyValue(String propertyName);boolean contains(String propertyName);boolean isEmpty(); }PropertyValues 繼承接口 Iterable,可以理解為 PropertyValue 的容器,PropertyValue 表示對象中的某一個屬性值,包含的主要字段如下。
public class PropertyValue extends BeanMetadataAttributeAccessor implements Serializable {private final String name;private final Object value;private boolean optional = false;// 是否進行過類型轉換private boolean converted = false;// 類型轉換后的值private Object convertedValue; }PropertyValue 包括原始值和轉換后的值,也就意味著 Spring 可能會對用于輸入的值進行類型轉換。
PropertyValues 接口常用實現如下。
- MutablePropertyValues:標準的 PropertyValues 實現,除了屬性獲取,還支持對屬性的設置。
- ServletConfigPropertyValues:支持 web 環境獲取 servlet 上下文配置的 PropertyValues。
- ServletRequestParameterPropertyValues:支持 web 環境獲取請求參數的 PropertyValues。
DataBinder
DataBinder 是數據綁定的核心類,它內部的屬性可以分為兩類,一類是數據綁定的配置,另一類用于數據綁定,理解這些屬性后我們就能大概知道它內部的機制。
數據綁定配置有關的字段如下。
public class DataBinder implements PropertyEditorRegistry, TypeConverter {// 是否忽略未知字段private boolean ignoreUnknownFields = true;// 是否忽略無效字段,如這些字段的值在目標對象中不可訪問(如嵌套路徑中的字段對應值為空)private boolean ignoreInvalidFields = false;// 是否自動增長包含空值的嵌套路徑private boolean autoGrowNestedPaths = true;// 自動增長的數組或集合的大小的限制private int autoGrowCollectionLimit = DEFAULT_AUTO_GROW_COLLECTION_LIMIT;// 綁定字段白名單private String[] allowedFields;// 綁定字段黑名單private String[] disallowedFields;// 必須綁定的字段private String[] requiredFields; }數據綁定功能實現的字段如下。
public class DataBinder implements PropertyEditorRegistry, TypeConverter {// 需要進行數據綁定的目標對象private final Object target;// 需要進行數據綁定的目標對象名稱private final String objectName;// 數據綁定結果private AbstractPropertyBindingResult bindingResult;// 類型轉換private SimpleTypeConverter typeConverter;// 類型轉換服務private ConversionService conversionService;// 將錯誤碼轉換為消息編碼的解析器private MessageCodesResolver messageCodesResolver;// 錯誤處理器,用于處理字段缺失和進行異常轉換private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor();// 參數校驗器private final List<Validator> validators = new ArrayList<>(); }數據綁定功能實現的字段又分為幾大類:數據綁定的目標對象、負責類型轉換的轉換器、負責參數校驗的校驗器、保存參數校驗結果的 BindingResult。
DataBinder 進行數據綁定時,需要將屬性值轉換為目標對象屬性的類型,還會把校驗結果存至 BindingResult。由于 DataBinder 實現了接口 PropertyEditorRegistry、TypeConverter,還會將實現委托到內部的 typeConverter。
至于校驗器則用于額外的校驗方法使用,在上篇《Spring 參數校驗最佳實踐及原理解析》有進行分析 web 請求參數的校驗會調用 DataBinder 的校驗方法,感興趣可以自行閱讀。
也就是說 DataBinder 兼具對象屬性設置、類型轉換、校驗的能力。#bind 方法實現代碼如下。
public void bind(PropertyValues pvs) {MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues ?(MutablePropertyValues) pvs : new MutablePropertyValues(pvs));doBind(mpvs);}這里對屬性進行了簡單處理后就進入了真正數據綁定的方法。
protected void doBind(MutablePropertyValues mpvs) {checkAllowedFields(mpvs);checkRequiredFields(mpvs);applyPropertyValues(mpvs);}數據綁定時會對屬性必要的校驗和處理。讓我們把重點放到屬性的設置。
protected void applyPropertyValues(MutablePropertyValues mpvs) {try {// Bind request parameters onto target object.getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());} catch (PropertyBatchUpdateException ex) {// Use bind error processor to create FieldErrors.for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());}}}屬性設置直接調用 PropertyAccessor 設置屬性的方法,遇到異常后則使用錯誤處理器處理。那么 PropertyAccessor 是從哪里來的呢?繼續跟蹤代碼。
protected ConfigurablePropertyAccessor getPropertyAccessor() {return getInternalBindingResult().getPropertyAccessor();}這里可以看出,PropertyAccessor 是從內部的 BindingResult 獲取到的,繼續跟蹤代碼則會發現這里 BindingResult 的實現是 BeanPropertyBindingResult,至于 PropertyAccessor 的實現使用的則是 BeanWrapper。也是說真正進行數據綁定的實現由 BeanWrapper 完成,這里不再具體分析。
Spring XML 配置中屬性設置分析
我們已經對 Spring 數據綁定的能力有了一定的了解,那 Spring 是怎么運用這項能力的呢?這里進行一些簡單分析,先看 Spring 如何將 XML 中的屬性配置設置到 bean 的屬性上的。
數據綁定中,屬性使用 PropertyValues 表示,Spring 會將 xml 中的屬性配置轉換為 PropertyValues 并存儲至表示 bean 元數據的 BeanDefinition。核心代碼如下。
public class BeanDefinitionParserDelegate {public void parsePropertyElement(Element ele, BeanDefinition bd) {String propertyName = ele.getAttribute(NAME_ATTRIBUTE);... 省略部分代碼try {Object val = parsePropertyValue(ele, bd, propertyName);PropertyValue pv = new PropertyValue(propertyName, val);bd.getPropertyValues().addPropertyValue(pv);} finally {this.parseState.pop();}} }有了 BeanDefinition,Spring 就可以根據其內部的元數據實例化 bean,并設置 bean 的屬性。核心代碼如下。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactoryimplements AutowireCapableBeanFactory {protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {... 省略部分代碼try {bw.setPropertyValues(new MutablePropertyValues(deepCopy));} catch (BeansException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex);}} }可以看到,bean 屬性的設置和 DataBinder 數據綁定的底層實現一樣,也是委托給 BeanWrapper 設置屬性值,至于這里的 BeanWrapper,則是 Spring 實例化 bean 之后創建的。
WEB 請求參數數據綁定分析
Web 環境下,Spring 需要把請求中的參數或其他參數綁定到 Controller 方法參數值中,這是由 HandlerMethodArgumentResolver 接口來處理的,它可以根據 Controller 方法參數定義解析出參數值。
對于請求到 Model 參數的綁定,實現類為 ModelAttributeMethodProcessor 有關參數綁定的核心代碼如下。
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {@Override@Nullablepublic final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {... 省略部分代碼WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);if (binder.getTarget() != null) {if (!mavContainer.isBindingDisabled(name)) {// 數據綁定bindRequestParameters(binder, webRequest);}// 參數校驗validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {throw new BindException(binder.getBindingResult());}}if (!parameter.getParameterType().isInstance(attribute)) {// 類型轉換attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);}bindingResult = binder.getBindingResult();... 省略部分代碼 }protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {((WebRequestDataBinder) binder).bind(request);} }可以看到,這里直接調用了WebRequestDataBinder#bind方法將 request 轉換為方法參數中的屬性,此外數據綁定后還進行了參數校驗與類型轉換。
總結
Spring 數據綁定的核心類是 DataBinder,其底層使用 BeanWrapper 實現對象屬性值的設置,對于數據綁定,往往又伴隨著數據校驗、類型轉換。Spring 中的類型轉換同樣具有多種實現,下篇將進行介紹。
總結
以上是生活随笔為你收集整理的聊聊 Spring 核心特性中的数据绑定 (DataBinder)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云计算技术-HCIA之VLAN及总结
- 下一篇: hotmail手机端_hotmail a