SpringMVC配置

一、环境搭建

概述

SpringMVC是Spring旗下的一个独立的模块,在容器工厂之上的模块(Spring的子容器);在SpringMVC的配置中,完全可以使用spring容器的配置方式。只不过springMVC容器中,只配置控制层相关的bean注入;Spring容器配置service层和dao层。

和spring类似,主要功能就是解耦合;同时SpringMVC又包含了MVC框架架构;里面的前端控制器用于和视图层交流;后端控制器用于和业务层交流。

配置

SpringMVC配置步骤:

1.准备web环境

2.导包,在web.xml中配置前端控制器的访问

3.并初始加载SpringMVC的容器(产生springMVC容器配置)

4.编写后端控制器操作,里面的功能看成是一个一个的handler(功能)

SpringMVC依赖包的导入:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.1.6.RELEASE</version>
</dependency>

web.xml中配置前端控制器

<servlet>
    <servlet-name>mvc</servlet-name>
    <!-- 真实前端控制器的类路径 -->
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param><!--通过初始化参数加载springMVC容器 -->
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:mvc.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>mvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

后端控制器创建,并产生目标handler

@Controller //产生控制层bean <bean id="userController" ...
@RequestMapping("/user")  //请求路径,可选,建议加上,用于区分模块
public class UserController {
    //访问:http://localhost:8080/user/test1
    @RequestMapping("/test1") //用于区分下面的handler(方法)
    public String test1(){  //返回类型统一String
        System.out.println("hello,SpringMVC");
        return "a";  //跳转默认为:转发
    }
}

访问路径:http://localhost:8080/user/test1

二、请求参数

springMVC中的参数接收,指的是从前端传入的参数的接收;一定要和mybatis参数绑定区分开

变量收参

前端参数匹配上handler的变量名后,值自动注入。

@RequestMapping("/param1")
//Date类型默认传参格式为:2023/08/09
//使用注解DateTimeFormat修改格式:2023-08-09
public String param1(Integer id, String name, @DateTimeFormat(pattern="yyyy-MM-dd") Date birthday){
    System.out.println(id+"--"+name+"--"+birthday);
    return "a";
}

访问路径:http://localhost:8080/user/param1?id=2&name=zs&birthday=2023-08-09

实体收参

请求参数为实体对象,传的参数与实体属性一致,则值自动注入。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private  Integer id;
    private  String name;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
}
@RequestMapping("/param2")  //实体收参
public String param2(User user){
    System.out.println(user);
    return "a";
}

数组收参

往往前端相同参数名,匹配不同的value值,在目标handler中使用数组参数接收。

数组名与参数名也是要一致,才能接收到。

<form action="/user/param3">
    爱好:<input type="checkbox" name="love" value="eat">吃
    <input type="checkbox" name="love" value="drink">喝<br>
    <input type="submit" value="提交">
</form>
@RequestMapping("/param3")  //数组收参
public String param2(String[] love){
System.out.println(Arrays.toString(love));
return "a";
}

集合收参(了解)

此处的集合收参,本质上讲,还是实体收参,只不过属性为集合。

<form action="/user/param4">
    用户名:<input type="text" name="users[0].name" value="zs1" /><br>
    密码:<input type="text" name="users[0].id" value="1" /><br>

    用户名:<input type="text" name="users[1].name" value="zs2" /><br>
    密码:<input type="text" name="users[1].id" value="2" /><br>
    <input type="submit" value="提交">
</form>
@RequestMapping("/param4")  //集合收参(了解)
//本质上还是实体收参,实体中的集合属性名,要和传参的一致
//且集合中的实体元素属性,和前端传入的属性也要一致
public String param4(UserList userList){
    for(User u :userList.getUsers()){
        System.out.println(u);
    }
    return "a";
}

路径传参

通过路径的方式,将参数传递

@RequestMapping("/param5/{id}")  //路径传参
//@PathVariable("id"):如果参数名和路径{名}一致,则可省略"id",直接写成@PathVariable
public String param4(@PathVariable("id") Integer id){
    System.out.println("路径传参:"+id);
    return "a";
}

访问路径:http://localhost:8080/user/param5/666

乱码处理

post请求中的中文参数传递,则需要处理乱码;之前的做法可以通过request.setCharactorEncoding(“utf-8”);,现在交给编码过滤器类中,完成此操作

<filter>
    <filter-name>en</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param> <!-- 初始化参数,将名为encoding的utf-8值注入到编码类中 -->
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>en</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

三、跳转

有两种跳转方式,转发与重定向。

转发

可以转发到JSP页面,也可以转发到后端控制器路径

//转发:
@RequestMapping("/for1")
public String for1(){ //转发到jsp页面
    System.out.println("for1...");
    return "forward:/a.jsp"; //内部识别转发前缀,后面内容忽略视图解析器拼接
}

//转发2
@RequestMapping("/for2")
public String for2(){ //转发到后端控制器
    System.out.println("for2...");
    return "forward:/user/for1"; //内部识别转发前缀,后面内容忽略视图解析器拼接
}

重定向

与转发类似,可重定向到JSP页面,也可重定向到后端控制器路径

//重定向1:跳转到jsp页面
@RequestMapping("/red1")
public String red1(){
    System.out.println("red1...");
    return "redirect:/a.jsp"; //重定向前缀,后面内容忽略视图解析器拼接
}

//重定向2:跳转到后端控制器handler
@RequestMapping("/red2")
public String red2(){
    System.out.println("red2...");
    return "redirect:/user/red1"; //重定向前缀,后面内容忽略视图解析器拼接
}

细节:

进行增删改操作时,往往使用重定向;避免重复出现变更异常

进行查询操作时,往往使用转发;实时更新数据。

四、传值

传值主要讲返回结果后的值的传递。通常需要使用request或session来存值

使用request必须讲Servlet、JSP,jstl依赖包导入

request与session

基本的存值方式:使用request与session

//传值:
@RequestMapping("/resValue1")
//只要写了request和session参数,则前端控制器会帮我们注入对象
public String resValue1(HttpServletRequest request, HttpSession session){
    request.setAttribute("id",100); //request存值
    session.setAttribute("name","zs"); //session存值
    session.setAttribute("date",new Date());
    return "a"; //默认是转发的
}

前端使用EL表达式${id},${name}来获取值。

${id}<br> <%-- 优先接收request,session,context --%>
${name}<br>
<fmt:formatDate value="${date}" pattern="yyyy-MM-dd"/><br>

Model

Model存值和request是一致的。

@RequestMapping("/resValue2")  //Model存值是默认存到request中的
public String resValue2(Model model){
    model.addAttribute("age",30);
    return "a";
}

前端取值:

<%-- requestScope:取reqeust的值 只能取自身域存储的值 --%>
request:${requestScope.age}<br>
session:${sessionScope.age}<br>

ModelAndView

ModelAndView既可以存值,又可以展示视图

Model表示存到request域;View表示可以展示视图;handler中可以返回该类型;视图解析器可以解析到

@RequestMapping("/resValue3")  //ModelAndView存值
public ModelAndView resValue3(){
    ModelAndView mav = new ModelAndView(); //实例化对象并存值及返回
    mav.setViewName("a");
    mav.addObject("love","吃");
    return mav;
}

jsp展示中获取数据:

love1:${requestScope.love}<br>
love2:${sessionScope.love}<br>

@SessionAttributes

该注解,可以将Model中的值拷贝到Session中,这样request和session都有存值。

@SessionAttributes({"sex"}) //sex属性,将model的值从requst转session
public class UserController {
    @RequestMapping("/resValue4")  //Model转session存值
    public String resValue4(Model model){
        model.addAttribute("sex","nan"); //sex存到了session中
        return "a";
    }

    @RequestMapping("/resValue5")  //Model转session存值
    public String resValue5(SessionStatus ss){
        ss.setComplete(); //移除SessionAttributes中session的存志
        return "a";
    }
}

JSP获取数据:

<%-- 两个都能显示值 除非使用SessionStatus的移除,session的值就没了 --%>
sex1:${requestScope.sex}<br>  
sex2:${sessionScope.sex}<br>

五、静态资源

问题

在前端的定位中,html,css,js以及图片都是属于静态资源。静态资源的访问,在全局web.xml中使用了映射路径"/“的方式,去处理静态资源。但是,在我们在局部的web.xml中配置前端控制器时,使用到了”/“路径,所以会导致冲突,冲突后,局部优先,前端控制器可以使用;但静态资源的访问则失效了。

处理方案

方式1:将前端控制器的映射路径改为:*.do (不推荐)

这种方式,在后端控制器中,每一个handler都需要带上*.do的路径,非常麻烦。

方式2:生成静态资源映射路径

<!-- 静态处理2:静态资源的映射路径方式
         mapping="/html/**":我们要访问的映射路径
         location="/hhh/: 映射到真实的路径中
         弊端:每个真实的静态资源目录,都需要有映射路径
    -->
<mvc:resources mapping="/html/**" location="/hhh/" />

方式3:默认的静态资源处理(推荐)

<!-- 在后端控制器中,产生一个映射路径为/**处理静态资源的目标handler;
      它的优先级最低;其他的资源处理不了,则会通过该目标handler处理静态资源 -->
<mvc:default-servlet-handler />

六、JSON处理

概述

在目标handler中返回的是json数据(字符串); 在前端页面中可以得到该数据并进行展示

说明:之前handler返回字符串时,需要进行视图解析;例如,返回 a; 转成转发到/a.jsp中; 需要加入@ResponseBody注解,表示无需视图解析;返回的就是字符串内容。

@ResonpseBody

该注解,如果handler的返回值是字符串,则直接返回字符串(类似resp.getWriter().write())

@RequestMapping("/test1")
@ResponseBody
public String test1(){
    System.out.println("test...");
    return "test1";
}

@RequestMapping(value = "/test2",produces="text/html;charset=utf-8")
@ResponseBody
public String test2(){
    //resp.setContentType("text/html;charset=utf-8");
    System.out.println("test2...");
    return "你好";
}

如果返回的是对象,则需要JSON解析器将对象解析成json字符串。需要使用jackson包进行解析

<!-- Jackson springMVC默认的Json解决方案选择是 Jackson,所以只需要导入jackson的jar,即可使用。-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>

JSON解析的测试:

@ResponseBody
public User json1(){
    User user = new User();
    user.setName("zs");
    return user;  //返回对象;json解析器会将对象解析成json字符串
}

@RequestMapping("/json2") //@ResponseBody放到返回值旁边
public @ResponseBody List<User> json2(){
    List<User> list = new ArrayList<>();
    list.add(new User());
    list.add(new User());
    return list;
}

@RestController

@RestController=@ResonpseBode+@Controller

@RestController //等价于@Controller+@ResponseBody
public class JSONController {
     @RequestMapping("/json1")
    //@ResponseBody
    public User json1(){
        User user = new User();
        user.setName("zs");
        return user;  //返回对象;json解析器会将对象解析成json字符串
    }

    @RequestMapping("/json2") //@ResponseBody放到返回值旁边
    public /*@ResponseBody*/ List<User> json2(){
        List<User> list = new ArrayList<>();
        list.add(new User());
        list.add(new User());
        return list;
    }
}

@RequestBody

该注解与参数有关,可以将传的json字符串转为实体对象参数。

传入json字符串,需要使用ajax传参数,且使用post请求

<script src="js/jquery-1.11.min.js" />
<script>
    $(function(){
        $("#bb").click(function () {
            //ajax
            var user = {id:1,name:"张三"};
            $.ajax({
                url:'${pageContext.request.contextPath}/json3',
                type:'post',
                contentType:"application/json",//声明请求参数类型为 json
                data:JSON.stringify(user),// 转换js对象成json
                success:function(ret){
                    alert(ret);
                }
            });
        })
    })
    function aaa() {
        var xhr = new XMLHttpRequest();
        xhr.open("post","${pageContext.request.contextPath}/json3");
        xhr.setRequestHeader("content-type","application/json");//设置请求头
        xhr.send('{"id":1,"name":"shine"}');//传递json串

        xhr.onreadystatechange=function(){  //回调数据
            if(xhr.readyState==4&&xhr.status==200){
                alert(xhr.responseText);  //返回结果
            }
        }
    }
</script>

后端控制器中,使用@RequestBody将json数据解析成对象。

@RequestMapping("/json3")  //前端传入的是json数据,通过@RequestBody解析成对象
//实体属性与json的key要一致,才能注入值
public String json3(@RequestBody User user){
    System.out.println(user);
    return "ok";
}

七、总结与作业

总结

1.SpringMVC环境搭建(重点)
概述,SpringMVC的好处,与spring的关系
配置方式
2.请求参数(重点)
变量收参、实体收参,数组收参,集合收参(了解),路径传参,乱码处理
3.跳转
转发-转发到jsp,转发到handler;重定向和转发类似
4.传值(重点)
request与session,model,modelandview,@SessionAttributes
5.静态资源
静态资源处理与前端控制器的路径冲突,需要处理
6.JSON处理(重点)
@ResonpseBody使用-与返回相关、@RestController,@RequestBody-与参数相关
返回对象的解析-解析成json数据

作业

1.springMVC配置,测试访问到目标handler
2.创建Student实体类,并通过实体收参,将表单中注册的学生信息接收并打印
3.在目标handler中,使用jacksoon解析器,返回json数据(返回对象解析成json数据)
4.使用ajax传递json字符串,在handler中解析成对象。