SSM之SpringMVC系列(二)---- 请求参数的绑定和常用的注解

上篇文章我们讲了SpringMVC 概述和SpringMVC 入门,现在来讲SpringMVC中请求参数的绑定和常用的注解

请求参数的绑定

绑定的机制

  • 当我们从表单提交数据到后台的时候,请求参数都是基于key=value的形式,SpringMVC 绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。譬如:
<a href="user/findAccount?accountId=10">查询账户</a>

上面代码的请求参数是:accountId=10

  • 此时我们利用 SpingMVC 就可以这样接收:
	/**
	* 查询账户
	* @return
	*/
	@RequestMapping("/user")
	public String findAccount(Integer accountId) {
    
    
		System.out.println("查询了账户。。。。"+accountId);
		return "success"; 
	}

支持的数据类型:

  • 基本类型参数:
    • 包括基本类型和String类型
  • POJO 类型参数:
    • 包括实体类,以及关联的实体类
  • 数组和集合类型参数:
    • 包括 List结构和Map结构的集合(包括数组)

SpringMVC 绑定请求参数是自动实现的,但是要想使用,必须遵循使用要求。

基本类型和 String 类型作为参数

  • 如果是基本类型或者 String 类型,要求我们的参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写),见以下示例:
  • param.jsp代码:
<%-- 请求参数绑定--%>
    <a href="param/testParam?username=zhang&password=123">请求参数绑定</a><hr/>
  • ParamController控制器代码
/**
 * 请求参数绑定
 */
@Controller
@RequestMapping("/param")
public class ParamController {
    
    

    /**
     * 请求参数绑定入门
     * @return
     */
    @RequestMapping("/testParam")
    public String testParam(String username,String password){
    
    
        System.out.println("用户名:" +username);
        System.out.println("密码:" +password);
        System.out.println("执行了...");
        return "success";
    }
  • 运行结果
    在这里插入图片描述

POJO 类型作为参数

  • 如果是 POJO 类型,或者它的关联对象, 要求表单中参数名称和 POJO 类的属性名称保持一致。并且控制器方法的参数类型是POJO 类型。特别地,如果 POJO 类中含有其他 POJO 类的引用,那么请求参数名称应该为 外部属性名.内部属性名,见以下示例:
  • param.jsp代码:
<form action="param/saveAccount" method="post">
        账号:<input type="text" name="username"><br/>
        密码:<input type="text" name="password"><br/>
        金额:<input type="text" name="money"><br/>
        用户姓名:<input type="text" name="user.uname"><br/>
        用户年龄:<input type="text" name="user.age"><br/>
        <input type="submit" value="提交"><br/>
    </form>
  • ParamController控制器代码
/**
     * 请求参数绑定把数据封装到JavaBean的类中
     * @return
     */
    @RequestMapping("/saveAccount")
    public String saveAccount(Account account){
    
    
        System.out.println("执行了...");
        System.out.println(account);
        return "success";
    }
  • 实体类 Account 类、User 类代码:
public class Account implements Serializable {
    
    

    private String username;
    private String password;
    private Double money;
    private User user;

    public String getUsername() {
    
    
        return username;
    }

    public void setUsername(String username) {
    
    
        this.username = username;
    }

    public String getPassword() {
    
    
        return password;
    }

    public void setPassword(String password) {
    
    
        this.password = password;
    }

    public Double getMoney() {
    
    
        return money;
    }

    public void setMoney(Double money) {
    
    
        this.money = money;
    }

    public User getUser() {
    
    
        return user;
    }

    public void setUser(User user) {
    
    
        this.user = user;
    }

    @Override
    public String toString() {
    
    
        return "Account{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", money=" + money +
                ", user=" + user +
                '}';
    }
}

public class User implements Serializable {
    
    
    private String uname;
    private Integer age;

    public String getUname() {
    
    
        return uname;
    }

    public void setUname(String uname) {
    
    
        this.uname = uname;
    }

    public Integer getAge() {
    
    
        return age;
    }

    public void setAge(Integer age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "uname='" + uname + '\'' +
                ", age=" + age +
                '}';
    }
}

  • 运行结果
    在这里插入图片描述

集合类型作为参数

  • 如果要绑定集合类型的参数,那么要求集合类型的请求参数必须在 POJO 类中,同时前端的请求参数名称和 POJO类的集合属性名称保持一致。当给 List集合中的元素赋值时,使用下标;当给 Map 集合中的元素赋值时,使用键值对。接收的请求参数是 json 格式数据。需要借助一个注解实现。
  • param.jsp代码:
<%--把数据封装Account类中,类中存在list和map的集合--%>
    <form action="param/saveAccount" method="post">
        账号:<input type="text" name="username"><br/>
        密码:<input type="text" name="password"><br/>
        金额:<input type="text" name="money"><br/>
        <%-- 绑定到 List 集合 --%>
        用户姓名:<input type="text" name="list[0].uname"><br/>
        用户年龄:<input type="text" name="list[0].age"><br/>
        <%-- 绑定到 map 集合 --%>
        用户姓名:<input type="text" name="map['one'].uname"><br/>
        用户年龄:<input type="text" name="map['one'].age"><br/>
        <input type="submit" value="提交"><br/>
    </form>
  • Account 类代码:
public class Account implements Serializable {
    
    

    private String username;
    private String password;
    private Double money;
    private List<User> list;
    private Map<String,User> map;
    //private User user;
    //getters and setters
    //...
    //toString()...
}

  • 运行结果
    在这里插入图片描述

请求参数乱码问题

  • 对于GET方式提交的请求,如果参数中含有中文,那么是不会乱码的,因为 Tomcat 8 已经将乱码问题解决了。如果 GET方式请求乱码,那么可以在 Tomcat 安装目录下的conf\server.xml中进行添加,如图:
    在这里插入图片描述

  • 而对于 POST 方式提交的参数乱码问题,则应该在 web.xml中配置一个 SpringMVC 提供的过滤器 CharacterEncodingFilter,见代码

<!-- 配置编码过滤器 -->
  <filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!-- 设置字符集 -->
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <!-- 过滤所有请求 -->
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

<Filter> 位置最好出现在 <servlet>之前

自定义类型转换器

  • 表单提交的任何数据类型全部都是字符串类型,但是后台定义Integer类型,数据也可以封装上,说明Spring框架内部会默认进行数据类型转换

  • 可是有的时候会出现这样的情况,前端提交了一个日期字符串,控制器方法使用 Date类型的变量进行接收,由于日期字符串有很多种格式,所以 SpringMVC 的内置转换器不一定能帮我们转换成Date 类型。

  • param.jsp代码:

 <%--    自定义类型转换器--%>
    <form action="param/saveUser" method="post">
        用户姓名:<input type="text" name="uname" /><br/>
        用户年龄:<input type="text" name="age" /><br/>
        用户生日:<input type="text" name="date" /><br/>
        <input type="submit" value="提交"><br/>
    </form>
  • ParamController控制器代码
    /**
     * 自定义类型转换器
     * @return
     */
    @RequestMapping("/saveUser")
    public String saveUser(User user){
    
    
        System.out.println("自定义类型转换器...");
        System.out.println(user);
        return "success";
    }
  • 运行结果:
    注意我写的日期格式
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 此时我们就可以根据自己的需求来定义自定义的类型转换器,要求实现 Converter<S,T>接口,其中 S 为待转换的类型, T 为目标类型。
/**
 * 字符串转日期
 */
public class StringToDateConverter implements Converter<String, Date> {
    
    

    /**
     * String source    传入进来字符串
     * @param source
     * @return
     */
    @Override
    public Date convert(String source) {
    
    
        //判断
        if (source == null){
    
    
            throw new RuntimeException("请传入数据");
        }
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        try {
    
    
            // 把字符串转换日期
            return df.parse(source);
        }catch (Exception e){
    
    
            throw new RuntimeException("数据类型转换出现错误");
        }
    }
}

  • 最后我们需要在 SpringMVC 的配置文件中配置该类型转换器
<!--  配置自定义转换器  -->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.cz.utils.StringToDateConverter"></bean>
            </set>
        </property>
    </bean>
    <!-- 开启SpringMVC框架注解的支持 -->
    <mvc:annotation-driven conversion-service="conversionService"/>

使用 Servlet 原生 API 作为参数

  • SpringMVC 支持使用原始 Servlet API对象作为方法参数,也就是说,我们可以把这些参数直接写在控制器方法的参数列表上,支持原始ServletAPI对象有:

  • HttpServletRequest HttpServletResponse HttpSession java.security.Principal Locale InputStream OutputStream
    Reader Writer

我们可以把上述对象,直接写在控制的方法参数中使用。

  • 示例代码:
/**
     * Servlet原生的API
     * @return
     */
    @RequestMapping("/testServlet")
    public String testServlet(HttpServletRequest request, HttpServletResponse response){
    
    
        System.out.println("Servlet原生的API...");
        System.out.println(request);
        HttpSession session = request.getSession();
        System.out.println(session);
        System.out.println(response);
        return "success";
    }

常用注解

@RequestParam

  • 作用:

    • 请求中指定名称的参数绑定到控制器方法的某个参数上
  • 属性:

    • value / name: 这两个属性互为别名,都是指定请求参数中的名称
    • required : 请求参数中是否必须提供此参数,默认值为true,表示必须提供,否则报错
  • anno.jsp代码:

    <%--常用的注解--%>
    <a href="anno/testRequestParam?name=哈哈">RequestParam</a>
  • 控制器代码:
/**
 * 常用注解
 */
@Controller
@RequestMapping("/anno")
public class AnnoController {
    
    

    @RequestMapping("/testRequestParam")
    public String testRequestParam(@RequestParam(name = "name") String username){
    
    
        System.out.println("执行了");
        System.out.println(username);
        return "success";
    }
}

  此时使用 @RequestParam("name") ,那么如果请求参数中没有name 参数,程序就会报错。同时,由于已经使用 @RequestParam进行参数的绑定,所以控制器方法的参数名称 username已经无法绑定了,也就是说,即使请求参数中有参数 username,那么也不会绑定到控制器方法的 username 参数上了。

@RequestBody

  • 作用:

    • 取请求体内容,得到的内容为key1=value1&key2=value2...,只适用 POST 方式提交的请求,因为GET 方式无请求体。
  • 属性:

    • required : 是否必须有请求体,默认值为true,表示必须有请求体,GET 方式请求会报错;当为 false时,GET 方式请求得到的为` null``
  • jsp代码:

<%--RequestBody注解--%>
    <form action="anno/testRequestBody" method="post">
        用户姓名:<input type="text" name="username" /><br/>
        用户年龄:<input type="text" name="age" /><br/>
        <input type="submit" value="提交" />
    </form>
  • 控制器代码
/**
     * 获取到请求体的内容
     * @return
     */
    @RequestMapping("/testRequestBody")
    public String testRequestBody(@RequestBody String body){
    
    
        System.out.println("执行了...");
        System.out.println(body);
        return "success";
    }

@PathVariable

  • 作用:

    • 将 URL 中的占位符绑定到控制器方法的参数上,例如 localhost:8080/springmvc/user/{id},这里的{id}就是一个占位符。
  • 属性:

    • value :指定 URL 中的占位符名称
    • required: 是否必须提供占位符
  • jsp代码:

<!-- PathVariable 注解 -->
    <a href="anno/testPathVariable/10">testPathVariable</a>
  • 控制器代码:
/**
     * PathVariable注解
     * @return
     */
    @RequestMapping(value="/testPathVariable/{uid}")
    public String testPathVariable(@PathVariable(name="uid") String id){
    
    
        System.out.println("执行了...");
        System.out.println(id);
        return "success";
    } 

该注解可以用于绑定 REST 风格 URL 的占位符,restful 有以下特点:

  • 资源(Resources):每一个 URI 对应着一个资源
    • 它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个 URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要获取这个资源,访问它的 URI 就可以,因此 URI
      即为每一个资源的独一无二的识别符。
  • 表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层 (Representation)。
    • 比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
  • 状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。
    • HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(StateTransfer)。而这种转化是建立在表现层之上的,所以就是
      “表现层状态转化”。
    • 具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET 、POST 、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE
      用来删除资源
  • restful 的示例
    • /account/1 HTTP GET : 得到 id = 1 的 account
    • /account/1 HTTP DELETE: 删除 id = 1 的 account
    • /account/1 HTTP PUT: 更新 id = 1 的 account
      在这里插入图片描述

@RequestHeader

  • 作用:

    • 用于获取请求消息头
  • 属性:

    • value: 要获取的消息头名称
    • required: 是否必须有此消息头
  • jsp代码:

<!-- RequestHeader注解 -->
<a href="anno/testRequestHeader">RequestHeader</a>
  • 控制器代码:
/**
     * 获取请求头的值
     * @param header
     * @return
     */
    @RequestMapping(value="/testRequestHeader")
    public String testRequestHeader(@RequestHeader(value="Accept") String header, HttpServletRequest request,HttpServletResponse response) throws IOException {
    
    
        System.out.println("执行了...");
        System.out.println(header);
        // return "success";
        // response.sendRedirect(request.getContextPath()+"/anno/testCookieValue");
        return "redirect:/param.jsp";
    }

@CookieValue

  • 作用:

    • 用于获取 Cookie的值
  • 属性:

    • value: 要获取的Cookie名称
    • required: 是否必须有此 Cookie
  • jsp代码:

<!-- CookieValue注解 -->
<a href="anno/testCookieValue">CookieValue</a>
  • 控制器代码:

    /**
     * 获取Cookie的值
     * @return
     */
    @RequestMapping(value="/testCookieValue")
    public String testCookieValue(@CookieValue(value="JSESSIONID") String cookieValue){
    
    
        System.out.println("执行了...");
        System.out.println(cookieValue);
        return "success";
    }

@ModelAttribute

  • 作用:

    • 用于修饰方法或者参数,如果修饰方法,那么该方法(有无返回值均可)会在控制器方法之前执行;如果修饰参数,可以获取指定的数据并赋值给参数。当表单提交的数据不是完整的实体类数据时,我们就可以借助该注解,对空属性进行预处理(譬如赋值为数据库记录中该对象原本的值)。
  • 属性:

    • value : 用于获取数据的keykey 可以是 POJO类的属性名称,也可以是Map集合的key
  • jsp代码:

<!-- ModelAttribute注解 -->
<form action="anno/testModelAttribute" method="post">
        用户姓名:<input type="text" name="uname" /><br/>
        用户年龄:<input type="text" name="age" /><br/>
        <input type="submit" value="提交" />
    </form>
  • 控制器代码(@ModelAttribute 修饰的方法带返回值)
/**
     * ModelAttribute注解
     * @return
     */
    @RequestMapping(value="/testModelAttribute")
    public String testModelAttribute(User user){
    
    
        System.out.println("testModelAttribute执行了...");
        System.out.println(user);
        return "success";
	}
	//带返回值,对提交数据进行预处理
    @ModelAttribute
    public User showUser(String uname){
    
    
        System.out.println("showUser执行了...");
        // 通过用户查询数据库(模拟)
        User user = new User();
        user.setUname(uname);
        user.setAge(20);
        user.setDate(new Date());
        return user;
    }
  • 控制器代码(@ModelAttribute 修饰的方法不带返回值)
/**
     * ModelAttribute注解
     * @return
     */
    @RequestMapping(value="/testModelAttribute")
    public String testModelAttribute(@ModelAttribute("abc") User user){
    
    
        System.out.println("testModelAttribute执行了...");
        System.out.println(user);
        return "success";
    }
 	//不带返回值,将数据放入 Map 集合中
 	@ModelAttribute
    public void showUser(String uname, Map<String,User> map){
    
    
        System.out.println("showUser执行了...");
        // 通过用户查询数据库(模拟)
        User user = new User();
        user.setUname(uname);
        user.setAge(20);
        user.setDate(new Date());
        map.put("abc",user);
    }

@SessionAttributes

  • 作用:

    • 可以将数据存入Session域,使得数据在多次执行控制器方法的时候共享,只能作用在类上
  • 属性:

    • value: 用于指定存入的属性名称
    • type : 用于指定存入的数据类型
  • jsp代码:

<!-- SessionAttributes注解 -->
    <a href="anno/testSessionAttributes">testSessionAttributes</a>
    <a href="anno/getSessionAttributes">getSessionAttributes</a>
    <a href="anno/delSessionAttributes">delSessionAttributes</a>
  • 控制器代码
/**
 * 常用的注解
 */
@Controller
@RequestMapping("/anno")
@SessionAttributes(value={
    
    "msg"})   // 把msg=美美存入到session域对中
public class AnnoController {
    
    
	/**
     * SessionAttributes的注解
     * @param model
     * @return
     */
    @RequestMapping(value="/testSessionAttributes")
    public String testSessionAttributes(Model model){
    
    
        System.out.println("testSessionAttributes...");
        // 底层会存储到request域对象中
        model.addAttribute("msg","美美");
        return "success";
    }

    /**
     * 获取值
     * @param modelMap
     * @return
     */
    @RequestMapping(value="/getSessionAttributes")
    public String getSessionAttributes(ModelMap modelMap){
    
    
        System.out.println("getSessionAttributes...");
        String msg = (String) modelMap.get("msg");
        System.out.println(msg);
        return "success";
    }

    /**
     * 清除
     * @param status
     * @return
     */
    @RequestMapping(value="/delSessionAttributes")
    public String delSessionAttributes(SessionStatus status){
    
    
        System.out.println("getSessionAttributes...");
        status.setComplete();
        return "success";
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43844418/article/details/113853171
今日推荐