日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

聊聊 Spring 核心特性中的数据绑定 (DataBinder)

發(fā)布時(shí)間:2024/1/1 javascript 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 聊聊 Spring 核心特性中的数据绑定 (DataBinder) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前面的話

Spring 的核心特性包括 IOC 容器、事件、資源管理、國際化、校驗(yàn)、數(shù)據(jù)綁定、類型轉(zhuǎn)換、EL 表達(dá)式、AOP。其他特性可以輕易的在網(wǎng)絡(luò)上找到很多資料,而數(shù)據(jù)綁定這個(gè)特性即便在 Spring 官網(wǎng)描述卻也不太多。這是因?yàn)閿?shù)據(jù)綁定主要應(yīng)用于 Spring 內(nèi)部,對于用戶而言直接使用的場景并不多。如果想要深入理解 Spring 內(nèi)部的運(yùn)行機(jī)制,數(shù)據(jù)綁定是必須了解的一塊內(nèi)容。

理解數(shù)據(jù)綁定

數(shù)據(jù)綁定允許將用戶輸入的內(nèi)容動(dòng)態(tài)綁定到應(yīng)用程序中的領(lǐng)域模型中。對于 Spring 而言,用于輸入具體的場景主要包括 xml 中 bean 的定義、web 環(huán)境下的請求參數(shù)。

數(shù)據(jù)綁定在 Spring 中的核心類是 org.springframework.validation.DataBinder,可以看到這個(gè)類位于 validation 包中,數(shù)據(jù)綁定時(shí)也往往伴隨著參數(shù)校驗(yàn)。先看看如何手動(dòng)使用這個(gè)類。

@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 類型的參數(shù),并將參數(shù)中的數(shù)據(jù)設(shè)置到對象的屬性中。

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 表示對象中的某一個(gè)屬性值,包含的主要字段如下。

public class PropertyValue extends BeanMetadataAttributeAccessor implements Serializable {private final String name;private final Object value;private boolean optional = false;// 是否進(jìn)行過類型轉(zhuǎn)換private boolean converted = false;// 類型轉(zhuǎn)換后的值private Object convertedValue; }

PropertyValue 包括原始值和轉(zhuǎn)換后的值,也就意味著 Spring 可能會(huì)對用于輸入的值進(jìn)行類型轉(zhuǎn)換。

PropertyValues 接口常用實(shí)現(xiàn)如下。

  • MutablePropertyValues:標(biāo)準(zhǔn)的 PropertyValues 實(shí)現(xiàn),除了屬性獲取,還支持對屬性的設(shè)置。
  • ServletConfigPropertyValues:支持 web 環(huán)境獲取 servlet 上下文配置的 PropertyValues。
  • ServletRequestParameterPropertyValues:支持 web 環(huán)境獲取請求參數(shù)的 PropertyValues。

DataBinder

DataBinder 是數(shù)據(jù)綁定的核心類,它內(nèi)部的屬性可以分為兩類,一類是數(shù)據(jù)綁定的配置,另一類用于數(shù)據(jù)綁定,理解這些屬性后我們就能大概知道它內(nèi)部的機(jī)制。

數(shù)據(jù)綁定配置有關(guān)的字段如下。

public class DataBinder implements PropertyEditorRegistry, TypeConverter {// 是否忽略未知字段private boolean ignoreUnknownFields = true;// 是否忽略無效字段,如這些字段的值在目標(biāo)對象中不可訪問(如嵌套路徑中的字段對應(yīng)值為空)private boolean ignoreInvalidFields = false;// 是否自動(dòng)增長包含空值的嵌套路徑private boolean autoGrowNestedPaths = true;// 自動(dòng)增長的數(shù)組或集合的大小的限制private int autoGrowCollectionLimit = DEFAULT_AUTO_GROW_COLLECTION_LIMIT;// 綁定字段白名單private String[] allowedFields;// 綁定字段黑名單private String[] disallowedFields;// 必須綁定的字段private String[] requiredFields; }

數(shù)據(jù)綁定功能實(shí)現(xiàn)的字段如下。

public class DataBinder implements PropertyEditorRegistry, TypeConverter {// 需要進(jìn)行數(shù)據(jù)綁定的目標(biāo)對象private final Object target;// 需要進(jìn)行數(shù)據(jù)綁定的目標(biāo)對象名稱private final String objectName;// 數(shù)據(jù)綁定結(jié)果private AbstractPropertyBindingResult bindingResult;// 類型轉(zhuǎn)換private SimpleTypeConverter typeConverter;// 類型轉(zhuǎn)換服務(wù)private ConversionService conversionService;// 將錯(cuò)誤碼轉(zhuǎn)換為消息編碼的解析器private MessageCodesResolver messageCodesResolver;// 錯(cuò)誤處理器,用于處理字段缺失和進(jìn)行異常轉(zhuǎn)換private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor();// 參數(shù)校驗(yàn)器private final List<Validator> validators = new ArrayList<>(); }

數(shù)據(jù)綁定功能實(shí)現(xiàn)的字段又分為幾大類:數(shù)據(jù)綁定的目標(biāo)對象、負(fù)責(zé)類型轉(zhuǎn)換的轉(zhuǎn)換器、負(fù)責(zé)參數(shù)校驗(yàn)的校驗(yàn)器、保存參數(shù)校驗(yàn)結(jié)果的 BindingResult。

DataBinder 進(jìn)行數(shù)據(jù)綁定時(shí),需要將屬性值轉(zhuǎn)換為目標(biāo)對象屬性的類型,還會(huì)把校驗(yàn)結(jié)果存至 BindingResult。由于 DataBinder 實(shí)現(xiàn)了接口 PropertyEditorRegistry、TypeConverter,還會(huì)將實(shí)現(xiàn)委托到內(nèi)部的 typeConverter。

至于校驗(yàn)器則用于額外的校驗(yàn)方法使用,在上篇《Spring 參數(shù)校驗(yàn)最佳實(shí)踐及原理解析》有進(jìn)行分析 web 請求參數(shù)的校驗(yàn)會(huì)調(diào)用 DataBinder 的校驗(yàn)方法,感興趣可以自行閱讀。

也就是說 DataBinder 兼具對象屬性設(shè)置、類型轉(zhuǎn)換、校驗(yàn)的能力。#bind 方法實(shí)現(xiàn)代碼如下。

public void bind(PropertyValues pvs) {MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues ?(MutablePropertyValues) pvs : new MutablePropertyValues(pvs));doBind(mpvs);}

這里對屬性進(jìn)行了簡單處理后就進(jìn)入了真正數(shù)據(jù)綁定的方法。

protected void doBind(MutablePropertyValues mpvs) {checkAllowedFields(mpvs);checkRequiredFields(mpvs);applyPropertyValues(mpvs);}

數(shù)據(jù)綁定時(shí)會(huì)對屬性必要的校驗(yàn)和處理。讓我們把重點(diǎn)放到屬性的設(shè)置。

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());}}}

屬性設(shè)置直接調(diào)用 PropertyAccessor 設(shè)置屬性的方法,遇到異常后則使用錯(cuò)誤處理器處理。那么 PropertyAccessor 是從哪里來的呢?繼續(xù)跟蹤代碼。

protected ConfigurablePropertyAccessor getPropertyAccessor() {return getInternalBindingResult().getPropertyAccessor();}

這里可以看出,PropertyAccessor 是從內(nèi)部的 BindingResult 獲取到的,繼續(xù)跟蹤代碼則會(huì)發(fā)現(xiàn)這里 BindingResult 的實(shí)現(xiàn)是 BeanPropertyBindingResult,至于 PropertyAccessor 的實(shí)現(xiàn)使用的則是 BeanWrapper。也是說真正進(jìn)行數(shù)據(jù)綁定的實(shí)現(xiàn)由 BeanWrapper 完成,這里不再具體分析。

Spring XML 配置中屬性設(shè)置分析

我們已經(jīng)對 Spring 數(shù)據(jù)綁定的能力有了一定的了解,那 Spring 是怎么運(yùn)用這項(xiàng)能力的呢?這里進(jìn)行一些簡單分析,先看 Spring 如何將 XML 中的屬性配置設(shè)置到 bean 的屬性上的。

數(shù)據(jù)綁定中,屬性使用 PropertyValues 表示,Spring 會(huì)將 xml 中的屬性配置轉(zhuǎn)換為 PropertyValues 并存儲(chǔ)至表示 bean 元數(shù)據(jù)的 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 就可以根據(jù)其內(nèi)部的元數(shù)據(jù)實(shí)例化 bean,并設(shè)置 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 屬性的設(shè)置和 DataBinder 數(shù)據(jù)綁定的底層實(shí)現(xiàn)一樣,也是委托給 BeanWrapper 設(shè)置屬性值,至于這里的 BeanWrapper,則是 Spring 實(shí)例化 bean 之后創(chuàng)建的。

WEB 請求參數(shù)數(shù)據(jù)綁定分析

Web 環(huán)境下,Spring 需要把請求中的參數(shù)或其他參數(shù)綁定到 Controller 方法參數(shù)值中,這是由 HandlerMethodArgumentResolver 接口來處理的,它可以根據(jù) Controller 方法參數(shù)定義解析出參數(shù)值。

對于請求到 Model 參數(shù)的綁定,實(shí)現(xiàn)類為 ModelAttributeMethodProcessor 有關(guān)參數(shù)綁定的核心代碼如下。

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)) {// 數(shù)據(jù)綁定bindRequestParameters(binder, webRequest);}// 參數(shù)校驗(yàn)validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {throw new BindException(binder.getBindingResult());}}if (!parameter.getParameterType().isInstance(attribute)) {// 類型轉(zhuǎn)換attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);}bindingResult = binder.getBindingResult();... 省略部分代碼 }protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {((WebRequestDataBinder) binder).bind(request);} }

可以看到,這里直接調(diào)用了WebRequestDataBinder#bind方法將 request 轉(zhuǎn)換為方法參數(shù)中的屬性,此外數(shù)據(jù)綁定后還進(jìn)行了參數(shù)校驗(yàn)與類型轉(zhuǎn)換。

總結(jié)

Spring 數(shù)據(jù)綁定的核心類是 DataBinder,其底層使用 BeanWrapper 實(shí)現(xiàn)對象屬性值的設(shè)置,對于數(shù)據(jù)綁定,往往又伴隨著數(shù)據(jù)校驗(yàn)、類型轉(zhuǎn)換。Spring 中的類型轉(zhuǎn)換同樣具有多種實(shí)現(xiàn),下篇將進(jìn)行介紹。

總結(jié)

以上是生活随笔為你收集整理的聊聊 Spring 核心特性中的数据绑定 (DataBinder)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。