SpringMVC代码碎片拾遗(01)_一个SpringMVC中对于领域模型绑定的不注意错误

来源:互联网 发布:淘宝买dnf账号 编辑:程序博客网 时间:2024/05/16 10:01

最近这个项目真是各种问题猛喷发,呵呵。

SpringMVC的各种运用不用说了,而更多的需要把底层重写的地方就成为了这个架构的亮点。

害怕Session风暴而使用MongoDB的值传递方式虽然比较奇葩但是的确是有点特色,不过问题来了,绑定参数处理的Resolver都需要自己重写咯。。。

这些后面慢慢记录。


先说一个某天晚上特别诡异的问题,死活查不到原因,后来跟踪代码才发现的问题吧。(其实是一个特别低级的错误,没仔细看自己的业务层直接扒开源代码看了半天

是夜,HN同学正在把他那一个超级大的功能模块进行测试,第一个按钮就直接exception了。

看到后,一个反应就是画面上或者后来的逻辑有越界的地方,当时没细看出错地点。大概看了一下代码觉得没有任何问题。仔细一看出错地点,嗯?居然是spring的BeanWrapperImpl抛出来的问题。这个Class是大名鼎鼎的数据绑定的类,一般Controller的参数的绑定默认都是在这个Class中进行的invoke操作,这个类非常内聚,很多core的功能都在这个类中实现完成。曾经看过这个Class想过读一下代码,但是读源代码如果没有一个特定使用环境总觉得读一会就要睡着的节奏。这次看来是逃不过去了。

错误如下:

org.springframework.beans.InvalidPropertyException: Invalid property 'inputList[0]' of bean class [xx.xx.xx.xx.online.xx.xx.xxx]: Index of out of bounds in property path 'inputList[0]'; nested exception is java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:812) ~[BeanWrapperImpl.class:4.1.0.RELEASE] at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:551) ~[BeanWrapperImpl.class:4.1.0.RELEASE] at org.springframework.beans.BeanWrapperImpl.getBeanWrapperForPropertyPath(BeanWrapperImpl.java:528) ~[BeanWrapperImpl.class:4.1.0.RELEASE] at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:892) ~[BeanWrapperImpl.class:4.1.0.RELEASE] at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:95) ~[AbstractPropertyAccessor.class:4.1.0.RELEASE] at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:728) ~[DataBinder.class:4.1.0.RELEASE] at org.springframework.validation.DataBinder.doBind(DataBinder.java:624) ~[DataBinder.class:4.1.0.RELEASE] at org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:189) ~[WebDataBinder.class:4.1.0.RELEASE]


其中inputList是我们自己做成的Form中的一个filed,存储的事画面上的一个循环List。当时没有好好看这段Log,只是看到了BeanWrapperImpl就开始跟踪源代码了。。。

出错的地方在:

else if (value instanceof List) {int index = Integer.parseInt(key);List<Object> list = (List<Object>) value;growCollectionIfNecessary(list, index, indexedPropertyName, pd, i + 1);value = list.get(index); // 这个地方只要get(0)就直接出错了} private void growCollectionIfNecessary(Collection<Object> collection, int index, String name,   PropertyDescriptor pd, int nestingLevel) {  if (!isAutoGrowNestedPaths()) {   return;  }  int size = collection.size();  if (index >= size && index < this.autoGrowCollectionLimit) {   Class<?> elementType = GenericCollectionTypeResolver.getCollectionReturnType(pd.getReadMethod(), nestingLevel);   if (elementType != null) {    for (int i = collection.size(); i < index + 1; i++) {     collection.add(newValue(elementType, name));    }   }  } }

代码拿到手刚开始没太看明白。。。当时已经22:00多,直接跟踪了一下恍然大悟。

首先,对于我们需要绑定的数据内容,spring会根据其基本的类型采取不同的方式来获得数据(具体调用过程比较复杂,后面写文章再说),这里走到了list的这个选择分支。那么代码中是value = list.get(index); 来进行的获取,从第一个数据开始取得那么就是要get(0),但是发现get(0)直接就出上面的错误。然后发现了在这个调用前还有一个growCollectionIfNecessary的方法,这个方法貌似有些作用。

方法中其实很简单isAutoGrowNestedPaths()能过的话(今后讨论这个方法),下面是进行了一个往插入一个object的操作。

collection.add(newValue(elementType, name));

其中newValue可以直接反射出以elementType为对象的一个Bean,而这个elementType是啥?就是GenericCollectionTypeResolver.getCollectionReturnType(pd.getReadMethod(), nestingLevel);取得的一个泛型类型,是通过pd这个参数传进来的,pd直接掌握着需要绑定的这个对象的内部的信息,比如其拥有的泛型内容是什么。

看到这里,恍然大悟,错误的原因是我们根本没有在自己的Form中给这个inputList设置DTO泛型,导致经过growCollectionIfNecessary方法后,list中并没有塞入任何东西,所以就out of bound了。。。

按说如果刚开始好好看看Log的话一下子应该也能明白,半夜的脑子的确不行了。

减少加班,远离加班。。。







0 0
原创粉丝点击