`
gdfloyd
  • 浏览: 73900 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

Spring WebMVC List容器元素的Data Bind

阅读更多

最近学习SpringMVC,做了一个小Demo, 发现一个问题:无法绑定command对象List Field中的元素的属性。

 

Command Object 类似如下:

public class CommandObject {

	private List<ListElement> mylist = new ArrayList<ListElement>();
	
	// getter and setter
}

这里list属性必须先行实例化,否则会错误。这个与Hibernate要求PO的一对多Set必须实例化一样。 

 

页面大致如下:

 

<input type="text" name="command.xxxlist[0].name" value="" />
<input type="text" name="command.xxxlist[1].name" value="" />
<input type="text" name="command.xxxlist[2].name" value="" />

 

运行后会报错, 由于绑定的时候list为empty, xxxlist.get(index)自然会数组越界,name要set都不知set到哪里去了。Spring也不会自行实例化,这让我感到相当恼火。

 

研读Source Code,寻求解决方法,不禁释然:当前版本要兼容1.4,Spring要如何知道实例化哪个Class,在没有使用泛型的情况下。

 

追踪代码,核心在org.springframework.beans.BeanWrapperImpl.java中。

 

private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
		String propertyName = tokens.canonicalName;
		String actualName = tokens.actualName;
		PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
		if (pd == null || pd.getReadMethod() == null) {
			throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName);
		}
		Method readMethod = pd.getReadMethod();
		try {
			if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
				readMethod.setAccessible(true);
			}
			Object value = readMethod.invoke(this.object, (Object[]) null);
			if (tokens.keys != null) {
				// apply indexes and map keys
				for (int i = 0; i < tokens.keys.length; i++) {
					String key = tokens.keys[i];
					if (value == null) {
						throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
								"Cannot access indexed value of property referenced in indexed " +
								"property path '" + propertyName + "': returned null");
					}
					else if (value.getClass().isArray()) {
						value = Array.get(value, Integer.parseInt(key));
					}
					else if (value instanceof List) {
						List list = (List) value;
						value = list.get(Integer.parseInt(key));
					}
					else if (value instanceof Set) {
						// Apply index to Iterator in case of a Set.
						Set set = (Set) value;
						int index = Integer.parseInt(key);
						if (index < 0 || index >= set.size()) {
							throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
									"Cannot get element with index " + index + " from Set of size " +
									set.size() + ", accessed using property path '" + propertyName + "'");
						}
						Iterator it = set.iterator();
						for (int j = 0; it.hasNext(); j++) {
							Object elem = it.next();
							if (j == index) {
								value = elem;
								break;
							}
						}
					}
					else if (value instanceof Map) {
						Map map = (Map) value;
						Class mapKeyType = null;
						if (JdkVersion.isAtLeastJava15()) {
							mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(pd.getReadMethod(), i + 1);
						}
						// IMPORTANT: Do not pass full property name in here - property editors
						// must not kick in for map keys but rather only for map values.
						Object convertedMapKey = this.typeConverterDelegate.convertIfNecessary(key, mapKeyType);
						// Pass full property name and old value in here, since we want full
						// conversion ability for map values.
						value = map.get(convertedMapKey);
					}
					else {
						throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
								"Property referenced in indexed property path '" + propertyName +
								"' is neither an array nor a List nor a Set nor a Map; returned value was [" + value + "]");
					}
				}
			}
			return value;
		}
		catch (InvocationTargetException ex) {
			throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
					"Getter for property '" + actualName + "' threw exception", ex);
		}
		catch (IllegalAccessException ex) {
			throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
					"Illegal attempt to get property '" + actualName + "' threw exception", ex);
		}
		catch (IndexOutOfBoundsException ex) {
			throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
					"Index of out of bounds in property path '" + propertyName + "'", ex);
		}
		catch (NumberFormatException ex) {
			throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
					"Invalid index in property path '" + propertyName + "'", ex);
		}
	}

 于是决定改Source了,具体如下。顺便还发现Map也有同样问题,便一道改了。

 

private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
		String propertyName = tokens.canonicalName;
		String actualName = tokens.actualName;
		PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
		if (pd == null || pd.getReadMethod() == null) {
			throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName);
		}
		Method readMethod = pd.getReadMethod();
		try {
			if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
				readMethod.setAccessible(true);
			}
			Object value = readMethod.invoke(this.object, (Object[]) null);
			if (tokens.keys != null) {
				// apply indexes and map keys
				for (int i = 0; i < tokens.keys.length; i++) {
					String key = tokens.keys[i];
					if (value == null) {
						throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
								"Cannot access indexed value of property referenced in indexed " +
								"property path '" + propertyName + "': returned null");
					}
					else if (value.getClass().isArray()) {
						value = Array.get(value, Integer.parseInt(key));
					}
					else if (value instanceof List) {
						List list = (List) value;
						int positionOfList = Integer.parseInt(key);
						Class listElementClass = GenericCollectionTypeResolver.getCollectionReturnType(readMethod);
						if(list.size() <= positionOfList && JdkVersion.isAtLeastJava15()
								&& listElementClass != null
								&& !(ClassUtils.isPrimitiveOrWrapper(listElementClass) 
										|| listElementClass.equals(String.class))) {
							if(positionOfList > Byte.MAX_VALUE) {
								throw new IllegalAccessException("Invalid property '" + nestedPath + propertyName + "' of ["
										+ getRootClass() + "] : "
										+ positionOfList
										+ " is too large to support (max value is " 
										+ Byte.MAX_VALUE
										+")");
							}
							for(int j = list.size(); j < positionOfList; j++) {
								Object listElement = BeanUtils.instantiateClass(listElementClass);
								list.add(listElement);
							}
							value = BeanUtils.instantiateClass(listElementClass);
							list.add(value);
							
						} else {
							value = list.get(positionOfList);
						}
					}
					else if (value instanceof Set) {
						// Apply index to Iterator in case of a Set.
						Set set = (Set) value;
						int index = Integer.parseInt(key);
						if (index < 0 || index >= set.size()) {
							throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
									"Cannot get element with index " + index + " from Set of size " +
									set.size() + ", accessed using property path '" + propertyName + "'");
						}
						Iterator it = set.iterator();
						for (int j = 0; it.hasNext(); j++) {
							Object elem = it.next();
							if (j == index) {
								value = elem;
								break;
							}
						}
					}
					else if (value instanceof Map) {
						Map map = (Map) value;
						Class mapKeyType = null;
						Class mapValueType = null;
						if (JdkVersion.isAtLeastJava15()) {
							mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(readMethod, i + 1);
							mapValueType = GenericCollectionTypeResolver.getMapValueReturnType(readMethod, i + 1);
						}
						// IMPORTANT: Do not pass full property name in here - property editors
						// must not kick in for map keys but rather only for map values.
						Object convertedMapKey = this.typeConverterDelegate.convertIfNecessary(key, mapKeyType);
						// Pass full property name and old value in here, since we want full
						// conversion ability for map values.
						value = map.get(convertedMapKey);
						
						if(value == null && mapValueType != null 
								&& !(ClassUtils.isPrimitiveOrWrapper(mapValueType) 
										|| mapValueType.equals(String.class))) {
							value = BeanUtils.instantiateClass(mapValueType);
							map.put(convertedMapKey, value);
						}
					}
					else {
						throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
								"Property referenced in indexed property path '" + propertyName +
								"' is neither an array nor a List nor a Set nor a Map; returned value was [" + value + "]");
					}
				}
			}
			return value;
		}
		catch (InvocationTargetException ex) {
			throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
					"Getter for property '" + actualName + "' threw exception", ex);
		}
		catch (IllegalAccessException ex) {
			throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
					"Illegal attempt to get property '" + actualName + "' threw exception", ex);
		}
		catch (IndexOutOfBoundsException ex) {
			throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
					"Index of out of bounds in property path '" + propertyName + "'", ex);
		}
		catch (NumberFormatException ex) {
			throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
					"Invalid index in property path '" + propertyName + "'", ex);
		}
	}

 虽然代码有点难看,毕竟解决了问题。最后顺便提下,Spring内部的工具类相当给力,大大减轻了了修改工作量。^_^

分享到:
评论

相关推荐

    spring_MVC源码

    14. &lt;bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" /&gt; 15. 16. &lt;!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 --&gt; 17. &lt;bean class="org....

    spring WEB入门级项目搭建

    &lt;artifactId&gt;spring-webmvc &lt;version&gt;5.3.23 &lt;groupId&gt;org.springframework &lt;artifactId&gt;spring-jdbc &lt;version&gt;5.3.23 &lt;!-- 其他可能需要的依赖,如:JSTL,连接池等 --&gt; ``` 接着,我们需要配置...

    spring mvc注解jdbctemplate

    import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; @Controller ...

    spring mvc step by step

    import org.springframework.web.bind.annotation.GetMapping; @Controller public class HomeController { @GetMapping("/") public String home(Model model) { model.addAttribute("message", "Hello, ...

    spring mvc 接口

    Spring MVC 是 Spring Framework 的一个重要模块,它实现了 MVC(模型-视图-控制器)设计模式,帮助开发者构建易于维护和扩展的 Web 应用程序。在 Spring MVC 中,控制器负责接收用户请求,并决定调用哪个模型来处理...

    SpringDataJPA入门

    - 添加SpringDataJPA、SpringWeb和JPA的依赖项到Maven或Gradle构建文件中。 - 配置数据源和JPA设置,如实体管理工厂、事务管理器等。 - 创建`persistence.xml`文件,指定数据源、实体类包及持久化提供商。 ### 3. ...

    springMvc集合接收参数的用法

    import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import java.util...

    Spring-Hibernate的通过标签分页源码

    import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; @Controller public ...

    从最基本的Java工程搭建SpringMVC+SpringDataJPA+Hibernate

    &lt;artifactId&gt;spring-webmvc&lt;/artifactId&gt; ${springmvc.version}&lt;/version&gt; &lt;/dependency&gt; &lt;!-- Spring Data JPA --&gt; &lt;dependency&gt; &lt;groupId&gt;org.springframework.data&lt;/groupId&gt; &lt;artifactId&gt;spring-data-jpa...

    使用注解SpringMVC从页面导出Excel和word文档的使用的jar包——使用ExportExcel工具类.rar

    import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller ...

    Spring Boot项目07之文件上传与回显项目源码

    import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; @PostMapping("/upload...

    java不依赖sturts的springmvc多附件上传

    在Spring MVC的配置文件(如`web.xml`或Spring Boot的配置类)中,我们需要添加一个MultipartResolver来处理文件上传。通常我们会选择`CommonsMultipartResolver`,这是基于Apache Commons FileUpload库的。配置如下...

    使用Java Spring框架的介绍、心得、项目及相关练习

    import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/users") public class UserController { @Autowired private UserService userService...

    (java版本)自己做了一个jquery ajax异步请求,获得一个list对象的下拉框例子

    import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class MyController { @GetMapping("/getDropdownData") public List&lt;MyObject&gt; ...

    Eclipse中新建SpringBoot项目完成对json、pojo、map、list的请求源码

    public List&lt;MyPojo&gt; handleListRequest(@RequestBody List&lt;MyPojo&gt; listData) { // 处理List请求 } } ``` 在上面的例子中,`@RestController`注解表示这是一个RESTful风格的控制器,而`@RequestMapping("/api")...

    Spring boot 实现单个或批量文件上传功能

    import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; import org.spring...

    springmvc_json_自动加载表格实例

    &lt;artifactId&gt;spring-webmvc &lt;version&gt;5.3.23 &lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt; &lt;artifactId&gt;jackson-databind &lt;version&gt;2.13.4.2 ``` 接下来,创建一个控制器(Controller)类,使用`@...

    基于javaweb 的bootstrap table使用案例源码下载

    import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping("/server") public...

    springmvc实例

    &lt;artifactId&gt;spring-webmvc &lt;version&gt;5.3.23 &lt;groupId&gt;mysql &lt;artifactId&gt;mysql-connector-java &lt;version&gt;8.0.30 &lt;!-- 其他如Spring Data JPA, Hibernate等持久层依赖 --&gt; ``` 接下来,我们创建 ...

    springmvc简单框架提供的jsonp服务端接口

    在IT行业中,SpringMVC是Java企业级应用开发中广泛使用的Web MVC框架,它提供了模型-视图-控制器(MVC)模式的实现,使得开发者能够更高效地构建可维护和可扩展的Web应用程序。本篇文章将深入探讨如何利用SpringMVC...

Global site tag (gtag.js) - Google Analytics