文章目录
1、参数绑定综述
当用户在页面触发某种请求时,一般会将一些参数(key/value)带到后台。在Spring MVC中可以通过参数绑定,将客户端请求的key/value数据绑定到Controller处理器方法的形参上,显然,这是很关键的一个问题。
当用户发送一个请求时,根据Spring MVC的请求处理流程,前端控制器会请求处理器映射器HandlerMapping返回一个处理器(或处理器链),然后请求处理器适配器HandlerAdapter执行相应的Handler处理器。此时,处理器适配器HandlerAdapter会调用Spring MVC提供的参数绑定组件将请求的key/value数据绑定到Controller处理器方法对应的形参上。
关于Spring MVC的参数绑定组件,早期版本中使用PropertyEditor,其只能将字符串转换为Java对象,而后期版本中使用Converter转换器,它可以进行任意类型的转换。Spring MVC提供了很多的Converter转换器,但在特殊情况下需要自定义Converter转换器(如日期数据绑定)。
Spring MVC中有一些默认支持的类型,这些类型可以直接在Controller类的方法中定义,在参数绑定的过程中遇到该种类型就直接进行绑定。其默认支持的类型有以下几种:HttpServletRequest、HttpServletResponse、HttpSession及Model/ModelMap。
HttpServletRequest可以通过request对象获取请求信息。
HttpServletResponse可以通过response对象处理响应信息。
HttpSession可以通过session对象得到session中存放的对象。
Model是一个接口,ModelMap是一个接口实现,它的作用就是讲model数据填充到request域。
2 、简单类型参数绑定
在Spring MVC中还可以自定义简单类型,这些类型也是直接在Controller类的方法中定义,在处理key/value信息时,就会以key名寻找Controller类的方法中具有相同名称的形参并进行绑定。
例如下面的例子,根据水果商品的id来获取水果的详细信息,具体Controller代码如下:
package com.ccff.controller;
import com.ccff.model.Fruits;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
//基于注解的Handler类
//使用 @Controller来标识它是一个控制器
@Controller
@RequestMapping("/query")
public class FruitsController {
private FruitsService fruitsService = new FruitsService();
//根据水果id查询水果信息
@RequestMapping(value = "/queryFruitByID.action", method = RequestMethod.GET)
public String queryFruitByID(Model model, int id){
//模拟service获取水果商品列表
List<Fruits> fruitsList = fruitsService.queryFruitsList();
List<Fruits> resultFruit = new ArrayList<>();
for (Fruits fruits : fruitsList){
if (fruits.getId() == id)
resultFruit.add(fruits);
}
//将结果放到model中传到显示页面中
model.addAttribute("resultFruit",resultFruit);
return "fruits/resultFruitList";
}
}
class FruitsService{
public List<Fruits> queryFruitsList(){
List<Fruits> fruitsList = new ArrayList<Fruits>();
Fruits apple = new Fruits();
apple.setId(1);
apple.setName("红富士苹果");
apple.setPrice(2.3);
apple.setProducing_area("山东");
Fruits banana = new Fruits();
banana.setId(2);
banana.setName("香蕉");
banana.setPrice(1.5);
banana.setProducing_area("上海");
fruitsList.add(apple);
fruitsList.add(banana);
return fruitsList;
}
}
可以在执行queryFruitByID请求时,为其指定一个参数id。由于通过RequestMapping的method属性指定了请求类型为GET类型,所以把项目部署在Tomcat上后,可以使用下面的URL请求:http://localhost:8080/demo/query/queryFruitByID.action?id=1就可以获得id为1的水果商品的详细信息,结果如图所示:
当然,如果参数名字不为“id”,绑定就不会成功,不过可以通过使用注解的方式为请求参数指定别名。注解@RequestParam可以对自定义简单类型的参数进行绑定,即如果使用@RequestParam,就无须设置Controller方法的形参名称与request传入的参数名称一致。而不使用@RequestParam时,就需要Controller方法的形参名称与request传入的参数名称一致,这样才能够绑定成功。
假设执行queryFruitByID请求时,传入的id属性名称为fruit_id,而Java代码使用的是驼峰命名,那么可以通过@RequestParam注解来指定绑定名称,而在形参中继续使用驼峰命名,Controller具体代码如下:
package com.ccff.controller;
import com.ccff.model.Fruits;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
//基于注解的Handler类
//使用 @Controller来标识它是一个控制器
@Controller
@RequestMapping("/query")
public class FruitsController {
private FruitsService fruitsService = new FruitsService();
//根据水果id查询水果信息
@RequestMapping(value = "/queryFruitByID.action", method = RequestMethod.GET)
public String queryFruitByID(Model model, @RequestParam(value = "fruit_id") int fruitId){
//模拟service获取水果商品列表
List<Fruits> fruitsList = fruitsService.queryFruitsList();
List<Fruits> resultFruit = new ArrayList<>();
for (Fruits fruits : fruitsList){
if (fruits.getId() == fruitId)
resultFruit.add(fruits);
}
//将结果放到model中传到显示页面中
model.addAttribute("resultFruit",resultFruit);
return "fruits/resultFruitList";
}
}
class FruitsService{
public List<Fruits> queryFruitsList(){
List<Fruits> fruitsList = new ArrayList<Fruits>();
Fruits apple = new Fruits();
apple.setId(1);
apple.setName("红富士苹果");
apple.setPrice(2.3);
apple.setProducing_area("山东");
Fruits banana = new Fruits();
banana.setId(2);
banana.setName("香蕉");
banana.setPrice(1.5);
banana.setProducing_area("上海");
fruitsList.add(apple);
fruitsList.add(banana);
return fruitsList;
}
}
再次将项目部署到Tomcat上后,在浏览器输入请求URL:http://localhost:8080/demo/query/queryFruitByID.action?fruit_id=1 ,可得如下结果,证明正确。
当Controller方法有多个形参时,如果请求中不包含其中的某个形参,此时是不会报错的,所以使用该参数时要进行空校验。如果要求绑定的参数一定不能为空,可以使用@RequestParam注解中的required属性来指定该形参是否必须传入,required属性为“true”指定参数必须传入。
修改Controller中的代码如下:
package com.ccff.controller;
import com.ccff.model.Fruits;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
//基于注解的Handler类
//使用 @Controller来标识它是一个控制器
@Controller
@RequestMapping("/query")
public class FruitsController {
private FruitsService fruitsService = new FruitsService();
//根据水果id查询水果信息
@RequestMapping(value = "/queryFruitByID.action", method = RequestMethod.GET)
public String queryFruitByID(Model model, @RequestParam(value = "fruit_id", required = true) int fruitId){
//模拟service获取水果商品列表
List<Fruits> fruitsList = fruitsService.queryFruitsList();
List<Fruits> resultFruit = new ArrayList<>();
for (Fruits fruits : fruitsList){
if (fruits.getId() == fruitId)
resultFruit.add(fruits);
}
//将结果放到model中传到显示页面中
model.addAttribute("resultFruit",resultFruit);
return "fruits/resultFruitList";
}
}
class FruitsService{
public List<Fruits> queryFruitsList(){
List<Fruits> fruitsList = new ArrayList<Fruits>();
Fruits apple = new Fruits();
apple.setId(1);
apple.setName("红富士苹果");
apple.setPrice(2.3);
apple.setProducing_area("山东");
Fruits banana = new Fruits();
banana.setId(2);
banana.setName("香蕉");
banana.setPrice(1.5);
banana.setProducing_area("上海");
fruitsList.add(apple);
fruitsList.add(banana);
return fruitsList;
}
}
在上面的例子,如果请求中没有包含fruit_id这个参数,直接请求URL:http://localhost:8080/demo/query/queryFruitByID.action ,则会报下图所示错误:
在Controller方法的形参中,如果有一些参数可能为空,但是又期望它们为空时有一个默认值,此时可以使用@RequestParam注解中的defaultValue属性来指定某些参数的默认值。
如将之前的Controller中queryFruitByID方法中的fruitId参数设置为空时的默认值为1,具体代码如下:
package com.ccff.controller;
import com.ccff.model.Fruits;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
//基于注解的Handler类
//使用 @Controller来标识它是一个控制器
@Controller
@RequestMapping("/query")
public class FruitsController {
private FruitsService fruitsService = new FruitsService();
//根据水果id查询水果信息
@RequestMapping(value = "/queryFruitByID.action", method = RequestMethod.GET)
public String queryFruitByID(Model model, @RequestParam(value = "fruit_id"