- 浏览: 50443 次
- 性别:
- 来自: 南京
文章分类
最新评论
在<spring源码学习系列3.2-handlerAdapter执行>中MultiActionController#invokeNamedMethod 方法中,将request参数值设置到command对象中-另一种形式的模型驱动
springmvc设计的一个很重要的原则是开闭原则:
对修改或处理流程关闭,对扩展开放
但对于自定义controller继承MultiActionController,覆盖一些方法,改变了springmvc的部分功能
如覆盖bind(HttpServletRequest request, Object command),绑定过程完全由用户的编程能力决定。
spring版本3.2.2
绑定入口接口:
ServletRequestDataBinder
日期格式转换在哪?
binder可以注册属性编辑器-将字符串格式转成成Data格式
参数校验在什么时候?
MultiActionController#bind
自定义validators,校验参数,必填校验等
MultiActionController#createBinder
对于MultiActionController中的方法,如bind,initBinder等可被自定义的controller子类覆盖,从而定义自己的绑定逻辑.但一般也不这么做,这样项目跟springmvc耦合性加强了
ServletRequestDataBinder继承体系为:
ServletRequestDataBinder extends WebDataBinder extends DataBinder
创建ServletRequestDataBinder的对象时,用要设置值的command对象初始化binder。即将command封装到DataBinder
=============================================职责分割线
实际绑定command对象的工作交由DataBinder接口去完成,用户(开发人员)只需要将设置值request及被设置值command传给DataBinder就行
1.request->MutalbPropertyValues(dataBinder)
2.MutalbPropertyValues->command(beanWrapper)
ServletRequestDataBinder#bind
WebDataBinder#doBind
2.2.1 处理特殊属性名,去掉前缀。如果不需要这个逻辑,可覆盖createBinder,自定义自己的dataBinder extexds ServletRequestDataBinder
DataBinder#doBind
DataBinder#doBind是绑定mpvs到command对象的地方,也许这就是将request参数封装到MutablePropertyValues的意义。ServletRequestDataBinder只是封装request到mpvs,也有其他形式的请求参数,只需要继承DataBinder,然后做相应的封装
对于检验的错误或其他信息的处理,springmvc是将其放到DataBinder的属性bindingResult中,其为类BeanPropertyBindingResult的对象
对于2.2.1与2.2.2.1绑定前校验,整合起来看,为什么不将她们的校验放在一起。应该独立来看,每个类的关注点或职责不一样,她们只需要保持各自方法的健壮性
DataBinder#applyPropertyValues
getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());为异常处理机制,将异常设置到bindingResult,在绑定结束时,会调用ServletRequestDataBinder#closeNoCatch判断是否有异常,有则抛出
request->mpvs
=============================================职责分割线
DataBinder最终还是将指挥棒交给了BeanWrapper
mpvs->command
AbstractPropertyAccessor#setPropertyValues
往下跟踪就是beanWrapper如何设置属性值了,离最初的主题springmvc原理越来越远了,所以不再往下了
以下为得到beanWrapper或ConfigurablePropertyAccessor的路径-可忽略
DataBinder#getPropertyAccessor
DataBinder#getInternalBindingResult
DataBinder#initBeanPropertyAccess
BeanPropertyBindingResult#getPropertyAccessor
BeanPropertyBindingResult#createBeanWrapper
PropertyAccessorFactory#forBeanPropertyAccess
感想:
如果不用command绑定,正常的request.getParameter(),效率肯定比springmvc的参数绑定效率高,空间上也占用比较少的内存.
那么在时间和空间上,有哪些区别呢,对于command绑定:
创建了ServletRequestDataBinder对象
创建了MutablePropertyValues对象-用于包装request
创建了BeanPropertyBindingResult
创建了DefaultBindingErrorProcessor
创建了BeanWrapperImpl-设置command属性
参考:
Validation, Data Binding, and Type Conversion
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html
springmvc设计的一个很重要的原则是开闭原则:
对修改或处理流程关闭,对扩展开放
但对于自定义controller继承MultiActionController,覆盖一些方法,改变了springmvc的部分功能
如覆盖bind(HttpServletRequest request, Object command),绑定过程完全由用户的编程能力决定。
spring版本3.2.2
绑定入口接口:
ServletRequestDataBinder
日期格式转换在哪?
binder可以注册属性编辑器-将字符串格式转成成Data格式
@InitBinder public void initBinder(WebDataBinder binder) { DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); CustomDateEditor dateEditor = new CustomDateEditor(df, true); binder.registerCustomEditor(Date.class, dateEditor); }
参数校验在什么时候?
// If last parameter isn't of HttpSession type, it's a command. if (paramTypes.length >= 3 && !paramTypes[paramTypes.length - 1].equals(HttpSession.class)) { Object command = newCommandObject(paramTypes[paramTypes.length - 1]); params.add(command); bind(request, command); }
MultiActionController#bind
protected void bind(HttpServletRequest request, Object command) throws Exception { logger.debug("Binding request parameters onto MultiActionController command"); // 1 初始化ServletRequestDataBinder ServletRequestDataBinder binder = createBinder(request, command); // 2 设置request参数到command binder.bind(request); if (this.validators != null) { for (Validator validator : this.validators) { if (validator.supports(command.getClass())) { ValidationUtils.invokeValidator(validator, command, binder.getBindingResult()); } } } binder.closeNoCatch(); }
自定义validators,校验参数,必填校验等
MultiActionController#createBinder
protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object command) throws Exception { // 1.1 实例化ServletRequestDataBinder ServletRequestDataBinder binder = new ServletRequestDataBinder(command, getCommandName(command)); // 1.2 初始化binder-用户自定义-webBindingInitializer-springmvc扩展点 initBinder(request, binder); return binder; }
对于MultiActionController中的方法,如bind,initBinder等可被自定义的controller子类覆盖,从而定义自己的绑定逻辑.但一般也不这么做,这样项目跟springmvc耦合性加强了
@Override protected void bind(HttpServletRequest request, Object command) throws Exception { System.out.println("自定义绑定方法bind"); } @Override protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object command) throws Exception { return null; } @Override protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception { binder.setAutoGrowNestedPaths(true); binder.setAutoGrowCollectionLimit(1024); System.out.println("hi, i am initBinder"); }
ServletRequestDataBinder继承体系为:
ServletRequestDataBinder extends WebDataBinder extends DataBinder
创建ServletRequestDataBinder的对象时,用要设置值的command对象初始化binder。即将command封装到DataBinder
=============================================职责分割线
实际绑定command对象的工作交由DataBinder接口去完成,用户(开发人员)只需要将设置值request及被设置值command传给DataBinder就行
1.request->MutalbPropertyValues(dataBinder)
2.MutalbPropertyValues->command(beanWrapper)
ServletRequestDataBinder#bind
public void bind(ServletRequest request) { // 2.1 创建MutablePropertyValues:将request值设置到ProperValues里面 // 循环request.getParameterNames()的参数放入map,并初始化MutablePropertyValues MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request); MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class); if (multipartRequest != null) { bindMultipart(multipartRequest.getMultiFileMap(), mpvs); } addBindValues(mpvs, request); // 2.2 委托父类方法,属性值mpvs设置到command对象-已经在实例化ServletRequestDataBinder时包含 doBind(mpvs); }
WebDataBinder#doBind
@Override protected void doBind(MutablePropertyValues mpvs) { // 2.2.1 处理特殊属性名(以! 或者 _开头)的属性名,去掉前缀-WebDataBinder绑定前校验 // 用户(开发人员)可在自定义的controller下,覆盖createBinder方法,改变这个地方的逻辑 checkFieldDefaults(mpvs); checkFieldMarkers(mpvs); // 2.2.2 委托父类方法,实际绑定command对象 super.doBind(mpvs); }
2.2.1 处理特殊属性名,去掉前缀。如果不需要这个逻辑,可覆盖createBinder,自定义自己的dataBinder extexds ServletRequestDataBinder
DataBinder#doBind
/** * Actual implementation of the binding process, working with the * passed-in MutablePropertyValues instance. * @param mpvs the property values to bind, * as MutablePropertyValues instance * @see #checkAllowedFields * @see #checkRequiredFields * @see #applyPropertyValues */ protected void doBind(MutablePropertyValues mpvs) { // 2.2.2.1 检查是否自定义允许解析的属性名-DataBinder绑定前校验 checkAllowedFields(mpvs); checkRequiredFields(mpvs); // 2.2.2.2 执行绑定 applyPropertyValues(mpvs); }
DataBinder#doBind是绑定mpvs到command对象的地方,也许这就是将request参数封装到MutablePropertyValues的意义。ServletRequestDataBinder只是封装request到mpvs,也有其他形式的请求参数,只需要继承DataBinder,然后做相应的封装
对于检验的错误或其他信息的处理,springmvc是将其放到DataBinder的属性bindingResult中,其为类BeanPropertyBindingResult的对象
对于2.2.1与2.2.2.1绑定前校验,整合起来看,为什么不将她们的校验放在一起。应该独立来看,每个类的关注点或职责不一样,她们只需要保持各自方法的健壮性
DataBinder#applyPropertyValues
protected void applyPropertyValues(MutablePropertyValues mpvs) { try { // 2.2.2.2.1 // 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()); } } }
getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());为异常处理机制,将异常设置到bindingResult,在绑定结束时,会调用ServletRequestDataBinder#closeNoCatch判断是否有异常,有则抛出
request->mpvs
=============================================职责分割线
DataBinder最终还是将指挥棒交给了BeanWrapper
mpvs->command
AbstractPropertyAccessor#setPropertyValues
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) throws BeansException { List<PropertyAccessException> propertyAccessExceptions = null; List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ? ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues())); for (PropertyValue pv : propertyValues) { try { // This method may throw any BeansException, which won't be caught // here, if there is a critical failure such as no matching field. // We can attempt to deal only with less serious exceptions. setPropertyValue(pv); } catch (NotWritablePropertyException ex) { if (!ignoreUnknown) { throw ex; } // Otherwise, just ignore it and continue... } catch (NullValueInNestedPathException ex) { if (!ignoreInvalid) { throw ex; } // Otherwise, just ignore it and continue... } catch (PropertyAccessException ex) { if (propertyAccessExceptions == null) { propertyAccessExceptions = new LinkedList<PropertyAccessException>(); } propertyAccessExceptions.add(ex); } } // If we encountered individual exceptions, throw the composite exception. if (propertyAccessExceptions != null) { PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]); throw new PropertyBatchUpdateException(paeArray); } }
往下跟踪就是beanWrapper如何设置属性值了,离最初的主题springmvc原理越来越远了,所以不再往下了
以下为得到beanWrapper或ConfigurablePropertyAccessor的路径-可忽略
DataBinder#getPropertyAccessor
protected ConfigurablePropertyAccessor getPropertyAccessor() { return getInternalBindingResult().getPropertyAccessor(); }
DataBinder#getInternalBindingResult
protected AbstractPropertyBindingResult getInternalBindingResult() { if (this.bindingResult == null) { initBeanPropertyAccess(); } return this.bindingResult; }
DataBinder#initBeanPropertyAccess
public void initBeanPropertyAccess() { Assert.state(this.bindingResult == null, "DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods"); this.bindingResult = new BeanPropertyBindingResult( getTarget(), getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit()); if (this.conversionService != null) { this.bindingResult.initConversion(this.conversionService); } }
BeanPropertyBindingResult#getPropertyAccessor
/** * Returns the {@link BeanWrapper} that this instance uses. * Creates a new one if none existed before. * @see #createBeanWrapper() */ @Override public final ConfigurablePropertyAccessor getPropertyAccessor() { if (this.beanWrapper == null) { this.beanWrapper = createBeanWrapper(); this.beanWrapper.setExtractOldValueForEditor(true); this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths); this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit); } return this.beanWrapper; }
BeanPropertyBindingResult#createBeanWrapper
protected BeanWrapper createBeanWrapper() { Assert.state(this.target != null, "Cannot access properties on null bean instance '" + getObjectName() + "'!"); return PropertyAccessorFactory.forBeanPropertyAccess(this.target); }
PropertyAccessorFactory#forBeanPropertyAccess
public static BeanWrapper forBeanPropertyAccess(Object target) { return new BeanWrapperImpl(target); }
感想:
如果不用command绑定,正常的request.getParameter(),效率肯定比springmvc的参数绑定效率高,空间上也占用比较少的内存.
那么在时间和空间上,有哪些区别呢,对于command绑定:
创建了ServletRequestDataBinder对象
创建了MutablePropertyValues对象-用于包装request
创建了BeanPropertyBindingResult
创建了DefaultBindingErrorProcessor
创建了BeanWrapperImpl-设置command属性
参考:
Validation, Data Binding, and Type Conversion
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html
发表评论
-
spring疑难解惑-循环依赖的解决
2020-06-17 23:27 542AbstractAutowireCapableBeanFact ... -
spring容器
2019-07-14 08:47 305private final ServletContainer ... -
spring容器
2019-07-13 23:35 0spring容器与springmvc容器 73 ... -
spring源码学习系列2.6-spring ioc原理-codes
2019-03-05 22:56 488web.xml <listener> < ... -
spring源码学习系列3.4-spring mvc原理-codes
2019-01-21 22:46 297本篇章从核心类角度解读springmvc的原理 web.xm ... -
spring源码学习系列4.2-spring aop原理-codes
2018-12-04 22:29 562jdk: Proxy InvocationHandler ... -
spring源码学习系列4.1-spring实现对ibatis的事务管理
2018-09-17 15:44 579事务由spring管理,可以理解为由spring管理数据库连接 ... -
spring源码学习系列4-3种常用的自动代理创建器
2018-09-02 15:48 5713种自动代理器是 AnnotationAwareAspectJ ... -
spring源码学习系列1.2-spring事务代理深入分析2
2018-05-27 19:46 452提示: BeanPostProcessor AopUtils ... -
spring源码学习系列2.5-ApplicationContext初始化-设计模式
2018-05-08 15:17 521ApplicationContext容器的初始化可以通过模板方 ... -
spring源码学习系列3.3-DispatcherServlet初始化-设计模式
2018-05-07 11:12 623springmvc的核心是DispatcherServlet ... -
封装spring-security
2018-01-23 19:33 519github地址: https://github.com/ne ... -
eclipse导入spring源码
2018-05-12 07:20 981spring在git上的项目时gradle管理jar包的,所以 ... -
spring源码学习系列3.2.3-异常页面拦截机制
2017-07-29 19:07 768前序:本文的意义在于了解 tomcat处理异常 请求访问 ... -
spring源码学习系列3.2.2-How to bind String to Date
2017-07-17 12:40 599springmvc开发中,经常需将界面日期数据(String) ... -
spring源码学习系列3.2-handlerAdapter执行
2017-05-28 12:01 410DispatcherServlet#doDispatch中调用 ... -
spring源码学习系列3.1-handlerMapping初始化
2017-05-28 11:56 703SimpleUrlHandlerMapping的继承体系 or ... -
spring源码学习系列2.4-finishRefresh会做什么
2017-05-06 16:36 579spring容器初始化完成后,调用finishRresh 该 ... -
spring源码学习系列3-springmvc原理
2017-05-28 11:56 458问题: springmvc是如何控 ... -
spring源码学习系列2-容器初始化入口-refresh
2017-04-23 21:33 477context=XmlWebApplicationContex ...
相关推荐
com.alibaba:hessian-lite:jar:3.2.1-fixed-2 hessian-lite hessian-lite-3.2.1-fixed-2.jar
本文将围绕"spring-framework-3.2.1.RELEASE"这一版本,详细探讨Spring框架3.x系列的核心特性和使用方法。 一、Spring概述 Spring是一个全面的后端应用程序开发框架,它提倡依赖注入(Dependency Injection, DI)和...
总结一下,"spark-3.2.1-bin-hadoop2.7.tgz"是一个专为Linux设计的Spark版本,与Hadoop 2.7兼容,提供了高效的大数据处理能力,涵盖了核心计算、SQL查询、流处理、机器学习和图计算等多个方面。在实际应用中,开发者...
Gradle 的核心特性之一是它的基于Groovy的领域特定语言(DSL),这种DSL使得构建脚本更加简洁易读,允许开发者用面向对象的方式描述项目构建过程。在Gradle-3.2.1中,你可以通过编写`build.gradle`文件来定义项目的...
1. **Apache DolphinScheduler源码包(apache-dolphinscheduler-3.2.1-src.tar.gz)** 这个源码包包含了DolphinScheduler项目的全部源代码,是开发人员进行二次开发、定制或者深入理解其内部机制的重要资源。解压后...
标题 "helm-v3.2.1-linux-amd64.tar.gz" 暗示着这是一个包含 Helm v3.2.1 版本的软件包,适用于 Linux 平台的 x86_64(AMD64)架构。Helm 是 Kubernetes(K8s)生态系统中的一个关键组件,用作管理 Kubernetes 应用...
iSparta是一款专为Windows平台设计的代码覆盖率工具,基于开源的Istanbul进行开发,主要服务于JavaScript项目。它的核心功能是帮助开发者在测试过程中了解代码的覆盖情况,即哪些代码被执行过,哪些还未被触及,这...
标题 "twrp-pixel-installer-sailfish-3.2.1-0.zip" 暗示了这是一款针对Pixel设备的TWRP(Team Win Recovery Project)恢复环境的安装程序,版本为3.2.1-0,而"Sailfish"可能是该版本的一个特定代号或特色。...
apache-maven-3.2.1-bin.zip
赠送原API文档:commons-collections-3.2.1-javadoc.jar; 赠送源代码:commons-collections-3.2.1-sources.jar; 包含翻译后的API文档:commons-collections-3.2.1-javadoc-API文档-中文(简体)版.zip 对应Maven...
postgis-bundle-pg10x64-setup-3.2.1-1.exe安装包,对应postgres10
python.exe
【标题】"isparta-3.2.1-win.rar" 指的是一个名为 "isparta" 的工具的Windows版本,版本号为3.2.1,并且该工具的安装或打包文件以RAR压缩格式提供。RAR是一种常见的文件压缩格式,用于减少文件大小以便于存储和传输...
赠送原API文档:cas-client-core-3.2.1-javadoc.jar; 赠送源代码:cas-client-core-3.2.1-sources.jar; 赠送Maven依赖信息文件:cas-client-core-3.2.1.pom; 包含翻译后的API文档:cas-client-core-3.2.1-javadoc...
NessusClient-3.2.1-es5.i386.rpm NessusClient-3.2.1-es5.i386.rpm
赠送原API文档:commons-collections-3.2.1-javadoc.jar 赠送源代码:commons-collections-3.2.1-sources.jar 包含翻译后的API文档:commons-collections-3.2.1-javadoc-API文档-中文(简体)-英语-对照版.zip 对应...
NLpack1-eclipse-SDK-3.2.1-win32汉化包
apache-maven-3.2.1-bin.tar
安装matplotlib 3.2.1时,我们可以使用提供的"matplotlib-3.2.1-cp38-cp38-win32.whl"文件。这是一个wheel文件,是Python的二进制包格式,可以直接通过pip安装,无需编译,大大简化了安装过程。在命令行中,只需输入...