进阶

请求参数的绑定

请求参数绑定说明

  • 绑定机制

表单提交的数据都是key=value形式的,且都是字符串,比如:username=hehe&password=123

springMVC的参数绑定过程是将表单提交的请求参数作为控制器进行绑定的,底层使用反射的方式拿到值并且赋值给参数

要求:提交表单的**name**属性与参数的名称是相同的


基本数据类型和字符串

  • 只要方法中参数名字和表单中的值相同即可
  • 例如
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>SpringMVC入门程序</h3>

    <form action="test/hello">
        用户名:<input type="text" name="username"> <%--注意这里,username和后面参数的username相同--%>
        密码:<input type="text" name="password">  <%--注意这里,password和后面的password相同,--%>
        <input type="submit" value="提交">
    </form>

</body>
</html>
package com.bean.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(path = "/test")
public class HelloController {
    //使用参数绑定获得值
    @RequestMapping(path = "/hello")
    String sayHello(String username,Integer password){//这里的username和password和表单的name属性名称相同,所以才可以获取值
        System.out.println(username);
        System.out.println(password);
        return "/success";
    }

}

JAVABEAN实体

简单实体

  • 表单中的name属性要写为:username的形式,usernamedomain中的username相同
  • 参数列表中直接写domain的形式
package com.bean.domain;

import java.util.Date;

public class User {
    
    private String username;
    private String password;
    private Date birthday;

    public User() {
    }

    public User(String username, String password, Date birthday) {
        this.username = username;
        this.password = password;
        this.birthday = birthday;
    }

    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 Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>SpringMVC入门程序</h3>

    <form action="test/hello">
        用户名:<input type="text" name="username">
        密码:<input type="text" name="password">
        日期:<input type="text" name="birthday">  
        <%--这里的格式先写为yyyy/MM/dd的形式,因为这里涉及到一个类型转换的问题--%>
        
        <input type="submit" value="提交">
    </form>

</body>
</html>
package com.bean.controller;


import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(path = "/test")
public class HelloController {
    //使用参数绑定获得值
    @RequestMapping(path = "/hello")
    String sayHello(User user){//这里的user和前面表单上的是一个值
        System.out.println(user.getUsername());
        System.out.println(user.getPassword());
        System.out.println(user.getBirthday());
        return "/success";
    }
}

带有其他JavaBean的实体

package com.bean.domain;

public class Address {
    private String address;

    public Address() {
    }

    public Address(String address) {
        this.address = address;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}
package com.bean.domain;

import java.util.Date;

public class User {

    private String username;
    private String password;
    private Date birthday;
    private Address address;
    public User() {
    }

    public User(String username, String password, Date birthday, Address address) {
        this.username = username;
        this.password = password;
        this.birthday = birthday;
        this.address = address;
    }

    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 Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", birthday=" + birthday +
                ", address=" + address +
                '}';
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>SpringMVC入门程序</h3>

    <form action="test/hello">
        用户名:<input type="text" name="username">
        密码:<input type="text" name="password">
        日期:<input type="text" name="birthday">  
        <%--这里的格式先写为yyyy/MM/dd的形式,因为这里涉及到一个类型转换的问题--%>
        
        其他实体(Address)<input type="text" name="address.address">
        <%--这里注意,name中的前一个address指的是address实体类,但是后面的是实体类中的参数,是将address封装成了一个address对象--%>
        
        <input type="submit" value="提交">
    </form>


</body>
</html>
package com.bean.controller;


import com.bean.domain.Address;
import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(path = "/test")
public class HelloController {
    //使用参数绑定获得值
    @RequestMapping(path = "/hello")
    String sayHello(User user){//这里的user和前面表单上的是一个值
        System.out.println(user.getUsername());
        System.out.println(user.getPassword());
        System.out.println(user.getBirthday());
        System.out.println(user.getAddress());
        return "/success";
    }
}

集合

package com.bean.domain;

public class Address {
    private String address;

    public Address() {
    }

    public Address(String address) {
        this.address = address;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}
package com.bean.domain;

import java.util.Date;
import java.util.List;
import java.util.Map;

public class User {

    private String username;
    
    private List<Address> addresseList;
    
    private Map<String,Address> addressMap;

    public User() {
    }

    public User(String username, List<Address> addresseList, Map<String, Address> addressMap) {
        this.username = username;
        this.addresseList = addresseList;
        this.addressMap = addressMap;
    }

    public String getUsername() {
        return username;
    }

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

    public List<Address> getAddresseList() {
        return addresseList;
    }

    public void setAddresseList(List<Address> addresseList) {
        this.addresseList = addresseList;
    }

    public Map<String, Address> getAddressMap() {
        return addressMap;
    }

    public void setAddressMap(Map<String, Address> addressMap) {
        this.addressMap = addressMap;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", addresseList=" + addresseList +
                ", addressMap=" + addressMap +
                '}';
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>SpringMVC入门程序</h3>

    <form action="test/hello">
        用户名:<input type="text" name="username">
        List:<input type="text" name="addresseList[0].address">
        <%--这里直接将address封装进了addressList,这里的addressList和实体类中的是一个--%>

        Map:<input type="text" name="addressMap['one'].address">
        <%--这里直接将address封装进了addressMap,这里的addressMap和实体类中的是一个--%>

        <input type="submit" value="提交">
    </form>


</body>
</html>
package com.bean.controller;


import com.bean.domain.Address;
import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(path = "/test")
public class HelloController {
    //使用参数绑定获得值
    @RequestMapping(path = "/hello")
    String sayHello(User user){//这里的user和前面表单上的是一个值
        System.out.println(user.getUsername());
        System.out.println("----------------------------");
        System.out.println(user.getAddresseList());
        System.out.println("----------------------------");
        System.out.println(user.getAddressMap());
        return "/success";
    }
}
wangwu
----------------------------
[Address{address='wangwu'}]
----------------------------
{one=Address{address='wangwu'}}

中文乱码的解决方案

在书写中会存在中文乱码的情况,GET请求不会出现,但是POST请求会出现这种情况,所以我们应该配置一个过滤器来解决这种情况

  • web.xml中配置一个过滤
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!--配置过滤器 filter-class:这个类是spring提供的类 init-param:配置编码,设置为UTF-8-->
  <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>

  <!--配置前端控制器 org.springframework.web.servlet.DispatcherServlet:前端控制器,这个类是固定的-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <!--配置读取springmvc.xml这个配置文件-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>

    <!-- 一般来说DispatcherServlet是在请求的时候才创建的,但是这里配置的是服务器启动的时候就要创建 -->
    <load-on-startup>1</load-on-startup>
  </servlet>

  <!--url-pattern:值为"/",说明任何的类都会拦截-->
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

类型转换与自定义类型转换器

首先一定要了解到,前端给后端数据的时候给的都是字符串,是由spring帮我们把这些字符串转为的各种类型

在之前讲解JAVABEAN实体绑定的时候曾经使用过Date数据类型,当时说明的是日期要以yyyy/MM/dd的形式写,这是因为spring底层在帮我们做类型转换的时候就是以这种形式转换的

我们不想以这种方式进行转换,而想用一个我们比较熟悉的格式:yyyy-MM-dd的形式做一个类型转换

spring不提供这种格式,我们自己写一个

注意:这里只是以Date为例做一个转换器,当我们发现其他类型也不好使的时候,应该自己会写

  1. 自定义一个类,这个类要实现Converter<S,T>接口,该接口有两个泛型
    • S:表示接收到的,我们这里当然就是字符串了
    • T:表示要转换的类型
  2. Spring的配置文件中配置类型转换器
  3. annotation-driven标签中引用配置的类型转换服务

  • util
package com.bean.utils;

import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class StringToDateConverter implements Converter<String, Date> {//注意别导错包
    @Override
    public Date convert(String s) {
        SimpleDateFormat simpleDateFormat;
        try{
            if (StringUtils.isEmpty(s)){
                throw new NullPointerException("请输入要转换的日期");
            }
            simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            Date date = simpleDateFormat.parse(s);
            return date;
        } catch (ParseException e) {
            throw new RuntimeException("转换异常");
        }
    }
}
  • springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--这里开启注解扫描-->
    <context:component-scan base-package="com.bean"></context:component-scan>

    <!--配置视图解析器-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>


    <!--配置类型转换器工厂,通过源码我们可以看到这是一个类型转换器的工厂-->
    <bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <!--配置一个类型转化器,通过源码我们可以看到这里需要一个set,那么就配置一个set,指向我们的方法-->
        <property name="converters">
            <set>
                <!--配置自定义类型转换器-->
                <bean class="com.bean.utils.StringToDateConverter"></bean>
            </set>
        </property>
    </bean>

    <!--开启SpringMVC框架注解的支持,我们知道,这个注解已经默认配置了处理器映射器,处理器配置器,
    但是在spring中的类型转换器工厂中并没有我们想要的,所以我们自己加一个-->
    <mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
</beans>
  • jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>SpringMVC入门程序</h3>

    <form action="test/hello" method="post">
        Date:<input type="date" name="date"/>
        <input type="submit" value="提交">
    </form>


</body>
</html>
package com.bean.controller;


import com.bean.domain.Address;
import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RequestMethod;

import java.util.Date;

@Controller
@RequestMapping(path = "/test")
public class HelloController {
    //使用参数绑定获得值
    @RequestMapping(path = "/hello")
    String sayHello(Date date){//这里的user和前面表单上的是一个值
        System.out.println(date.toString());
        return "/success";
    }
}

常用注解

@RequestParam

作用:

  • 把请求中指定名称的参数给控制器中的形参赋值。

属性:

  • value:请求参数中的名称。
  • required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
package com.bean.controller;


import com.bean.domain.Address;
import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Date;

@Controller
@RequestMapping(path = "/test")
public class HelloController {

    @RequestMapping(path = "/hello")
    String sayHello(
            @RequestParam(value = "time",required = true) Date date,
            @RequestParam(value = "username",required = false) String username
    ){
        /*
        * 这里注意了,既然value中指定的就是time,那么在jsp中就不可以写date了,而是写time,然后time的值将会赋给date
        * 然后后面的username当然就是username,required指定了fase,说明不写也不会报错
        * */

        return "/success";
    }
}

@RequestBody

作用:

  • 用于获取请求体内容。直接使用得到是 key=value&key=value…结构的数据。
  • 必须是post,get 请求方式不适用

属性:

  • required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值为 false,get 请求得到是 null。

这里获取请求体的内容的时候,在控制器中的参数列表里直接写一个String body就行了,获取的内容是key=value&key=value的形式

package com.bean.controller;


import com.bean.domain.Address;
import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Date;

@Controller
@RequestMapping(path = "/test")
public class HelloController {

    @RequestMapping(path = "/hello")
    String sayHello(@RequestBody String body){
        /*
        * 获取请求体内容,get没有请求体,不适用
        *   required
        *       true:请求值必须有请求体
        *       false:请求可以没有请求体
        * */
        System.out.println(body);

        String[] split = body.split("&");
        for (String s : split) {
            System.out.println(s);
        }

        return "/success";
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>SpringMVC入门程序</h3>

    <form action="test/hello" method="post">

        Date:<input type="date" name="time"/>
        username:<input type="text" name="username"/>

        <input type="submit" value="提交">

    </form>


</body>
</html>
time=2019-12-11&username=zhangsan
time=2019-12-11			这里直接就是字符串形式的
username=zhangsan

@PathVaribale

作用:

  • 用于绑定 url中的占位符。

例如:请求 url/delete/{id},这个**{id}**就是 url占位符。

  • url支持占位符是 spring3.0 之后加入的。是 springmvc支持 rest 风格 URL 的一个重要标志。

属性:

  • value:用于指定 url中占位符名称。
  • required:是否必须提供占位符。

Rest风格URL

简单来说就是使用同一个地址和不同的网络访问进行不同的操作

例子:

/account/1 HTTPGET 得到id=1的account

/account/1 HTTPDELETE 删除id=1的account

/account/1 HTTPPUT 更新id=1的account


为什么要讲REST风格呢,因为我们使用@PathVaribale@RequestMapping可以自己形成这种风格


package com.bean.controller;


import com.bean.domain.Address;
import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

@Controller
@RequestMapping(path = "/test")
public class HelloController {

    /*因为浏览器只能模拟get和post两种请求,所以只写这两种*/

    /*这里是访问的find方法,通过POST请求*/
    @RequestMapping(value = "/find",method = RequestMethod.POST)
    String find(){
        System.out.println("POST方式查询所有用户");
        return "success";
    }

    @RequestMapping(value = "/find",method = RequestMethod.GET)
    String findAll(){
        System.out.println("GET方式查询所有用户");
        return "success";
    }

    /*访问的findById方法,通过占位符来表示查询的哪个id,uid就是绑定了参数列表中的uid*/
    @RequestMapping(value = "/find/{uid}",method = RequestMethod.GET)
    String findByID(@PathVariable(value = "uid") Integer id){
        System.out.println("根据id查询用户");
        return "seccess";
    }

    /*访问的findById方法,通过占位符来表示查询的哪个id,uid就是绑定了参数列表中的uid*/
    @RequestMapping(value = "/find/{uid}",method = RequestMethod.POST)
    String findByUID(@PathVariable(value = "uid") Integer id){
        System.out.println("根据id查询用户");
        return "seccess";
    }



}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>SpringMVC入门程序</h3>

    <%--使用get方式来查询所有用户--%>
    <a name="date" href="test/find">sayHelloGet</a>

    <%--使用post方法来访问所有用户--%>
    <form action="test/find" method="post"></form>

    <%--使用GET方式来查询特定用户,注意这种带有占位符的是直接/xxx,而不是?xxx=xxx的形式了,因为这是一个路径,不是参数--%>
    <a href="test/find/100"></a>

    <%--同样的,使用post来标定占位符的时候,也是要手动添加/xxx,因为这是路径不是参数--%>
    <form action="test/find/100"></form>

</body>
</html>

@RequestHeader

作用:

  • 用于获取请求消息头。

属性:

  • value:提供消息头名称
  • required:是否必须有此消息头

注:

  • 在实际开发中一般不怎么用。

<a href="springmvc/useRequestHeader">获取请求消息头</a>
@RequestMapping("/useRequestHeader")
public String useRequestHeader(@RequestHeader(value="Accept-Language",required=false)String requestHeader){
	System.out.println(requestHeader);
	return "success"; 
}

@CookieValue

作用:

  • 用于把指定 cookie 名称的值传入控制器方法参数。

属性:

  • value:指定 cookie 的名称。
  • required:是否必须有此 cookie。

package com.bean.controller;


import com.bean.domain.Address;
import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

@Controller
@RequestMapping(path = "/test")
public class HelloController {
    
    /*把名称为JSESSIONID的cookie值传给cookieValue*/
    @RequestMapping(value = "/hello")
    String sayHello(@CookieValue(value = "JSESSIONID",required = false) String cookieValue){
        System.out.println(cookieValue);
        return "/success";
    }
}

@ModelAttribute

作用:

  • 该注解是 SpringMVC4.3 版本以后新加入的。它可以用于修饰方法和参数。
  • 出现在方法上
  • 表示当前方法会在控制器的方法执行之前,先执行。
  • 它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
  • 出现在参数上

获取指定的数据给参数赋值。

属性:

  • value:用于获取数据的 key。key 可以是 POJO 的属性名称,也可以是 map 结构的 key。

应用场景:

  • 当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。

例如:

  • 我们在编辑一个用户时,用户有一个创建信息字段,该字段的值是不允许被修改的。
  • 在提交表单数据是肯定没有此字段的内容,一旦更新会把该字段内容置为 null,此时就可以使用此注解解决问题。

因为被@ModleAttribute修饰的方法会先执行,所以首先解决一些问题,比如用户传过来的值不够完整,或者解决乱码错误等


现在我们的情景就是从表单提交过来的不完整,一共有数据:username,password,date。

但是只提交了两个数据usernamepassword

现在我们做的工作就是把没有提交的数据默认就是数据库中原来的数据


方法一:有返回值的

package com.bean.domain;

import java.util.Date;
import java.util.List;
import java.util.Map;

public class User {

    private String username;

    private String password;
    
    private Date date;

    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 Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", date=" + date +
                '}';
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>SpringMVC入门程序</h3>

    <a href="test/hello?username=123&password=123">hello</a>

</body>
</html>
package com.bean.controller;


import com.bean.domain.Address;
import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

@Controller
@RequestMapping(path = "/test")
public class HelloController {

    @RequestMapping(value = "/hello")
    String sayHello(User user){
        System.out.println(user);
        return "/success";
    }

    //这个方法先执行,假装查询了数据库
    @ModelAttribute
    User model(User user){
        System.out.println("model执行了:"+user);
        user.setUsername(user.getUsername());
        user.setPassword(user.getPassword());
        user.setDate(new Date());
        return user;
    }
    
}

方法二:没有返回值的

package com.bean.controller;


import com.bean.domain.Address;
import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.Map;

@Controller
@RequestMapping(path = "/test")
public class HelloController {

    @RequestMapping(value = "/hello")
    String sayHello(@ModelAttribute(value = "userMap") User user){//获取了map集合里的对象
        System.out.println(user);
        return "/success";
    }

    //这个方法先执行,假装查询了数据库,放到了Map集合里
    @ModelAttribute
    void model(User user, Map<String,User> map){
        System.out.println("model执行了:"+user);
        user.setUsername(user.getUsername());
        user.setPassword(user.getPassword());
        user.setDate(new Date());
        map.put("userMap",user);
    }

}

SessionAttribute

作用:

  • 用于多次执行控制器方法间的参数共享。
  • 只能放到类上

属性:

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

向Session域中存值

  • index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>SpringMVC入门程序</h3>

    <a href="test/hello">SessionAttributes</a>

</body>
</html>
  • 控制器
package com.bean.controller;


import com.bean.domain.Address;
import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.Map;

@Controller
@RequestMapping(path = "/test")
@SessionAttributes(value = {"message"})//这里是将request域中的message再放到session中,注意,此注解只能加到类上
public class HelloController {

    @RequestMapping(value = "/hello")
    String sayHello(Model model){
        System.out.println("控制器方法执行了,底层会放到Request域中");

        //底层会放到Request域中,但是这个要配合直接@SessionAttribute才可以放到session域中
        model.addAttribute("message","消息内容");

        return "/success";
    }

}
  • success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%--注意这里isELIgnored="false",表示不忽略EL表达式--%>

<html>
<head>
    <title>Title</title>
</head>
<body>

    <h3>跳转成功</h3>
    <%--获取值--%>
    ${sessionScope.get("message")}

</body>
</html>

从Session域中取值

刚才我们所讲的Model其实是一个接口,这个接口没有取值的方法,但是他的实现类有,比如一个:ModelMap

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>SpringMVC入门程序</h3>

    <a href="test/hello">SessionAttributes</a>

</body>
</html>
package com.bean.controller;


import com.bean.domain.Address;
import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.Map;

@Controller
@RequestMapping(path = "/test")
@SessionAttributes(value = {"message"})//这里是将request域中的message再放到session中,注意,此注解只能加到类上
public class HelloController {

    @RequestMapping(value = "/hello")
    String sayHello(ModelMap model){

        /*从Session中取值,严格来说并不是session,而是request域*/
        System.out.println(model.get("message"));

        return "/success";
    }

    @ModelAttribute
    void addValue(ModelMap model){
        /*存值到request域中*/
        model.addAttribute("message","消息内容");
    }

}

相应视图和结果视图

返回值分类

字符串

controller方法返回字符串可以采用指定逻辑视图名,使用视图解析器解析地址

//指定逻辑视图名,经过视图解析器解析为 jsp 物理路径:/WEB-INF/pages/success.jsp
@RequestMapping("/testReturnString")
public String testReturnString() {
  System.out.println("AccountController 的 testReturnString 方法执行了。。。。");
  return "success"; 
}

void

使用HttpServletRequestHttpServletResponse

@RequestMapping(value = "/hello")
void sayHello(HttpServletRequest request, HttpServletResponse response) throws IOException {

    //1. 转发,请求一次,因为不使用视图解析器了,所以要手动指定文件和文件名
    request.getRequestDispatcher("/WEB-INF/pages/success.jsp");

    //2. 重定向,请求多次,重定向不可以直接访问到WEB-INF下的内容
    response.sendRedirect("test");

    //3. 通过response直接输出结果
    response.setCharacterEncoding("utf-8");
    response.setContentType("application/json;charset=utf-8");
    response.getWriter().write("直接输出结果");

}

ModleAndView

@RequestMapping(value = "/hello")
ModelAndView sayHello(){
  //SpringMVC为我们提供的这个方法,可以存值(底层向request域),可以跳转界面
  ModelAndView modelAndView = new ModelAndView();
  
  //存值
  modelAndView.addObject("key","value");
  
  //跳转界面
  modelAndView.setViewName("success");
  
  return modelAndView;
}

转发和重定向

forward

注意在使用:forward:xxx这种写法的时候,必须要采用实际的运行路径

这种方法相当于:request.getRequestDispatcher("**url**").forward(request,response)

可以写页面,也可以写其他的控制器方法

@RequestMapping(value = "/hello")
String sayHello(){
  
  //在使用了字符串的情况下,默认就是请求转发
//        return "success";
  
  //或者我们也可以写为:
  return "forward:/WEB-INF/pages/success.jsp";
}

Redirect

@RequestMapping(value = "/hello")
String sayHello(){
  
  //重定向,注意/WEB-INF下的文件重定向是找不到的,只有请求转发能找到
  return "redirect:test";
}

ResponseBody响应JSON

  • 使用说明

作用:

该注解用于将 Controller的方法返回的对象,通过 HttpMessageConverter 接口转换为指定格式的数据,

如:json,xml 等通过 Response 响应给客户端

  • 使用示例

需求:

  • 使用@ResponseBody 注解实现将 controller方法返回对象转换为 json响应给客户端。

前置知识点:

  • Springmvc默认用 MappingJacksonHttpMessageConverterjson数据进行转换,需要加入jackson的包。
<!--Jackson required包-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.0</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.0</version>
</dependency>

在开始之前,说明一下几个问题:

  1. 首先在webapp下面建立一个js的文件夹,里面放jq文件,然后引入到jsp
  2. 配置前端控制器

我们之前配置的前端控制器都是拦截所有的请求,也包括拦截jq的请求,拦截之后就不会响应了

所以我们应该在前端控制器中配置好,让他不要拦截

不仅仅是配置js,而且应该配置所有的静态资源

springmvc.xml告诉前端控制器,哪些文件不要拦截

  • 前端控制器再看一遍
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="
 http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/mvc
 http://www.springframework.org/schema/mvc/spring-mvc.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd">

  <!--这里开启注解扫描-->
  <context:component-scan base-package="com.bean"></context:component-scan>

  <!--配置视图解析器-->
  <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages"></property>
        <property name="suffix" value=".jsp"></property>
  </bean>

  <!--告诉前端控制器,什么内容不要拦截,这里暂时是js下的所有文件
  血泪的错误:location在前边,mapping在后面,location后面不加**
  如果还不行,把target删了重新运行一遍
-->
  <mvc:resources location="/js/" mapping="/js/**"></mvc:resources>


  <mvc:annotation-driven/>
</beans>
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <!--配置过滤器 filter-class:这个类是spring提供的类 init-param:配置编码,设置为UTF-8-->
    <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>

    <!--配置前端控制器 org.springframework.web.servlet.DispatcherServlet:前端控制器,这个类是固定的-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <!--配置读取springmvc.xml这个配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>

        <!-- 一般来说DispatcherServlet是在请求的时候才创建的,但是这里配置的是服务器启动的时候就要创建 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--url-pattern:值为"/",说明任何的类都会拦截-->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

JSON

  • JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Title</title>
        <script src="js/jquery.min.js"></script>

    </head>
    <body>
        <h3>SpringMVC入门程序</h3>

        <input id="test" type="button" value="value"/>

        <script>

            $("#test").click(function(){
                $.ajax({
                    type:"post",
                    url:"test/hello",
                    contentType:"application/json;charset=utf-8",
                    data:'{"username":1,"age":"20"}',
                    dataType:"json",
                    success:function(data){
                        alert(data);
                    }
                });
            });
        </script>

    </body>
</html>
  • JavaBean
package com.bean.domain;

import java.io.Serializable;

public class User implements Serializable {

    private String username;
    private Integer age;

    public String getUsername() {
        return username;
    }

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

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}
  • Controller
package com.bean.controller;


import com.bean.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.Map;

@Controller
@RequestMapping(path = "/test")
public class HelloController {

    @RequestMapping(value = "/hello")
    public @ResponseBody User sayHello(@RequestBody User user){//@ReponseBody会帮我们转为json数据
        System.out.println(user);//输出了前端传过来的json,但是后端使用jackson把json字符串封装到了user对象中

        User returnUser = new User();
        returnUser.setUsername("测试");
        returnUser.setAge(20);

        //返回的是user对象,但是前端接收到的仍然是json数据,使用的@ReopnseBody
        return returnUser;
    }

}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。