SpringMVC

SpringMVC

简介

zpLUCq.png

SpringMVC和MVC的关系

MVC架构是表现层的设计模型,M(model模型)、V(View视图)、C(controller控制器),SpringMVC也叫Spring web MVC,是专门用来做web层开发的,可以极大简化基于MVC架构的表现层开发。

SpringMVC和Spring的关系

SpringMVC是Spring的一个框架,实际上属于Spring的一个模块。

SpringMVC其实也是一个容器,和spring容器一样,不过它放的的控制层对象。

使用注解@Controller注册控制层对象,把对象放在控制层容器中,把创建的对象当做控制器使用,这个可以控制器可以像Servlet一样能够接收客户端的请求,并显示结果。

SpringMVC和Servlet的关系

SpringMVC可以理解为Servlet的升级版,SpringMVC底层也使用了Servlet,在Servlet基础上添加了功能,但是@Controller注册的对象只是一个普通对象,不是一个Servlet对象,但是SpringMVC赋予了控制器对象额外的功能,使拥有Servlet功能。

原理:SpringMVC有一个Servlet对象叫做DispacherServlet,该类负责接收用户所有的请求,即用户会先请求DispacherServlet,然后DispacherServlet负责转发请求给具体的Controller,最后真正处理请求的是Controller对象,而DispacherServlet对象只是充当拦截所有请求转发的角色。

控制器对象

使用注解@Controller注解创建出来来的对象,该对象交给SpringMVC容器管理,该对象作为控制器使用,能够处理请求,显示结果,但本质不是Servlet,使用和Servlet一样。

入门程序

1、使用IDEA创建一个web项目。

2、导依赖

spring-webmvc依赖,它会自动导spring的相关依赖

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

3、在web.xml注册SpringMVC核心对象DispacherServlet

  • 配置DispacherServlet对象要设置springmvc核心配置文件
  • 配置DispacherServlet对象要处理的映射,处理所有的映射请求,但不包括静态资源,可以使用自定义扩展名,扩展自定义扩展名的请求。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                  http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--        自定义springMVC配置文件位置-->
        <init-param>
<!--            配置文件属性-->
            <param-name>contextConfigLocation</param-name>
<!--            自定义配置文件路径-->
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
<!--        设置Tomcat启动就创建对象-->
        <load-on-startup>1</load-on-startup>
    </servlet>
<!--    设置拦截的映射-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
<!--        可以使用自定义扩展名,也可以使用/-->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

4、编写前端Jsp页面请求servlet

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/10
  Time: 15:50
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>springMVC.jsp</title>
</head>
<body>
<a href="<c:url value='/springmvc.do'/>">访问Controller</a>
</body>
</html>

5、创建Controller类

package com.lwg.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import sun.plugin2.main.server.ModalitySupport;

/**
 * @author lwg
 * @title
 * @create 2022/11/10 15:53
 */
//注册控制器,交给springMVC容器管理
@Controller
public class HelloController {
    //设置访问路径
    @RequestMapping(value = "/springmvc.do")
    public ModelAndView doHello(){
        ModelAndView modelAndView = new ModelAndView();
//        添加数据,框架会自动将添加到request域中
//        相当于request.setAttribute()
        modelAndView.addObject("msg","hello,SpringMVC");
//        设置视图,指定视图完整路径,框架会对视图执行forward操作
        modelAndView.setViewName("/hello.jsp");
//        返回ModelAndView对象
        return modelAndView;
    }
}

@Controller:注册控制器,交给SpringMVC容器管理

@RequestMapping:设置映射地址

6、编写另一张前端页面,接收服务器返回的信息。

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/10
  Time: 15:59
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>hello.jsp</title>
</head>
<body>
<h2>${msg}</h2>
</body>
</html>

7、编写springmvc核心配置文件,注册组件扫描器,注册视图注册器。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--    注册组件扫描器,扫描Controller注解的所在的包-->
    <context:component-scan base-package="com.lwg.controller"/>
</beans>

8、运行

z9PDEV.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H1AsRW7T-1669126130657)(C:\Users\35173\AppData\Roaming\Typora\typora-user-images\image-20221110162021525.png)]

入门程序执行过程:

1、启动Tomcat,读取web.xml配置文件,加载load-on-startup标签配置,立刻通过反射创建DispatcherServlet中央调度对象。

2、在创建DispatcherServlet对象会执行init()方法:

z9aBlD.png

3、在读取springmvc.xml配置文件时,会根据配置文件配置的组件扫描器的包扫描指定包,将有@Controller标识的类,创建控制对象并放进容器中。

4、当有请求到来的时候,会执行的Servlet的service方法,从而执行DispatcherServlet的doDispatch(req,resp)方法,方法中会根据uri中的请求地址交给指定的控制器指定的方法处理请求。

配置视图解析器

一些页面我们不允许用户直接通过地址栏输入地址访问,这时我们就可以将页面放在WEB-INF目录下,这样用户通过地址栏就访问不了。

但是我们转发请求时就要写多几层目录,这时我们可以在springmvc核心文件中配置视图解析器,统一为我们加前几层目录。

<!--    配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--        自动加前缀-->
        <property name="prefix" value="/WEB-INF/view"/>
<!--        自动加后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

配置多个地址请求访问同一个控制器

@RequestMapping({地址1,地址2})

package com.lwg.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author lwg
 * @title
 * @create 2022/11/10 15:53
 */
//注册控制器,交给springMVC容器管理
@Controller
public class HelloController {
    //设置访问路径
    @RequestMapping(value = {"/springmvc.do","/twomvc.do"})
    public ModelAndView doHello(){
        ModelAndView modelAndView = new ModelAndView();
//        添加数据,框架会自动将添加到request域中
//        相当于request.setAttribute()
        modelAndView.addObject("msg","hello,SpringMVC");
//        设置视图,指定视图完整路径,框架会对视图执行forward操作
        modelAndView.setViewName("/hello");
//        返回ModelAndView对象
        return modelAndView;
    }
}

SpringMVC执行流程

z9wn2T.png

执行流程分析:

1、浏览器提交到中央调度器。

2、中央调度器将请求转给处理器映射器。

3、处理器映射器根据请求找到处理该请求的处理器,并将其封装成处理执行链后返回到中央调度器。

4、中央调度器根据处理执行链找到处理器,找到处理器的处理器适配器。

5、处理器适配器调用执行处理器。

6、处理器将处理结果以及要跳转的页面封装成一个ModelAndView中并返回到处理器适配器。

7、处理器适配器将结果返回到中央调度器。

8、中央调度器调用视图解析器,将ModelAndView中的视图封装成视图对象。视图解析器将封装的视图对象返回到浏览器。

9、中央调用视图对象,进行数据填充,形成响应对象。

10、中央调度器响应浏览器。

@RequestMapping请求映射规则

value属性设置映射地址

@RequestMapping(value=“/addUser.do”)

通过@RequestMapping注解定义处理器对于请求的映射规则。注解可以定义在类上,也可以定义在方法上。value属性值常以"/"开始。

@Controlle定义的类可以定义多个方法,不同的的方法对应的uri是不一样,uri都被指定到@RequestMapping的value上。

如果类上@RequestMapping的value是表示模块名称,处理方法上的相应的处理。

案例:注解开发springmvc处理用户的CRUD请求

package com.lwg.controller;

import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.xpath.internal.operations.Mod;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author lwg
 * @title
 * @create 2022/11/10 22:04
 */
@Controller
@RequestMapping("/user")
public class UserController {
//    处理添加用户请求
    @RequestMapping("/addUser.do")
    public ModelAndView addUser(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","添加用户成功");
        modelAndView.addObject("method","执行了addUser方法");
        modelAndView.setViewName("add");
        return modelAndView;
    }
//    处理修改用户请求
    @RequestMapping("/updateUser.do")
    public ModelAndView updateUser(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","修改用户成功");
        modelAndView.addObject("method","执行了updateUser方法");
        modelAndView.setViewName("update");
        return modelAndView;
    }
//    处理删除用户请求
    @RequestMapping("/deleteUser.do")
    public ModelAndView deleteUser(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","删除用户成功");
        modelAndView.addObject("method","deleteUser");
        modelAndView.setViewName("delete");
        return modelAndView;
    }
}

映射请求方式

@RequestMapping的method属性设置处理的请求方式,只有满足对应的请求方式才处理。

method属性值是RequestMethod枚举常量:

z9DOQf.png

常用的有RequestMethod.GET(get请求)、RequestMethod.POST(post请求)。

@RequestMapping(value = "/addUser.do",method = RequestMethod.GET)
@RequestMapping(value = "/updateUser.do",method = RequestMethod.POST)

注意:如果不指定请求方式,则无论是GET还是POST或者还是其他都可以匹配,对请求方式没有要求。

客户端GET请求情况:浏览器地址访问、超链接访问、src资源请求、表单指定为get请求、ajax指定get

客户端POST请求情况:表单提交、ajax指定为post

案例:模拟测试查看数据用户列表和添加用户表单提交

  @RequestMapping(value = "userList.do",method = RequestMethod.GET)
    public ModelAndView userLsit(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","查看用户列表");
        modelAndView.addObject("method","userLsit");
        modelAndView.setViewName("/list");
        return modelAndView;
    }
//    处理添加用户请求
    @RequestMapping(value = "/addUser.do",method = RequestMethod.POST)
    public ModelAndView addUser(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","添加用户成功");
        modelAndView.addObject("method","执行了addUser方法");
        modelAndView.setViewName("/add");
        return modelAndView;
    }

处理器参数

处理器方法可以包含以下参数,这么参数系统自动赋值,直接调用即可。

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • 请求中携带的参数

案例:使用JavaWeb相关对象

@RequestMapping(value = "userList.do",method = RequestMethod.GET)
public ModelAndView userLsit(HttpServletRequest request, HttpServletResponse response, HttpSession session){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("msg","查看用户列表");
    modelAndView.addObject("method","userLsit");
    String userName = request.getParameter("userName");
    String password = request.getParameter("password");
    modelAndView.addObject("userName",userName);
    modelAndView.addObject("password",password);
    modelAndView.addObject("sessionId",session.getId());
    modelAndView.setViewName("/list");
    return modelAndView;
}

获取请求参数

接收前端传过来的参数,可以直接通过处理器方法的参数名称来接收,前提条件是要前端传过来的参数名称和处理器方法上的参数名称一致。

前端请求页面

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/10
  Time: 15:50
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>springMVC.jsp</title>
</head>
<body>
<p><a href="<c:url value='/user/userList.do?userName=张三&password=123456'/>">查看用户列表</a></p>
</body>
</html>

Controller处理器方法

@RequestMapping(value = "userList.do",method = RequestMethod.GET)
public ModelAndView userLsit(String userName,String password){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("msg","查看用户列表");
    modelAndView.addObject("method","userLsit");
    modelAndView.addObject("userName",userName);
    modelAndView.addObject("password",password);
    modelAndView.setViewName("/list");
    return modelAndView;
}

list.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/10
  Time: 22:14
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>list.jsp</title>
</head>
<body>
<h2>msg:${msg}</h2>
<h2>method:${method}</h2>
<h2>userName:${userName}</h2>
<h2>password:${password}</h2>
</body>
</html>

当请求参数名称和处理器方法上的参数名称不一致时,使用@RequestParam()注解指定请求参数名称。

@RequestMapping(value = "userList.do",method = RequestMethod.GET)
public ModelAndView userLsit(@RequestParam("userName") String name,@RequestParam("password") String pwd){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("msg","查看用户列表");
    modelAndView.addObject("method","userLsit");
    modelAndView.addObject("userName",name);
    modelAndView.addObject("password",pwd);
    modelAndView.setViewName("/list");
    return modelAndView;
}

当处理器的处理方法参数使用@RequestParam()指定请求参数名称后,默认前端请求必须携带@RequestParam()注解指定的参数名称过来,否则会报400错误。比如上面的案例,如果前端请求参数只有userName,或者只有password,或者两个没有,就会报400错误。这里我们可以设置@RequestParam()注解的required属性为false,表示该参数可传递可不传递。

@RequestMapping(value = "userList.do",method = RequestMethod.GET)
public ModelAndView userLsit(@RequestParam("userName") String name,@RequestParam(value = "password",required = false) String pwd){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("msg","查看用户列表");
    modelAndView.addObject("method","userLsit");
    modelAndView.addObject("userName",name);
    modelAndView.addObject("password",pwd);
    modelAndView.setViewName("/list");
    return modelAndView;
}

对象接收请求参数

在开发中如果请求参数多,可以使用对象来接收请求参数,对象的属性名要和请求参数名保持一致。

案例:学生注册提交表单,控制器处理方法用实体类接收请求参数。

新建实体类Student

package com.lwg.vo;

/**
 * @author lwg
 * @title
 * @create 2022/11/11 21:33
 */
public class Student {
    private Integer id;
    private String name;
    private Integer age;

    public Student() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

新建addStudent.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/11
  Time: 21:35
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>添加学生</title>
</head>
<body>
<form action="<c:url value='/student/add.do'/>" method="post">
    <p>
        <label for="id">编号</label>
        <input type="text" id="id" name="id">
    </p>
    <p>
        <label for="name">姓名</label>
        <input type="text" id="name" name="name">
    </p>
    <p>
        <label for="age">年龄</label>
        <input type="text" id="age" name="age">
    </p>
    <input type="submit" value="提交">
</form>
</body>
</html>

编写控制器

package com.lwg.controller;

import com.lwg.vo.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.ModelAndViewDefiningException;

/**
 * @author lwg
 * @title
 * @create 2022/11/11 21:40
 */
@Controller
@RequestMapping("/student")
public class StudentController {
    @RequestMapping("/add.do")
    public void addStrudent(Student student){
        ModelAndView modelAndView = new ModelAndView();
        System.out.println("传过来的student = " + student);
    }
}

运行结果:

zCxx5F.png

解决请求传递中文乱码

传统的是设置请求头的编码格式,但是这种做法过于繁琐。可以注册过滤器实现设置编码格式,解决中文乱码。

在web.xml配置过滤器:

<!--    注册过滤器-->
    <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>
<!--        配置强制请求编码-->
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
<!--        配置强制响应编码-->
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
<!--    配置拦截请求-->
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

**扩展:**vo、entity、domain、pojo包的区别

vo、entity、domain、pojo包下的所有类都是JavaBean。

**vo:**该包下的实体类与数据库没有关系,而是服务器与页面交互的的视图数据(后台与前台交互的数据类)

**entity:**该包下的实体类的属性与数据库表中的属性字段一致,数据类型一一对应。

**domain:**该包下的实体类的属性和数据库表中的属性字段一致外,实体类还会包含自定义的属性。

**pojo:**该包下的类没有严格的定义,可以是domain,可以是vo,也可以是entity,可以看做是entity、domain、vo的集合包。

处理方法返回值

处理方法返回值有四种:

  • ModelAndView模型和视图
  • String字符串
  • 无返回值
  • 自定义返回值类型
返回ModelAndView类型

当处理器处理方法处理完请求后,想跳转到其他资源,且又要在跳转的资源之间传递数据,此时处理方法返回ModelAndView类型是最好的。返回ModelAndView类型,要在处理方法内创建一个ModelAndView对象。

案例:返回ModelAndView对象,实现数据的传递和页面的跳转。

package com.lwg.controller;

import com.lwg.vo.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.ModelAndViewDefiningException;

/**
 * @author lwg
 * @title
 * @create 2022/11/11 21:40
 */
@Controller
@RequestMapping("/student")
public class StudentController {
    @RequestMapping("/add.do")
    public ModelAndView addStrudent(Student student){
        ModelAndView modelAndView = new ModelAndView();
        //保存数据
        modelAndView.addObject("name",student.getName());
        modelAndView.addObject("age",student.getAge());
        modelAndView.addObject("id",student.getId());
        //页面跳转
        modelAndView.setViewName("add.jsp");
        return modelAndView;
    }
}

如果处理方法只进行页面跳转或者只进行数据传递,这时返回ModelAndView类型就有点多余了,返回ModelAndView对象就不大合适。

返回String字符串

如果只是实现页面跳转,返回String字符串类型就非常合适。处理方法返回的字符串就是视图名称,视图的名称可以是逻辑名称,也可以是完整的视图路径。

使用逻辑名称要设置视图解析器。

使用完整的视图路径要注释掉视图解析器,不然它就会在完整的路径的基础上拼接视图解析器里配置的字符串,这时就会报404错误了。

案例:实现页面跳转,视图名称写逻辑名称和完整的视图路径。

前端:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/11
  Time: 22:08
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>veiw.jsp</title>
</head>
<body>
<a href="<c:url value='/student/relative.do'/>">视图名称逻辑名称</a><br/>
<a href="<c:url value='/student/complete.do'/>">视图名称完整路径</a>

</body>
</html>

控制器代码:

@RequestMapping("/relative.do")
public String relativeView(HttpServletRequest request){
    //如果想传递数据,可以使用HttpServletRequest对象
    request.setAttribute("name","张三");
    request.setAttribute("method","StudentController.relativeView()");
    //要相合视图解析器
    return "show";
}
@RequestMapping("/complete.do")
public String completeView(HttpServletRequest request){
    //如果想传递数据,可以使用HttpServletRequest对象
    request.setAttribute("name","李四");
    request.setAttribute("method","StudentController.completeView()");
    //使用完整视图路径,要注释掉视图解析器
    return "/WEB-INF/view/show.jsp";
}

总结:

  • 返回的视图名称如果使用的逻辑名称,要相合视图解析器
  • 返回的视图称呼如果使用的完整路径,要注释视图解析器
  • 如果想传递数据可以使用HttpServletRequest对象,但不建议,可以使用返回ModelAndView对象。
返回void

处理器方法返回void应用于ajax,处理完请求后不需要跳转到其他资源,可以返回void。

前端页面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/11
  Time: 22:23
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>reVoid.jsp</title>
    <script src="js/jquery-1.10.1.js"></script>
    <script>
        $(function () {
                $("button").click(function () {
                    $.ajax({
                        url:"<c:url value='/student/void.do'/>",
                        type:"POST",
                        data:{
                            name:"张三",
                            age:23,
                            id:2
                        },
                        dataType:"json",
                        success:function (data) {
                            alert(data.id);
                            alert(data.name);
                            alert(data.age);

                        }
                    })
                })
            })
    </script>
</head>
<body>
<button>点击发送ajax</button>
</body>
</html>

控制器方法:

@RequestMapping(value = "/void.do",method = RequestMethod.POST)
public void ajax(Student student, HttpServletResponse response){
    System.out.println("进来了");
    System.out.println("student = " + student);
    Gson gson = new Gson();
    String s = gson.toJson(student);
    try {
        response.getWriter().write(s);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
返回Object

处理器方法可以返回Object,这个 Object可以是Integer、String、自定义对象、Map、List等。返回的这个Object对象不是作为视图出现,而是作为页面显示数据显示出来的。返回对象要使用**@ResponseBody注解**,将转换后的json数据放入响应体。

@ResponseBody注解是将控制器方法返回的对象通过适当的转换为指定的格式之后,写入response对象的Boby区,通常是用来返回JSON数据或者XML数据。需要注意的是,在使用注解之后不会再走视图处理器,而是直接将数据写入输入流中,相当于通过response对象输出格式的数据。

由于注解@ResponseBody使用了jackson转换方式将对象转换成json数据,所有要需要导入对应的依赖

  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.13.3</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.3</version>
  </dependency>
</dependencies>

还要在springMVC核心配置文件中开启springmvc注解驱动,会自动将对象转换成json。

<mvc:annotation-driven/>

案例一:异常ajax请求,控制器返回自定义格式对象。

前端代码:

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/12
  Time: 16:28
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>objectAjax.jsp</title>
    <script src="js/jquery-1.10.1.js"></script>
    <script>
        $(function () {
                $("button").click(function () {
                    $.ajax({
                        url:"<c:url value='/student/objectAjax.do'/>",
                        type:"POST",
                        dataType:"json",
                        data:{
                            id:1,
                            name:"张三",
                            age:19
                        },
                        success:function (data) {
                            console.log(data)
                        }
                    })
                })
            })
    </script>
</head>
<body>
<button>点击发送ajax请求,返回自定义对象</button>
</body>
</html>

控制器方法:

@RequestMapping("/objectAjax.do")
@ResponseBody
public Student retultObject(Integer id,String name,Integer age){
    Student student = new Student();
    student.setId(id);
    student.setAge(age);
    student.setName(name);
    return student;
}

运行结果:

点击按钮,后端返回数据。

ziDDN4.png

案例二:ajax发送请求,服务器返回数组回来

前端代码:

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/12
  Time: 16:28
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>objectAjax.jsp</title>
    <script src="js/jquery-1.10.1.js"></script>
    <script>
        $(function () {
            $("#list").click(function () {
                $.ajax({
                    url:"<c:url value='/student/list.do'/>",
                    type: "GET",
                    dataType: "json",
                    success:function (data) {
                        console.log(data)
                    }

                })
            })
            })
    </script>
</head>
<body>
<button id="list">点击发送AJAX,返回数组</button>
</body>
</html>

控制器方法

@RequestMapping("/list.do")
@ResponseBody
public List<Student> retultList(){
    Student student1 = new Student();
    student1.setId(1);
    student1.setName("张三");
    student1.setAge(18);
    Student student2 = new Student();
    student2.setId(1);
    student2.setName("张三");
    student2.setAge(18);
    Student student3 = new Student();
    student3.setId(1);
    student3.setName("张三");
    student3.setAge(18);
    List<Student> studentList =new ArrayList<>();
    studentList.add(student1);
    studentList.add(student2);
    studentList.add(student3);
    return studentList;
}

运行结果:

点击按钮发送请求,后端返回的数据。

ziDNj0.png

案例三:发送ajax请求,后端返回String字符串数据回来

注意:返回字符串,如果没有@ResponseBody注解,就是发生页面跳转,但是如果带有@ResponseBody注解就是返回字符串数据。

前端:

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/12
  Time: 16:28
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>objectAjax.jsp</title>
    <script src="js/jquery-1.10.1.js"></script>
    <script>
        $(function () {
                
            $("#string").click(function () {
                $.ajax({
                    url:"<c:url value='/student/string.do'/> ",
                    type:"POST",
                    dataType:"text",
                    data:{
                        id:12,
                        name:"李四",
                        age:20
                    },
                    success:function (data) {
                        console.log(data);
                    }
                })
            })
            })
    </script>
</head>
<body>

<button id="string">点击发送AJAX,返回字符串</button>
</body>
</html>

控制器方法:

@RequestMapping("/string.do")
@ResponseBody
public String retultString(Student student){
    String resultStr="id:"+student.getId()+";name:"+student.getName()+";age:"+student.getAge();
    return resultStr;
}

url-pattern配置

通常情况下,我们将url-pattern配置设置为自定义格式的,比如配置为*.do等。

我们也可以将url-pattern设置为/,这样配置的话所有的请求都会被拦截,静态资源的获取请求也会被拦截,然后也将它们的请求当成一个普通的Controller请求,这里服务器就会报404错误,因为没有这个Controller。

产生原因:

在我们设置了访问动态资源的请求方式交给*.do来处理,或者交由其他自定义方式来处理,这时的静态资源其实是由了TomCat的DefaultServlet负责,而DefaultServlet通过/来拦截所有的请求,如果你的项目定义了你的请求路径,就以定义的为准,/拦截任意请求包括静态资源、动态资源,然后请求一个jsp或者servlet如果没有人处理就会交给DefaultServlet处理。DefaultServlet可以处理静态资源也可以处理动态资源。

如果项目的DispatcherServlet的url-pattern设置了/,这时候DispatcherServlet就会处理所有请求了,Tomcat的DefaultServlet就被覆盖了,然后功能就失效了,然后项目就不会处理静态资源了,就会报404了。

处理方法:

springMVC核心配置文件中配置DefaultServletHttpRequesHandler对象。

<mvc:default-servlet-handler/>

注:上述配置后,框架会创建一个DefaultServletHttpRequesHandler对象,该对象接收浏览器一切请求,会自动转发给Tomcat的DefaultServlet来处理,这时就又可以处理静态资源了。

但是这时请求动态资源就请求不到了,因为所有的请求都转发到了DefaultServlet来请求,导致DispatcherServlet失效了。

这时我们可以开启注解驱动就框架就会自动识别了,如果是需要SpringMVC处理的,则交给@RequestMapping处理,不需要的就交给Tomcat的servlet处理,对应的请求交给Servlet,没有对应的Servlet就直接返回,若是静态资源就返回对应的静态资源,找不到静态资源或者找不到servlet就返回404。

<mvc:annotation-driven/>

上述方法只适用于使用Tomcat服务器,其他服务器不适用。

终级解决方案:

springmvc定义了专门用来处理静态资源访问请求的处理器HttpRequesHandler,并且添加了<mvc:resources>标签,专门来用于访问静态资源。

<mvc:resources mapping="/html/**" location=/html/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/img/**" location="/img/"/>

mapping:表示请求资源

location:表示资源所在目录

注意:上面这种方式也要开启spring注解驱动

在实际开发中,为了更好管理静态资源,可以将静态资源放在一个文件夹下,便于管理。

上述配置简化成:

<mvc:resources mapping="/static/**" location="/static/"/>

SSM整合

SSM整合批是spring+springMVC+MyBatis。其实是spirng整合MyBatis。

ssm整合也两种方式

  • xml配置
  • 注解配置

ssm流程:

前端页面发起请求=>web层(SpringMVC处理)调用业务层=>spring容器管理service对象=>MyBatis访问数据库。

ssm两个容器:

  • springmvc容器
  • spring容器

案例:通过ssm整合完成实现学生注册和学生信息查询功能。效果图如下:

ziRg0I.png

一、数据库的准备

建学生表和插入一些数据

建表sql语句

DROP TABLE IF EXISTS `student`;

CREATE TABLE `student` (
  `s_id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) DEFAULT NULL,
  `age` INT(11) DEFAULT NULL,
  PRIMARY KEY (`s_id`)
) ENGINE=INNODB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;

插入数据

INSERT  INTO `student`(`s_id`,`name`,`age`) VALUES 

(1,'张三',18),

(2,'李四',20),

(3,'王五',22),

(4,'华子',23),

(5,'杨过',99),

(6,'杨过',99),

(7,'孙悟空',23),

(8,'渊子',23),

(9,'渊子',23),

(10,'李四地',12),

(11,'李四地',12),

(12,'李四地',12),

(13,'广闲',12);

二、编写后端

1、编写domain层,编写Student类

package com.lwg.domain;

/**
 * @author lwg
 * @title
 * @create 2022/11/12 23:07
 */
public class Student {
    private Integer sId;
    private String name;
    private Integer age;

    public Student() {
    }

    public Integer getsId() {
        return sId;
    }

    public void setsId(Integer sId) {
        this.sId = sId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "sId=" + sId +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2、编写dao层,编写StudentDao接口

这里使用注解的方式编写,所以不用配置mapper.xml

package com.lwg.dao;

import com.lwg.domain.Student;
import javafx.scene.chart.ValueAxis;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * @author lwg
 * @title
 * @create 2022/11/12 23:09
 */
public interface StudentDao {
    @Results(id = "studentMap",value = {
            @Result(id = true,column = "s_id",property = "sId")
    })
    @Select("select * from student")
    public List<Student>  selectStudentAll();
    @ResultMap("studentMap")
    @Select("SELECT * FROM student WHERE name=#{name}")
    public Student selectStudentByName(String name);
    @Insert("INSERT INTO student(NAME,age) VALUES(#{name},#{age})")
    public int insertStuent(Student student);
}

3、编写mybatis核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--
configuration是核心配置的根标签,表示配置的意思
-->
<configuration>

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

</configuration>

这里的日志输出使用到LO4j,所有要配置Log4j配置文件

log4j.properties文件:

log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

4、编写spring核心配置文件

applicationContext.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cotext="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:myBatisConfig.xml"/>
    </bean>
    <cotext:property-placeholder location="classpath:jdbcConfig.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.userName}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.lwg.dao"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
    </bean>

jdbcConfig.properties文件:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_task?useUnicode=true&characterEncoding=UTF-8
jdbc.userName=root
jdbc.password=xxxxxxxxxxx

5、编写service层

编写接口:

StudentService接口

package com.lwg.service;

import com.lwg.domain.Student;

import java.util.List;

/**
 * @author lwg
 * @title
 * @create 2022/11/12 23:32
 */
public interface StudentService {
   public Boolean addStuedent(Student student);
   public List<Student> queryAllStudent();
   public Student queryStudentByName(String name);
}

编写实现类

StudentServiceImpl类

package com.lwg.service.impl;

import com.lwg.dao.StudentDao;
import com.lwg.domain.Student;
import com.lwg.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author lwg
 * @title
 * @create 2022/11/13 20:45
 */
public class StudentServiceImpl implements StudentService {
    @Autowired
    private StudentDao studentDao;
    @Override
    public Boolean addStuedent(Student student) {
        if (studentDao.insertStuent(student)>0) {
            return true;
        }else {
            return false;
        }
    }

    @Override
    public List<Student> queryAllStudent() {
        return studentDao.selectStudentAll();
    }

    @Override
    public Student queryStudentByName(String name) {
        return studentDao.selectStudentByName(name);
    }


}

6、在spring核心配置文件中配置service层的bean

在applicationContext.xml文件中添加

<bean id="studentService" class="com.lwg.service.impl.StudentServiceImpl"/>

7、编写控制层

编写vo层的R类,R类用来统一前后端数据交互的格式

R类:

package com.lwg.controller.vo;

/**
 * @author lwg
 * @title
 * @create 2022/11/13 20:59
 * 用来与前端数据交互的实体类
 */
public class R {
    private Boolean result;
    private Object data;

    public R() {
    }

    public R(Boolean result, Object data) {
        this.result = result;
        this.data = data;
    }

    public Boolean getResult() {
        return result;
    }

    public void setResult(Boolean result) {
        this.result = result;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "R{" +
                "result=" + result +
                ", data=" + data +
                '}';
    }
}

StudentController类:

package com.lwg.controller;

import com.lwg.controller.vo.R;
import com.lwg.domain.Student;
import com.lwg.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
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.ResponseBody;

import java.util.List;
import java.util.Objects;

/**
 * @author lwg
 * @title
 * @create 2022/11/13 20:57
 */
@Controller
@RequestMapping("/student")
public class StudentController {
    @Autowired
    private StudentService studentService;
    @RequestMapping(value = "/verification",method = RequestMethod.POST)
    @ResponseBody
    public R verificationName(String name){
        Student student = studentService.queryStudentByName(name);
        if (Objects.isNull(student)) {
            return new R(true,true);
        }else {
            return new R(true,false);
        }
    }
    @RequestMapping(value = "/add",method = RequestMethod.POST)
    public String addStudent(Student student){
        Boolean isAdd = studentService.addStuedent(student);
        if(isAdd){
            return "list";
        }else {
            return "add";
        }
    }
    @RequestMapping(value = "/queryList",method = RequestMethod.GET)
    @ResponseBody
    public R queryAllStudent(){
        List<Student> studentList = studentService.queryAllStudent();
        return new R(true,studentList);
    }
}

8、编写springmvc核心配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="com.lwg.controller"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/stu/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <mvc:annotation-driven/>
    <mvc:default-servlet-handler/>
</beans>

注:上面的每一层工作完成后都要测试,这里就介绍了。

三、编写web.xml文件

  • 要注册springmvc中央调度器
  • 配置spring监听器
  • 注册编码过滤器,解决响应请求中文乱码问题
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                  http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
<!--        注册中央调度器-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spirngmvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
<!--        配置spring监听器-->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </context-param>
<!--            注册编码过滤器-->
        <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>
            <init-param>
                <param-name>forceRequestEncoding</param-name>
                <param-value>true</param-value>
            </init-param>
            <init-param>
                <param-name>forceResponseEncoding</param-name>
                <param-value>true</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>characterEncodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
</web-app>

四、编写前端

1、导入jquery资源包

2、编写相关页面

index.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/13
  Time: 22:02
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>主页</title>
</head>
<body>
<a href="<c:url value='/stu/add.jsp'/>">注册学生</a><br/>
<a href="<c:url value='/stu/list.jsp'/>">查询学生</a>
</body>
</html>

add.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/13
  Time: 21:28
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>添加学生</title>
    <script src="../js/jquery-1.10.1.js"></script>
    <script>
        $(function () {
                $("input[name='name']").blur(function () {
                    let name = $(this).val();
                    $.ajax({
                        url:"<c:url value='/student/verification'/>",
                        type:"POST",
                        dataType:"json",
                        data:{
                            name:name
                        },
                        success:function (data) {
                            if (data.result){
                                if (data.data) {
                                    $("#nameMsg").text("名字合法,可注册").css({
                                        color:"green"
                                    })
                                }else {
                                    $("#nameMsg").text("该学生已注册!").css({
                                        color:"red"
                                    })
                                }
                            }
                        }
                    })
                })
            })
    </script>
</head>
<body>
<h1>注册学生</h1>
<form action="<c:url value='/student/add'/>" method="post">
    <p>
        <label for="name">姓名:</label>
        <input type="text" id="name" name="name" placeholder="请输入学生姓名">

    </p>
   <p>
       <label for="age">年龄:</label>
       <input type="text" id="age" name="age">
   </p>
    <p id="nameMsg"></p>
    <p>
        <input type="submit" value="注册学生">
    </p>
</form>
</body>
</html>

list.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/13
  Time: 21:29
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>学生列表</title>
    <script src="../js/jquery-1.10.1.js"></script>
    <script>
        $(function () {
                $("button").click(function () {
                    $.ajax({
                        url:"<c:url value='/student/queryList'/>",
                        dataType:"json",
                        success:function (data) {
                            if (data.result) {
                                $("tr").remove();
                                data.data.forEach(function (value) {
                                    let sId = value.sId;
                                    let name = value.name;
                                    let age = value.age;
                                    $("table").append(`<tr>
                                                    <td>\${sId}</td>
                                                    <td>\${name}</td>
                                                    <td>\${age}</td>
                                                </tr>`)
                                })
                            }
                        }
                    })
                })
            })
    </script>
</head>
<body>
<h1>学生列表</h1>
<table align="center" border="1" cellpadding="10" cellspacing="0">
    <caption><button>查询学生信息</button></caption>
    <thead>
        <td>学号</td>
        <td>姓名</td>
        <td>年龄</td>
    </thead>
</table>
</body>
</html>

项目结构目录:

zkYQAA.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RZ3X135F-1669126130665)(C:\Users\35173\AppData\Roaming\Typora\typora-user-images\image-20221113233713270.png)]

五、运行项目

zkYYjS.png

zkYNng.png

zkYa7j.png

zkYr90.png

SpringMVC中的请求重定向和转发

当处理器处理完请求后,向其他资源进行跳转时,有两种跳转方式:

  • 请求转发
  • 请求重定向

根据所要跳转的资源分为:

  • 跳转到页面
  • 跳转到其他处理器

**注:**请求转发可以跳转到WEB-INF下的页面,而重定向不可以跳转到WEB-INF下的页面,因为重定向相当于用户再一次发送请求,而用户不能直接访问WEB-INF目录下的页面。

转发:

发生在服务器内部,服务器收到客户端的请求之后 ,会将请求转发到目标地址,再将目标地址的响应结果返回给客户端。

zAAiZD.png

重定向:

当服务器接收客户端请求后,会给客户端发送一个临时响应头,临时响应头记录到了客户端需要重定向的URL。客户端接收到地址后,会重新发送请求到新的URL。

zAAVJA.png

请求转发和重定向的区别

  1. 请求转发是一次请求一次响应,而重定向是两次请求两次响应。
  2. 请求转发地址是不会发生改变的,而重定向会显示重定向后面的的请求地址。
  3. 请求转发是服务器端的行为,而重定向是浏览器行为。
  4. 请求转发只能转发到本服务器,不能能够转发到其他服务器上,在而重定向可以重定向到本服务器上也可以重定向到其他服务器上。
  5. 请求转发是通过request对象来实现转发的,多个servlet可以共享同一个request,而重定向是通过response对象来实现重定向的,多个servlet不共享request,完全是两个request。
  6. 请求转发效率比重定向高,转发是一次请求,是服务器内部调用效率高。

如何选择转发和重定向:

  1. 如果是希望地址栏发生改变或者跨服务器,选择重定向。
  2. 如果希望同一个request共享数据,只能转发。
  3. 其他情况随意选择,建议如果是本地服务器,使用转发,转发效率高。

JavaWeb转发和重定向方式:

转发:

request.getRequestDispatcher("目标地址").forward(request,response);

重定向:

response.sendRedirect("目标地址");

而springMVC将转发和重定向操作进行了封装,使用起来更简单。

请求转发

返回ModelAndView类型

当处理器方法返回ModelAndView类型时,只需要在modelAndView对象的setViewName()方法参数中指定的视图前添加"forward:"。

modelAndView.setViewName("forward:/WEB-INF/show.jsp");

注:这时视图不再与视图解析器一起工作了,所有视图路径要写全,写相对路径,以/开头。forward不需要视图解析器。

控制器控制方法:

//请求转发,返回类型ModelAndView
@RequestMapping("/doForward1")
public ModelAndView forwaard1(){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("name","张三");
    modelAndView.addObject("age",23);
    modelAndView.setViewName("forward:/WEB-INF/show.jsp");
    return modelAndView;
}

show.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/14
  Time: 16:06
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>show.jsp</title>
</head>
<body>
<h1>${name}</h1>
<h1>${age}</h1>
转发成功
</body>
</html>
返回String类型

当处理器处理方法返回String时,只需要返回的视图路径前加forward:。

return "forward:/WEB-INF/show.jsp";

这里的视图路径也是写全路径,不与视图解析器使用。

控制方法:

//请求转发,返回类型String
@RequestMapping("/doForward2")
public String forward02(HttpServletRequest request){
    request.setAttribute("name","张三");
    request.setAttribute("age",32);
    return "forward:/WEB-INF/show.jsp";
}

show.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/14
  Time: 16:06
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>show.jsp</title>
</head>
<body>
<h1>${name}</h1>
<h1>${age}</h1>
转发成功
</body>
</html>

注:springMVC默认视图转发的方式是转发,所以无论是返回ModelAndView还是String字符串类型都默认是转发,可以省略forward:,这时就可以与视图解析器一起工作。

请求重定向

需要在ModelAndView对象的setViewName()方法上的视图参数前添加redirect:

modelAndView.setViewName("redirect:/show1.jsp");

注:

  1. 不能重定向到WEB-INF目录下的页面
  2. 不能共享同一个request,所以不能request传递数据,但是如果如果我们modelAndView添加了数据,通过modelAndView对象的addObjectK()添加数据,springMVC会将数据转换成参数一起重定向到目标地址给我们。

控制器方法:

@RequestMapping("/doRedirect1")
public ModelAndView redirect1(){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("name","李四");
    modelAndView.addObject("age",13);
    modelAndView.setViewName("redirect:/show1.jsp");
    return modelAndView;
}

show1.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/14
  Time: 16:34
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>show1.jsp</title>
</head>
<body>
<h1>${param.name}</h1>
<h1>${param.age}</h1>
重定向成功
</body>
</html>

SpringMVC异常处理

springMVC异常处理中使用到两个注解:

@ExceptionHandler:注解处理异常

@ControllerAdvice:对控制层进行功能增强,就是增强对异常的处理。

@ExceptionHandler注解作用:

  • 将一个方法指定为异常处理文法 ,value属性指定该方法所处理的异常类,即匹配的异常。
  • 被注解的方法和控制方法返回值一样,可以是ModelAndView、String、void、自定义对象;参数可以有HttpServletRequest、HttpServletResponse、参数、Exception。

案例:模拟出现异常处理

编写自定义异常类

MyException类:

package com.lwg.exception;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 17:44
 */
public class MyException extends Exception{
    public MyException() {
        super();
    }

    public MyException(String message) {
        super(message);
    }
}

AgeException类:

package com.lwg.exception;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 17:46
 */
public class package com.lwg.exception;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 17:46
 */
public class AgeException extends MyException{
    public AgeException() {
        super();
    }

    public AgeException(String message) {
        super(message);
    }
}
 extends MyException{
    public AgeException() {
        super();
    }

    public AgeException(String message) {
        super(message);
    }
}

NameException类:

package com.lwg.exception;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 17:45
 *
 * 姓名异常
 */
public class NameException extends MyException{
    public NameException() {
        super();
    }

    public NameException(String message) {
        super(message);
    }
}

编写控制器控制方法:

package com.lwg.controller;

import com.lwg.exception.AgeException;
import com.lwg.exception.MyException;
import com.lwg.exception.NameException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 17:47
 */
@Controller
public class ExceptionController {
    @RequestMapping("/doException")
    public ModelAndView doException(String name,Integer age) throws MyException {
        System.out.println("ExceptionController.doException");
        System.out.println("name = " + name);
        System.out.println("age = " + age);
        ModelAndView modelAndView = new ModelAndView();
        //模拟出异常
        if (name.equals("lwg")) {
            throw new NameException("姓名未注册");
        }
        if(age<0||age>200){
            throw new AgeException("年龄应在0~200之间");
        }
        modelAndView.addObject("name ",name);
        modelAndView.addObject("age",age);
        modelAndView.setViewName("show");
        return modelAndView;
    }
}

编写控制异常类

普通类使用@ControllerAdvice注解定义此类为一个异常处理类。

方法使用@ExceptionHandler定义异常处理方法,value属性表示抛出哪些异常会调用此方法。如果没有没有写就会是除开异常配置类中配置的异常外的所有异常。

package com.lwg.handle;

import com.lwg.exception.AgeException;
import com.lwg.exception.NameException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 19:59
 */
@ControllerAdvice
public class ExceptionHandle {
    //处理NameException异常
    @ExceptionHandler(NameException.class)
    public ModelAndView doNameException(Exception e){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("ex",e);
        modelAndView.addObject("msg",e.getMessage());
        modelAndView.setViewName("nameException");
        return modelAndView;
    }
    //处理AgeException异常
    @ExceptionHandler(AgeException.class)
    public ModelAndView doAgeException(Exception e){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("ex",e);
        modelAndView.addObject("msg",e.getMessage());
        modelAndView.setViewName("ageException");
        return modelAndView;
    }
    //处理除了NameException、AgeException外的异常
    @ExceptionHandler()
    public ModelAndView doOtherException(Exception e){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("ex",e);
        modelAndView.addObject("msg",e.getMessage());
        modelAndView.setViewName("otherException");
        return modelAndView;
    }
}

配置springmvc核心配置文件,配置包扫描,扫描异常处理类

<context:component-scan base-package="com.lwg.handle"/>

编写前端页面:

index.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/14
  Time: 17:54
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>index.jsp</title>
</head>
<body>
<form action="<c:url value='/doException'/>" method="post">
    <p>
        <label for="name">姓名</label>
        <input type="text" name="name" id="name">
    </p>
    <p>
        <label for="age">年龄</label>
        <input type="text" name="age" id="age">
    </p>
    <input type="submit" value="提交">
</form>
</body>
</html>

ageException.jsp:

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/14
  Time: 20:06
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>ageException.jsp</title>
</head>
<body>
<h1>年龄异常处理页面</h1>
<h2>${ex}</h2>
<h2>${msg}</h2>
</body>
</html>

nameException.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/14
  Time: 20:06
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>nameException.jsp</title>
</head>
<body>
<h1>姓名异常处理页面</h1>
<h2>${ex}</h2>
<h2>${msg}</h2>
</body>
</html>

otherException.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/14
  Time: 20:06
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>otherException.jsp</title>
</head>
<body>
<h1>其他异常处理页面</h1>
<h2>${ex}</h2>
<h2>${msg}</h2>
</body>
</html>

springMVC拦截器

定义

  • 拦截器是springMVC特有的对象,该对象需要实现HandlerInterceptor接口
  • 拦截器和过滤器很相似,都有拦截功能,过滤器可以过滤所有资源(包括动态资源和静态资源 ),拦截器只可以拦截访问控制器的(Controller)
  • 拦截器是全局的,可以对多个控制器进行拦截
  • 拦截器可以用来做登录处理,权限校验,日志记录

使用步骤

1、定义类实现HandlerInterceptor接口,重写接口相应的方法

2、在springmvc配置文件中进行拦截器的配置,可以配置多个拦截器

HandlerInterceptor接口

自定义拦截器,要实现HandlerInterceptor接口,然后重写相应的方法。

HandlerInterceptor接口有三个方法:

default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return true;
}

该方法在处理器方法执行之前执行。其返回值是boolean。返回true则紧接着执行控制器方法,且会将afterCompletion()放在在一个专门的方法栈等待执行。返回false,则不会放行后面的拦截器或者控制器。

default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}

该方法是在控制器方法执行完之后执行的。处理方法若最终无法执行完成,则该方法不会执行。由于方法是控制方法执行之后执行的的,可以通过方法的参数modelAndView修改控制方法响应的数据,还可以修改跳转的页面。

default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}

当preHandle方法返回true时,会将该方法放在专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行。即该方法是在中央高度器渲染(数据填充)响应页面之后才执行的。afterCompletion是最后执行的方法,用来于释放资源。

案例

通过拦截器实现请求拦截转发,回程拦截修改数据和视图,计算整个请求的执行时间。

编写控制器:

package com.lwg.controller;

import com.sun.org.apache.xpath.internal.operations.Mod;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 21:55
 */
@Controller
public class InterceptorController {
    @RequestMapping("/doInterceptor")
    public ModelAndView doInterceptor(String name,Integer age){
        System.out.println("InterceptorController.doInterceptor");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("name",name);
        modelAndView.addObject("age",age);
        modelAndView.setViewName("show");
        return modelAndView;
    }
}

编写拦截器

定义普通类实现HandlerInterceptor接口,重写preHandle、postHandle、afterCompletion方法。

package com.lwg.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Objects;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 21:50
 */
public class MyInterceptor implements HandlerInterceptor {
    private long beginTime=0;
    private long endTime=0;
    //在执行控制器方法之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        beginTime=System.currentTimeMillis();
        System.out.println("MyInterceptor.preHandle");
//        请求拦截转发
        /*
        请求拦截转发
        request.getRequestDispatcher("/WEB-INF/result.jsp").forward(request,response);
        return false;*/
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor.postHandle");
        if (Objects.nonNull(modelAndView)) {
            //修改数据
            modelAndView.addObject("data",new Date());
        }
        //修改视图
        modelAndView.setViewName("result");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor.afterCompletion");
        endTime=System.currentTimeMillis();
        System.out.println(endTime-beginTime);
    }
}

编写springmvc核心配置文件,配置拦截器

<!--    配置拦截器,可以配置多个-->
    <mvc:interceptors>
        <mvc:interceptor>
<!--            mvc:mapping path= 表示拦截的请求的,可以使用通配符

-->
            <mvc:mapping path="/**"/>
            <bean class="com.lwg.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

编写前端页面

interceptor.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/14
  Time: 22:02
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>interceptor.jsp</title>
</head>
<body>
<form method="post" action="<c:url value='/doInterceptor'/>">
    <p>
        <label for="name">姓名</label>
        <input type="text" id="name" name="name">
    </p>
    <p>
        <label for="age">年龄</label>
        <input type="text" id="age" name="age">
    </p>
    <input type="submit">
</form>
</body>
</html>

show.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/14
  Time: 17:50
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>show.jsp</title>
</head>
<body>
<h1>数据展示 </h1>
<h2>${name}</h2>
<h2>${age}</h2>
</body>
</html>

result.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/14
  Time: 21:54
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>请求拦截转发</title>
</head>
<body>
<h1>请求拦截转发</h1>
<h2>${name}</h2>
<h2>${age}</h2>
<h2>${data}</h2>
</body>
</html>

多个拦截器的使用情况

拦截器和过滤器一样,也可以在访问控制器之前部署多个拦截器。

案例:配置多个拦截器

编写两个拦截器:

AInterceptor

package com.lwg.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 22:51
 *
 */
public class AInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("AInterceptor.preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("AInterceptor.postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("AInterceptor.afterCompletion");
    }
}

BInterceptor

package com.lwg.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 22:57
 */
public class BInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("BInterceptor.preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("BInterceptor.postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("BInterceptor.afterCompletion");
    }
}

编写springmvc,配置两个拦截器

 <mvc:annotation-driven/>
<!--    配置拦截器,可以配置多个-->
    <mvc:interceptors>
<!--        配置第一个拦截器-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.lwg.interceptor.AInterceptor"/>
        </mvc:interceptor>
<!--        配置第二个拦截器-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.lwg.interceptor.BInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

编写控制器

package com.lwg.controller;

import com.sun.org.apache.xpath.internal.operations.Mod;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 21:55
 */
@Controller
public class InterceptorController {
    @RequestMapping("/doInterceptor")
    public ModelAndView doInterceptor(String name,Integer age){
        System.out.println("InterceptorController.doInterceptor");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("name",name);
        modelAndView.addObject("age",age);
        modelAndView.setViewName("show");
        return modelAndView;
    }
}

运行结果:

zA5Z4A.png

当配置了多个拦截器,形成拦截链,拦截器链的执行顺序和注册顺序一致。多个拦截执行顺序如图:

zA5Qu8.png

只要其中一个拦截器的preHandle()返回false上面的执行链就会断掉,后续的处理器方法和postHandle()方法不会执行。但无论执行链执行情况如何,只要方法栈中方法,即有拦截器preHandle返回了true,就会执行该拦截器的afterCompletion()方法。

案例

在之前的ssm案例加一个登录功能,用户不能直接访问list.jsp,要先登录才能访问,而如果之前登录的用户则可以直接访问。

大概思路:用户登录之后保存信息在session域中,在之前使用过滤器可以直接拦截静态资源的请求,但是拦截器只能拦动态资源的请求,不能拦截请求访问list.jsp,所以我们要将转换一下访问list.jsp页面的方式,访问list.jsp之前要先访问服务器,服务器再转发再list. jsp,然后还要将list.jsp放在WEB-INF目录下,防止用户直接通过地址访问。

实现:

在上面的ssm案例的基础发生改变

添加一个用户表

创建表sql语句:

CREATE TABLE `user` (
  `id` INT(20) NOT NULL AUTO_INCREMENT,
  `user_name` VARCHAR(30) NOT NULL,
  `pwd` VARCHAR(30) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;

插入一些数据,sql语句:

INSERT  INTO `user`(`id`,`user_name`,`pwd`) VALUES 

(1,'lwg','123'),

(2,'zaci','123'),

(3,'fjig','123'),

(4,'ylyl','342'),

(5,'gege','345'),

(6,'fwe','999'),

(7,'fff','12345'),

(18,'aa','1234567'),

(19,'ee','123455'),

(20,'dd','123456'),

(21,'qqq','123456'),

(22,'addd','123456'),

(23,'fffpp','123456');

创建用户实体类

User类

package com.lwg.domain;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 23:16
 */
public class User {
    private Integer id;
    private String userName;
    private String pwd;

    public User() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

编写dao层接口

根据用户名和密码查询用户

UserDao接口

package com.lwg.dao;

import com.lwg.domain.User;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 23:17
 */
public interface UserDao {
    @Results(id = "userMap",value = {
            @Result(column = "user_name",property = "userName")
    })
    @Select("select * from user where user_name=#{userName} and pwd=#{pwd}")
    public User selectUserByUserNamePwd(User user);
}

编写service层,编写接口和对应的实现类

UserService接口

package com.lwg.service;

import com.lwg.domain.User;
import com.sun.org.apache.xpath.internal.operations.Bool;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 23:25
 */
public interface UserService {
    public Boolean queryUser(User user);
}

UserServiceImpl

package com.lwg.service.impl;

import com.lwg.dao.UserDao;
import com.lwg.domain.User;
import com.lwg.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Objects;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 23:26
 */
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Override
    public Boolean queryUser(User user) {
        User resultUser = userDao.selectUserByUserNamePwd(user);
        if (Objects.isNull(resultUser)) {
            return false;
        }else {
            return true;
        }
    }
}

编写控制层

UserControlelr

package com.lwg.controller;

import com.lwg.domain.User;
import com.lwg.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 23:31
 */
@Controller
@RequestMapping("/user")
public class UserControlelr {
    @Autowired
    private UserService userService;
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    public ModelAndView login(HttpServletRequest request,User user){
        Boolean isLogin = userService.queryUser(user);
        ModelAndView modelAndView = new ModelAndView();
        if(isLogin){
            modelAndView.addObject("loginMsg","登录成功");
            HttpSession session = request.getSession();
            session.setAttribute("login",true);
            modelAndView.setViewName("forward:/index.jsp");
        }else {
            modelAndView.addObject("loginMsg","登录失败,账号或密码错误");
            modelAndView.setViewName("login");
        }
        return modelAndView;
       
    }
}

StudentController中添加控制方法:用于转发跳转到list.jsp页面。

@RequestMapping(value = "/list",method = RequestMethod.GET)
public String forwardListK(){
    return "forward:/WEB-INF/list.jsp";
}

创建并编写拦截器

LoginInterceptor类

package com.lwg.interceptor;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Objects;

/**
 * @author lwg
 * @title
 * @create 2022/11/14 23:53
 */
@ControllerAdvice
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Object login = session.getAttribute("login");
        System.out.println("拦截器生效");
        System.out.println("login = " + login);
        if (Objects.isNull(login)) {
            System.out.println("拦截进来");
            request.setAttribute("loginMsg","你还未登录,请登录");
            request.getRequestDispatcher("/stu/login.jsp").forward(request,response);
            return false;
        }else {
            return true;
        }
    }


}

编写springmvc核心配置文件

配置拦截器扫描组件和配置拦截器

<context:component-scan base-package="com.lwg.interceptor"/>
<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/student/list"/>
            <bean class="com.lwg.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

编写前端:

修改index访问的list.jsp的链接

<a href="<c:url value='/student/list'/>">查询学生</a>

编写login.jsp

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/14
  Time: 23:38
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<form action="<c:url value='/user/login'/>" method="post">
    <table>
        <tr>
            <td><label for="userName">用户名:</label> </td>
            <td><input type="text" id="userName" name="userName"></td>
        </tr>
        <tr>
            <td><label for="pwd">密码:</label> </td>
            <td><input type="text" id="pwd" name="pwd"></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="登录"></td>
        </tr>
        <tr><span style="color: red">${loginMsg}</span></tr>
    </table>

</form>
</body>
</html>

项目目录结构:

zZdCl9.png

zZdiO1.png

运行结果:

未登录:

zZd8Tf.png

zZdtfg.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bA3s5t0m-1669126130675)(C:\Users\35173\AppData\Roaming\Typora\typora-user-images\image-20221116173553297.png)]

登录之后:

zZdBmq.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9UnqbQNP-1669126130676)(C:\Users\35173\AppData\Roaming\Typora\typora-user-images\image-20221116173633362.png)]

zZdr7V.png

拦截器和过滤器的区别

1、过滤器是JavaWeb中三大组件之一,是要在web.xm中配置的,而拦截器是springMVC框架中的对象,需要在springmvc中配置。

2、过滤器是要实现Filter接口或者继承GenericFilter类,拦截器是实现HandlerInterceptor接口。

3、过滤器是用来过滤所有资源(包括静态和动态资源),而拦截器只是拦截动态资源请求(也就是控制器对象的处理方法),不能拦截html、js、css等静态资源。

4、过滤器的拦截时间点只有一个doFilter方法,而拦截器拦截时间点有三个,分别 是preHandle(执行控制方法前)、postHandle(控制器方法执行完成之后 )、afterCompletion(preHandle方法返回true,控制器执行完成后)。

5、过滤器和拦截器都具有拦截功能,过滤器和拦截器都可以配置多个。

6、过滤器是TomCat创建的对象,而拦截器是SpringMVC创建的对象。

7、拦截器侧重于对控制器的拦截,如果请求不能被DispatcherServlet接收,那么请求也不会执行拦截器。

拦截器执行原理

拦截器的作用是拦截指定用户的请求,对请求做预处理和执行后处理。其拦截的时间点在“处理器映射器根据用户提交的请求映射出了所有执行的处理器类,并且也找到了要执行该处理器类的适配器,在处理器适配器执行处理之前”。当然,在处理器映射器映射出要执行的处理器类时,已经将拦截器与处理器结合为了一个处理器执行链,并返回给中央调度器。中央调度器调用视图解析器解析成视图响应后,完成一次请求,最后执行afterCompletion方法。

Restful风格

介绍

Restful风格指的是网络应用中就是资源定位和资源操作的风格。不是标准也不是协议。
Rest即Representational State Transfer的缩写,可译为"表现层状态转化”。Restful风格最大的特点为:资源、统一接口、URI和无状态。
这种风格设计的软件,可以更简洁,更有层次,更易于实现缓存等机制。

REST(英文:Representational State Transfer,简称REST)
一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。

REST 指的是一组架构(约束条件)和原则。满足这些(约束条件)和(原则)的应用程序或设计就是 RESTful。

传统请求方式和Restful风格对比

传统请求方式:

协议://ip号:端口号/项目虚拟路径?参数名1=参数1&参数名2=参数2

传统方式请求CRUD请求:

请求所有:http://localhost:8080/user?action=query GET请求

请求单个:http://localhost:8080/user?action=query&id=1001 GET请求

添加:http://localhost:8080/user?action=add POST请求

修改:http://localhost:8080/user?action=update POST请求

删除:http://localhost:8080/user?action=delete&id=1001 GET请求

Restful风格请求:

协议://ip地址:端口号/项目虚拟路径/资源名称/参数值1/参数值2

SpringMVC发送PUT、DELETE请求

步骤如下:

1、前端页面编写POST请求的表单

2、在from表单添加一个_method属性,值为请求方式的隐藏域hidden。

3、在web.xm配置Filater过滤器,这个过滤器将POST请求转换成PUT或者delete请求。

注意:高版本的Tomcat的put、delete请求无法转发到jsp页面,会抛出405错误,但是支持重定向,如果是低版本的Tomcat支持的。

前端编写表单

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/16
  Time: 20:27
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>index</title>
</head>
<body>
<form action="<c:url value='/restful/put'/> " method="post">
    <h2>PUT请求</h2>
    <input type="hidden" name="_method" value="put">
    姓名:<input type="text" name="name" id="name"><br/>
    年龄:<input type="text" name="age" id="age"><br/>
    <input type="submit" value="发送put请求">
</form>
<div style="border: 2px solid black"></div>
<form action="<c:url value='/restful/delete'/>" method="post">
    <h2>delete请求</h2>
    <input type="hidden" name="_method" value="delete">
    <input type="submit" value="发送delete请求">
</form>
</body>
</html>

web.xml添加过滤器

<!--    添加过滤器,将POST请求转换PUT或者DELETE请求-->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

注:这里的HiddenHttpMethodFilter会根据post请求中的_method属性的值转换成对应的请求(put/delete),然后在转换之前会调用request.getParameter()方法,这时可能会发生中文乱码,所以要在CharacterEncodingFilter过滤器后面配置。

编写控制器,测试

package com.lwg.controller;

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

/**
 * @author lwg
 * @title
 * @create 2022/11/16 20:30
 */
@Controller
@RequestMapping("/restful")
public class RestfulController {
    //接收PUT请求
    @RequestMapping(value = "/put",method = RequestMethod.PUT)
    public ModelAndView restfulPut(String name,Integer age){
        System.out.println("这里put请求");
        ModelAndView modelAndView = new ModelAndView();
        System.out.println("name = " + name);
        modelAndView.addObject("name",name);
        modelAndView.addObject("age",age);
         /*  
        高版本不支持转发到jsp,这里如果这样写会报405错误
        modelAndView.setViewName("forward:/show.jsp");
      
      */
        modelAndView.setViewName("redirect:/show.jsp");
        return modelAndView;
    }
    //接收delete请求
    @RequestMapping(value = "/delete",method = RequestMethod.DELETE)
    public ModelAndView restfulDelete(){
        System.out.println("这是delete请求");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("redirect:/delete.jsp");
        return modelAndView;
    }

}

restful风格get、post、put、delete请求

后端接收retful请求注解:

第一种:

使用@RequestMapping注解,通过设置method属性值来接收不同的请求方式的请求。

  • @RequestMapping(value = “/get”,method = RequestMethod.GET)
  • @RequestMapping(value = “/pst”,method = RequestMethod.POST)
  • @RequestMapping(value = “/put”,method = RequestMethod.PUT)
  • @RequestMapping(value = “/delete”,method = RequestMethod.DELETE)

第二种:

使用对应的请求方式的注解

  • @GetMapping(“/get”) GET请求映射
  • @PostMapping(“/post”) POST请求映射
  • @PutMapping(“/put”) PUT请求映射
  • @DeleteMapping(“/delete”) DELETE请求映射

后端获取参数:

使用@PathVariable注解

一个参数的获取:

@DeleteMapping("/delete/{id}")
public ModelAndView restfulDelete(@PathVariable Integer id){
    System.out.println("这是delete请求");
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("redirect:/delete.jsp");
    return modelAndView;
}

多个参数的获取:

  @GetMapping("/query/{name}/{password}")
    @ResponseBody
    public String restfulGte(@PathVariable String name,@PathVariable Integer password){
        System.out.println("name = " + name);
        System.out.println("password = " + password);
        return "name:"+name+"password:"+password;

    }

注:控制方法的参数名要和路径上的参数名一致。

要想方法的参数名和路径上的参数名不一致也是可以的。可以在(@PathVariable 里的value属性设置参数名,这个值设置成路径上的参数名即可。

@GetMapping("/query/{name}/{password}")
@ResponseBody
public String restfulGte(@PathVariable("name") String userName,@PathVariable("password") Integer pwd){
    System.out.println("name = " + userName);
    System.out.println("password = " + pwd);
    return "name:"+userName+"password:"+pwd;

}

案例:Restful风格实现学生的CRUD

编写前端:发送请求

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/16
  Time: 20:27
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Restful风格实现学生的CRUD</title>
</head>
<body>
<a href="<c:url value='/student/1001'/>">通过id查询</a><br/>
<div style="border: 1px solid black"></div>
<form action="<c:url value='/student/add'/>" method="post">
    姓名:<input type="text" name="name"><br/>
    年龄:<input type="text" name="age"><br/>
    <input type="submit" value="添加">
</form>
<div style="border: 1px solid black"></div>
<form action="<c:url value='/student/1001'/> " method="post">
    <input type="hidden" name="_method" value="put">
    姓名:<input type="text" name="name"><br/>
    年龄:<input type="text" name="age"><br/>
    <input type="submit" value="修改">
</form>
<div style="border: 1px solid black"></div>
<form action="<c:url value='/student/1001'/>" method="post">
    <input type="hidden" name="_method" value="delete">
    <input type="submit" value="删除">
</form>
</body>
</html>

配置web.xml,配置HiddenHttpMethodFilter过滤器。

编写控制器

package com.lwg.controller;

import com.lwg.domain.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author lwg
 * @title
 * @create 2022/11/16 21:32
 */
@Controller()
@RequestMapping("/student")
public class StudentController {
    //查询
    @GetMapping("/{id}")
    public ModelAndView queryById(@PathVariable Integer id){
        System.out.println("查询单个学生,通过id");
        System.out.println("id = " + id);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("redirect:/lsit.jsp");
        return modelAndView;
    }
    //添加
    @PostMapping("/add")
    public ModelAndView addStudent(Student student){
        System.out.println("这是添加操作");
        System.out.println("student = " + student);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("redirect:/add.jsp");
        return modelAndView;
    }
    //修改
    @PutMapping("/{id}")
    public ModelAndView updateStudent(@PathVariable Integer id,Student student){
        System.out.println("这是修改操作");
        System.out.println("id = " + id);
        System.out.println("student = " + student);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("redirect:/update.jsp");
        return modelAndView;
    }
    //删除
    @DeleteMapping("/{id}")
    public ModelAndView deleteStudent(@PathVariable Integer id){
        System.out.println("这是删除操作");
        System.out.println("id = " + id);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("redirect:/delete.jsp");
        return modelAndView;
    }
}

编写返回页面。

SpringMVC文件上传和下载

JavaWeb文件上传

浏览器上传文件要求

1、必须指定method的请求方式为post请求,不能是get请求,因为get请求有文件大小限制,post请求没有限制。

2、默认表单的编码方式是是 enctype=“application/x-www-form-urlencoded” ,但是文件上传要指定编码方式为enctype=“multipart/from-data”。

3、表单的文件必须指定name属性值。


<form action="<c:url value='/file/upload'/>" method="post" enctype="multipart/form-data">
    <p>
        <label for="name">姓名</label>
        <input type="text" id="name">
    </p>
    <p>
        <input type="file" name="fileName">
    </p>
</form>
服务器对文件上传进行限制(Servlet)

普通表单获取参数使用request的getParameter()方法

String name = req.getParameter("name");

但是文件表单上面这个方法获取只能为null,这里需要使用request对象的getInputStream()方法获得整个请求体,请求体包括了普通表单的值也包括文件表单的字节信息。

ServletInputStream inputStream = req.getInputStream();

文件表单的协议表单格式:

包括文件的from表单就属于是文件表单,文件上传时http请求协议和普通表单不同,表单的每一个内容通过过 boundary 对应的分割线来分割。

如:

Content-Type: multipart/form-data; boundary=---------------------------374814438042057686603467223232
-----------------------------3374814438042057686603467223232
Content-Disposition: form-data; name="name"
-----------------------------3374814438042057686603467223232
Content-Disposition: form-data; name="fileName"; filename="tips.png"
Content-Type: image/png
PNG
qiY(÷ºØf°kT•ìÆ•Ö•Ü$Þ-H•µì3:ipV•Ål¨jð敵¬(h•HâD••ýé•e•²•&••S÷Ñ9|8EQj•=g?
•§(Ácff\vl®¶ºc¨•ÈJ•ÎgOæÍîÕ4úÎyp9•ïÙ•g•°_•Ñh4P5êÝs••$hÆ

文件解析我们可借用commons-fileupload.jar来处理。

commons-fileupload处理文件表单

解析文件相关的类:

  • 文件项工厂类: DiskFileItemFactory
  • 文件解析器:ServletFileUpload
  • 文件表单项:FileItem

解析步骤:

1、导依赖

<!--    解析文件上传-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.3</version>
    </dependency>

2、创建DiskFileItemFactory工厂:

DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();

3、通过DiskFileItemFactory工厂获得文件解析器:

ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);

4、通过文件解析解析,获得FileItem集合:

List<FileItem> fileItems = servletFileUpload.parseRequest(req);

注:每个fileItems都对应着一个表单(文件表单或者普通表单)

5、调用fileItems的相关api获取相关的表单信息

FileItem相关API

zM7gKO.png

package com.lwg.controller;

import com.sun.net.httpserver.HttpServer;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

/**
 * @author lwg
 * @title
 * @create 2022/11/20 21:38
 */
@WebServlet("/upload")
public class FileController extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        //        普通表单获取参数
        String name = req.getParameter("name");
//        创建diskFileItemFactory工厂
        DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
//        设置临时缓存以及临时缓存目录
//        设置超过当内存满多少时,将文件缓存到哪个目录
//        DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory(1024*1024*5,new File("D:/temp"));
//        通过diskFileItemFactory工厂获得工厂解析器
        ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
//        设置单个文件的上限不超过10KB
        servletFileUpload.setFileSizeMax(1024*10);
//        设置上传的总文件不超过10MB
        servletFileUpload.setSizeMax(1024*1024*10);
        try {
            //        通过解析器来解析文件表单,获得FileItem集合
            List<FileItem> fileItems = servletFileUpload.parseRequest(req);
            for (FileItem fileItem : fileItems) {
//                只处理文件表单
                if(!fileItem.isFormField()){
//                    获得上传文件的的MIME类型
                    String contentType = fileItem.getContentType();
                    System.out.println("contentType = " + contentType);
//                    获得文件大小
                    long size = fileItem.getSize();
                    System.out.println("size = " + size);
//                    获得文件name的值
                    String fileName = fileItem.getName();
                    System.out.println("fileName = " + fileName);
//                    获取某个路径的绝对路径
                    String path = getServletContext().getRealPath("/WEB-INF");
                    File file = new File(path,"file.png");
//                    保存文件
                    fileItem.write(file);
                }
            }

        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

补充:

1、可以设置临时缓存以及临时缓存目录

DiskFileItemFactory(int sizeThreshold, File repository) 构造方法设置

sizeThreshold:设置当内存满多少就往临时缓存里存储,System.getProperty(“java.io.tmdir”)代表默认大小,为10KB

File repository:设置临时缓存目录,System.getProperty(“java.io.tmdir”) 设置默认目录。

 DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory(1024*1024*5,new File("D:/temp"));

2、通过解析器可以设置限制文件大小和限制总文件的大小

 ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
//        设置单个文件的上限不超过10KB
        servletFileUpload.setFileSizeMax(1024*10);
//        设置上传的总文件不超过10MB
        servletFileUpload.setSizeMax(1024*1024*10);

3、处理表单乱码问题

request.setCharacterEncoding("utf-8");
servletFileUpload.setHeaderEncoding("utf-8"); // 优先级更高
fileItem.getString("UTF-8"); // 解决获取表单的值乱码的问题

SpringMVC文件上传

springmvc实现文件上传相比JavaWeb就简单的多了,底层还是调用了commons-fileupload。

浏览器上传表单要求和JavaWeb一样。

案例:浏览器选择一张图片,上传到服务器指定目录下

1、导依赖

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.3</version>
</dependency>

2、编写前端页面,使用上面JavaWeb项目的页面

3、在springmvc核心配置文件中配置文件解析器,用于将表单信息装在MultipartFile 类中。

 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--        <property name="maxUploadSize" value="10485760"/>-->
        <property name="defaultEncoding" value="UTF-8" />
    </bean>

注:如果不配置文件上传解析器,就会默认使用req.getParameter解析表单,这样解析可能为null,也能报其他错误。

4、编写控制器

控制器上接收表单传来的数据在控制方法上使用参数接收即可,但普通的文本域使用相对应的数据类型的参数接收,但是文件表单表单中的文件要使用MultipartFile类型的参数接收。

MultipartFile相关的API:

zQWBH1.png

如果控制方法的参数名称和前端传过来的不一致,普通的文体域使用@RequestParam注解统一,而文件要使用**@RequestPart**注解。

@Controller
@RequestMapping("/file")
public class MvcFileController {
    public ModelAndView getFile(@RequestParam("name") String userName, @RequestPart("fileName") MultipartFile file){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("show");
        return modelAndView;
    }
}

​ 控制器控制方法代码:

 @RequestMapping(value = "/upload",method = RequestMethod.POST)
    public ModelAndView getFile(String name, @RequestPart("fileName") MultipartFile fileName1){
        System.out.println("name = " + name);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("show");
//        获取文件的name值
        String fileName = fileName1.getName();
        System.out.println("fileName = " + fileName);
//        获取文件MIME类型
        String contentType = fileName1.getContentType();
        System.out.println("contentType = " + contentType);
//        判断文件是否空
        if(!fileName1.isEmpty()){
            File file1 = new File(fileName);
            try {
                //保存文件
                fileName1.transferTo(file1);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return modelAndView;
    }

案例二:上传多个文件

编写前端:

<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/21
  Time: 16:18
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>allFile.jsp</title>
</head>
<body>
<form action="<c:url value='/file/allfile'/> " method="post" enctype="multipart/form-data">
    <p>
        <input type="file" name="pic">
    </p>
    <p>
        <input type="file" name="word">
    </p>
    <input type="submit" value="提交">
</form>
</body>
</html>

编写控制器控制方法

@RequestMapping("/allfile")
public ModelAndView allFie(HttpServletRequest request,MultipartFile pic,MultipartFile word){
    processFile(pic,request,"/WEB-INF/img");
    processFile(word,request,"/WEB-INF/file");
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("show");
    return modelAndView;
}
public void processFile(MultipartFile file, HttpServletRequest request,String path){
    String fileName = file.getName();
    String realPath = request.getServletContext().getRealPath(path);
    File file1 = new File(realPath);
    if(!file1.exists()){
        file1.mkdir();
    }
    String originalFilename = file.getOriginalFilename();
    //获得文件的后缀名
    String extension = FilenameUtils.getExtension(originalFilename);
    String lastName=fileName+'.'+extension;
    File file2 = new File(realPath, lastName);
    try {
        file.transferTo(file2);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

案例三:上传多个视频或者同时上传多个同类型文件

前端要求每个表单控件的name值要相同。

服务器也就是控制方法的参数使用(MultipartFile[]接收。

前端代码:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/21
  Time: 16:48
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>videos.jsp</title>
</head>
<body>
<form aria-atomic="<c:url value='/file/videos'/> " method="post" enctype="multipart/form-data">
    <p><input type="file" name="videos"/></p>
    <p><input type="file" name="videos"/></p>
    <p><input type="file" name="videos"/></p>
    <input type="submit" value="提交"/>
</form>
</body>
</html>

控制器控制方法

@RequestMapping("/videos")
public ModelAndView getVideos(MultipartFile[] videos,HttpServletRequest request) throws IOException {
    String realPath = request.getServletContext().getRealPath("/WEB-INF/voide");
    File file = new File(realPath);
    if(!file.exists()){
        file.mkdir();
    }
    for (MultipartFile video : videos) {
        String videoName = video.getName();
        String originalFilename = video.getOriginalFilename();
        String extension = FilenameUtils.getExtension(originalFilename);
        videoName=videoName+"."+extension;
        File file1 = new File(realPath, videoName);
        video.transferTo(file1);
    }
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("show");
    return modelAndView;
}

JavaWeb文件下载

文件注意事项:

1、下载的流程是在浏览器弹出一个下载框即可,不同的浏览器的下载框不一样。有些需要另存为,有些不需要。

2、被下载的文件最好放在/WEB-INF目录下,不让用户直接通过输入地址访问文件,而是需要用户通过链接请求servlet来实现下载。

3、需要下载的文件通过转换成 对应的流,通过response.getOutputStream() 写给浏览器。

4、下载文件时,浏览器通过设置Content-Type响应头来指定响应文件的类型。

5、下载文件不同类型浏览器会有不同的处理方案:

  • 如果是视频或者音频,会直接在浏览器上播放;如果是图片,会直接在浏览器显示。
  • 如果希望文件弹出下载框,要添加响应头content-disposition

注:content-disposition默认是 inline ,就是让浏览器直接解析,浏览器会根据文件类型解析。值有attachment;filename,filename就是设置弹出下载框,弹出下载框的文件名为filename属性的值。

总结:下载关心的二头一流,两头是是 Content-Type 和 content-disposition 响应头,一流是响应流response.getOutputStream() 。

案例:通过点击发送请求下载文件

前端页面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/21
  Time: 20:14
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>download</title>
</head>
<body>
<a href="<c:url value='/download?fileName=抖音-记录美好生活3.mp4'/>">下载文件</a>
</body>
</html>

后端:

package com.lwg.controller;

import org.apache.commons.io.IOUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * @author lwg
 * @title
 * @create 2022/11/21 19:55
 */
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        //获取下载文件名字
        String fileName = req.getParameter("fileName");
        String filePath = getServletContext().getRealPath("/WEB-INF/file/" + fileName);
        System.out.println(filePath);
        File file = new File(filePath);
        if (!file.exists()){
            System.out.println("文件不存在");
            resp.setContentType("text/html;charset=utf-8");
            resp.getWriter().println("文件名错误请求重试");
        }
        String mimeType = getServletContext().getMimeType(fileName);
//        将文件名重新编码,因为浏览器处理用ISO-8859-1来处理
        fileName = new String(fileName.getBytes(), "ISO-8859-1");
//        设置两头
        resp.setHeader("Content-Type",mimeType);
        resp.setHeader("Content-Disposition", "attachment;filename=" + fileName);
//        设置响应体,将流写回浏览器
        IOUtils.copy(new FileInputStream(file),resp.getOutputStream());

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

SpringMVC文件下载

和JavaWeb文件下载一样。

还是上面的案例:

前端还是那个

后端控制方法:

 @RequestMapping("/download")
    public ModelAndView downloadFile(String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException {
        String filePath = request.getServletContext().getRealPath("/WEB-INF/file/" + fileName);
        ModelAndView modelAndView = new ModelAndView();
        System.out.println(filePath);
        File file = new File(filePath);
        if (!file.exists()){
            System.out.println("文件不存在");
            modelAndView.addObject("msg","文件不存在");
            modelAndView.setViewName("show");
            return modelAndView;
        }
        InputStream resourceAsStream = request.getServletContext().getResourceAsStream("WEB-INF/file/" + fileName);
        String mimeType = request.getServletContext().getMimeType(fileName);
        response.setHeader("Content-Type",mimeType);
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        ServletOutputStream outputStream = response.getOutputStream();
        byte[] bytes = IOUtils.toByteArray(resourceAsStream);
        outputStream.write(bytes);
        outputStream.flush();
        outputStream.close();
        modelAndView.addObject("msg","成功下载");
        modelAndView.setViewName("show");
        return modelAndView;

    }

案例三:使用 SpringMVC 提供的 ResponseEntity 实现文件下载

核心思想是使用ResponseEntity ,继承了HttpEntity 并增加了 status 状态码。可以在SprigMVC框架中作为方法返回值使用同 HttpEntity 一样,ResponseEntity 代表了一个响应实体对象,该对象包含了 header 和 body ,即(响应)头和请求(响应)体,另外还有 Status 状态码。

文件那些属于二进制,可以设置 ResponseEntity 的泛型为字节数组byte[] 。

控制方法:

  @RequestMapping("/download2")
    public ResponseEntity<byte[]> downloadFiele2(String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException {
        InputStream resourceAsStream = request.getServletContext().getResourceAsStream("/WEB-INF/file/" + fileName);
        byte[] bytes = IOUtils.toByteArray(resourceAsStream);
//        创建HttpHeaders对象设置响应头信息
        MultiValueMap<String,String> httpHeaders = new HttpHeaders();
//        设置下载方式和下载名字
        httpHeaders.set("Content-Disposition", "attachment;filename=" + getFileName(fileName,request));
        HttpStatus statuCode = HttpStatus.OK;
//        创建ResponseEntity对象
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes,httpHeaders,statuCode);
        return responseEntity;

    }
    public String getFileName(String fileName,HttpServletRequest request) throws UnsupportedEncodingException {
//       处理下载文件中文名
        String header = request.getHeader("User-Agent");
//        针对IE浏览器或者IE内核的浏览器
        if (header.contains("MSIE") || header.contains("Trident") || header.contains("Edge")) {
            fileName = URLEncoder.encode(fileName, "utf-8");

        } else {
            fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
        }
        return fileName;
    }

SpringMVC自定义转换器

SpringMVC针对前端传过来的参数会自动根据类型自动转换成对应的控制器的形参类型,但是有时候也可能不能够转换的情况,这时候就需要我们自定义转换器了。

实现有三种:

方式一:控制器中手动转换

前端代码:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/21
  Time: 22:10
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="<c:url value='/data/transformation1'/> " method="post">
    姓名:<input type="text" name="name"><br />
    生日:<input type="date" name="birthday"><br/>
    <input type="submit" value="提交">
</form>
</body>
</html>

控制器方法:

@RequestMapping("/transformation1")
public ModelAndView dataConverter(String name,String birthday){
    System.out.println("name = " + name);
    System.out.println("birthday = " + birthday);
    Date date=null;
    try {
        date = new SimpleDateFormat("yyyy-MM-dd").parse(birthday);
    } catch (ParseException e) {
        e.printStackTrace();
    }
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("date",date);
    modelAndView.setViewName("show");
    return modelAndView;

}

方式二:使用@DateTimeFormat注解处理日期格式

上面的转换是写在控制器里的,每次处理日期都需要写这些转换的代码,可,可以使用注解 @DateTimeFormat来处理

控制器方法:

@RequestMapping("/transformation2")
public ModelAndView dateType2(String name, @DateTimeFormat(pattern = "yyyyy-MM-dd")String birthday){
    System.out.println("birthday = " + birthday);
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("date",birthday);
    modelAndView.setViewName("show");
    return modelAndView;
}

pattern属性写日期类型的格式。

方式三:自定义转换器,实现Converter接口

案例一的解决方法每次都要在控制器写处理,而springmvc提供了Converter接口,让我们自定义转换器,实现一劳永逸。

Converter接口提供两个泛型zlsz2d.png

S:需要转换的类型

T:转换成的类型

重写Converter的convert方法即可,在方法里写类型转换的代码。

自定义类型转换器类:

package com.lwg.converter;

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

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


/**
 * @author lwg
 * @title
 * @create 2022/11/21 23:02
 * 自定义类型转换器
 */
//实现converter接口
public class DateConverter implements Converter<String,Date> {


    @Override
    public Date convert(String source) {
        Date date=null;
        try {
            date=new SimpleDateFormat("yyyy-MM-dd").parse(source);

        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}

在springmvc核心配置文件配置自定义转换器

<!--    配置类型转换器-->
    <bean id="formattingConversionServiceFactoryBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.lwg.converter.DateConverter"/>
            </set>
        </property>
    </bean>
开启
 <mvc:annotation-driven conversion-service="formattingConversionServiceFactoryBean"/>

修改控制方法

@RequestMapping("/transformation2")
public ModelAndView dateType2(String name, Date birthday){
    System.out.println("birthday = " + birthday);
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("date",birthday);
    modelAndView.setViewName("show");
    return modelAndView;
}

练习:客户端浏览器在输入框输入对应的内容,比如输入“足球、篮球、羽毛球”,后端把数据解析成List<String>类型的形参中。

前端页面:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: 35173
  Date: 2022/11/21
  Time: 23:35
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>爱好</title>
</head>
<body>
<form action="<c:url value='/data/hobby'/> " method="post">
    爱好:<input type="text" name="hobby"><br/>
    <input type="submit" value="提交">
</form>
</body>
</html>

编写控制方法

@RequestMapping("/hobby")
public ModelAndView hobbyString(@RequestParam List<String> hobby){
    System.out.println("hobby = " + hobby);
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("date",hobby);
    modelAndView.setViewName("show");
    return modelAndView;
}

注:这里要使用@RequestParam注解,没有会报错

编写类型转换器

package com.lwg.converter;

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author lwg
 * @title
 * @create 2022/11/21 23:37
 */
public class ListConverter implements Converter<String, List<String>> {
    @Override
    public List<String> convert(String source) {
        String[] split = source.split("、");
        List<String> resutlList=new ArrayList<>();
        for (int i=0;i<split.length;i++){
            resutlList.add(split[i]);
        }
        return resutlList;
    }
}

编写springmvc核心配置文件,配置自定义类型器

<bean id="formattingConversionServiceFactoryBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.lwg.converter.DateConverter"/>
            <bean class="com.lwg.converter.ListConverter"/>
        </set>

    </property>
</bean>

SpringMVC执行流程以及工作原理

springMVC执行流程以及工作原理图和流程分析

执行流程图:

z3Z4eK.png

执行流程:

  1. 用户点击某个请求路径,发起一个 HTTP request 请求,该请求会被提交到DispatcherServlet (前端控制器);
  2. 由 DispatcherServlet 请求一个或多个 HandlerMapping (处理器映射器),并返回一个执行链( HandlerExecutionChain )。
  3. DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter (处理器适配器);
  4. HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler (常称为 Controller );
  5. Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象( Spring MVC 的底层对象,包括 Model 数据模型和 View 视图信息);
  6. HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ;
  7. DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver (视图解析器)对视图进行解析;
  8. ViewResolver 根据 View 信息匹配到相应的视图结果,并返回给DispatcherServlet ;
  9. DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View (视图);
  10. 视图负责将结果显示到浏览器(客户端)。

SpringMVC核心接口

springMVC涉及的组件的有DispatcherServlet(前端控制器)、HandlerMapping(处理器映射器)、HandlerAdapter (处理器适配器)、 Handler(处理器)、 ViewResolver(视图解析器)和View (视图)。

DispatcherServlet(前端控制器):

springMVC的所有请求都要由DispatcherServlet来统一分发,相当于一个转发器或中央处理器,控制整个流程的执行,对各个组件进行统一调度,降低组件之间的耦合性,有利于组件之间的拓展。

HandlerMapping(处理器映射器):

根据请求的URL路径,通过;注解或者XML配置,找到匹配的处理器信息。

HandlerAdapter (处理器适配器):

根据映射找到处理器,按照特定的规则执行相关的处理器。

**Handler(处理器):**执行请求,返回相应的数据和视图信息,将其封装在ModelAndView 对象中。

**ViewResolver(视图解析器):**进行解析操作,将ModelAndView 的View信息将逻辑视图名称解析真正的视图View。

**View (视图):**其本身是一个接口,实现类支持不同的 View 类型( JSP 、 FreeMarker 、 Excel等)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

广深度优先

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值