SpringMVC笔记

SpringMVC笔记

一、SpringMVC概述

1.1、MVC模式

MVC的概念:

​ MVC是Model、View、Controller的缩写,分别代表Web应用程序中的3种职责。

  • 模型:用于存储数据以及处理用户请求的业务逻辑。
  • 视图:向控制器提交数据,显示模型中的数据。
  • 控制器:根据视图提出的请求判断将请求和数据交给哪个模型处理,将处理后的有关结果交给哪个视图更新显示。

基于Servlet的MVC模式的具体实现:

  • 模型:一个或多个JavaBean对象,用于存储数据(实体模型,由JavaBean类创建)和处理业务逻辑(业务模型,由一般的Java类创建)。
  • 视图:一个或多个JSP页面,向控制器提交数据和为模型提供数据显示,JSP页面主要使用HTML标记和JavaBean标记来显示数据。
  • 控制器:一个或多个Servlet对象,根据视图提交的请求进行控制,即将请求转发给处理业务逻辑的JavaBean,并将处理结果存放到实体模型JavaBean中,输出给视图显示。

基于Servlet的MVC模式的流程图如下所示:

1.2、SpringMVC

​ SpringMVC:是基于Spring的一个框架,实际上就是Spring的一个模块,专门是做web开发的。可以理解是Servlet的一个升级。

​ web开发底层是Servlet,框架是在Servlet基础上面加入一些功能,让你做web开发方便。

​ SpringMVC就是一个Spring。Spring是容器,IoC能够管理对象,使用<bean>,@Component、@Repository、@Service、@Controller。SpringMVC能够创建对象,放入到容器中(SpringMVC容器),SpringMVC容器中放的是控制器对象。

​ 我们要做的是使用@Controller创建控制器对象,把对象放入到SpringMVC容器中,把创建的对象作为控制器使用。这个控制器对象能接收用户的请求,显示处理结果,就当做是一个Servlet使用。

使用@Controller注解创建的是一个普通类的对象,不是Servlet,SpringMVC赋予了控制器对象一些额外的功能。

​ web开发底层是Servlet,SpringMVC中有一个对象是Servlet:DispatherServlet(中央调度器)。DispatherServlet:负责接收用户的所有请求,用户把请求给了DispatherServlet,之后DispatherServlet把请求转发给我们的Controller对象,最后是Controller对象处理请求。

1.3、SpringMVC工作原理

SpringMVC的工作流程:

  • 客户端请求提交到DispatcherServlet;
  • 由DispatcherServlet控制器寻找一个或多个HandlerMapping,找到处理请求的Controller;
  • DispatcherServlet将请求提交到Controller;
  • Controller调用业务逻辑处理后返回ModelAndView;
  • DispatcherServlet寻找一个或多个ViewResolver视图解析器,找到ModelAndView指定的视图;
  • 视图负责将结果显示到客户端。
SpringMVC执行流程图:

SpringMVC内部请求的处理流程

也就是SpringMVC接收请求到处理完成的过程:

1、用户发起请求some.do

2、DispatcherServlet接收请求some.do,把请求转交给处理器映射器

​ 处理器映射器:SpringMVC框架中的一种对象,框架把实现了HandlerMapping接口的类都叫做映射器(多个)

​ 处理器映射器的作用:根据请求,从SpringMVC容器对象中获取处理器对象(MyController controller = ctx.getBean(“some.do”))

​ 框架把找到的处理器对象放到一个叫做处理器执行链(HandlerExecutionChain)的类保存

​ HandlerExecutionChain:类中保存着:1、处理器对象(MyController);

​ 2、项目中的所有的拦截器 List<HandlerInterceptor> interceptorList;

​ 方法调用:HandlerExecutionChain mapperHandler = getHandler(processedRequest);

3、DispatcherServlet把2中的HandlerExecutionChain中的处理器对象交给了处理器适配器对象(多个)

​ 处理器适配器:SpringMVC框架中的对象,需要实现HandlerAdapter接口。

​ 处理器适配器作用:执行处理器方法(调用MyController.doSome()得到返回值ModelAndView)

​ 中央调度器中调用适配器:HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

​ 执行处理器方法:mv = ha.handle(processedRequest,response,mappedHandler.getHandler());

4、DispatcherServlet把3中获取的ModelAndView交给了视图解析器对象。

​ 视图解析器:SpringMVC中的对象,需要实现ViewResolver接口(可以有多个)

​ 视图解析器作用:组成视图完整路径,使用前缀,后缀。并创建View对象。

​ View是一个接口,表示视图的,在框架中jsp、html不是String表示,而是使用View和他的实现类表示视图。

​ InternalResourceView:视图类,表示jsp文件,视图解析器会创建InternalResourceView类对象。

​ 这个对象的里面,有一个属性url=/WEB-INF/view/show.jsp

5、DispatcherServlet把4步骤中创建的View对象获取到,调用View类自己的方法,把Model数据放入到request作用域。执行对象视图的forward。请求结束。

1.4、举例:

第一个SpringMVC的项目
需求:
用户在页面发起一个请求,请求交给SpringMVC的控制器对象,
并显示请求的处理结果。(在结果页面显示一个欢迎语句)
实现步骤:
1、新建web maven工程并加入依赖:
    spring-webmvc依赖,间接把spring的依赖都加入到项目
    servlet、jsp依赖
2、(重点)在web.xml中,注册springmvc框架的核心对象DispatcherServlet
    1)DispatcherServlet叫做中央调度器,是一个servlet,它的父类继承HttpServlet
    2)DispatcherServlet也叫做前端控制器(front controller)
    3)DispatcherServlet负责接收用户提交的请求,调用其他控制器的对象
        并把请求的处理结果显示给用户
3、创建一个发起请求的页面index.jsp
4、创建控制器类
    1)在类的上面加入@Controller注解,创建对象,并放入到springmvc容器中
    2)在类中的方法上面加入@RequestMapping注解
5、创建一个作为结果的jsp,显示请求处理的结果
6、创建springmvc的配置文件(和spring的配置文件一样)
    1)声明组件扫描器,指定@Controller注解所在的包名
    2)声明视图解析器,帮助处理视图的

web.xml

<?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">
    <!--声明springmvc的核心对象DispatcherServlet
        需要在tomcat服务器启动后,创建DispatcherServlet对象的实例
        为什么要创建DispatcherServlet对象的实例呢?
        因为创建DispatcherServlet在他的创建过程中,会同时创建springmvc容器对象
        读取springmvc的配置文件,把这个配置文件的对象都创建好,当用户发起
        请求时就可以直接使用对象了
        servlet的初始化会执行init()方法,DispatcherServlet在init中【
            创建容器,读取配置文件
            WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
            把容器对象放入到ServletContext中
            getServletContext().setAttribute(key,ctx);
        】
    -->
    <servlet>
        <servlet-name>myweb</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <!--自定义springmvc读取的配置文件的位置-->
        <init-param>
            <!--springmvc的配置文件的位置的属性-->
            <param-name>contextConfigLocation</param-name>
            <!--指定自定义文件的位置-->
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!--在tomcat启动后,创建Servlet对象
            load-on-startup,表示Tomcat启动之后创建对象的顺序,他的值是整数,数值越小,
            tomcat创建对象的时间越早,大于等于0的整数
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>myweb</servlet-name>
        <!--
            使用框架的时候,url-pattern可以使用两种值
            1、使用扩展名方式,语法 *.xxx, xxx是自定义的扩展名。常用的方式 *.do,*.action,*.mvc等
                http://localhost:8080/some.do
            2、使用斜杠 "/"
        -->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>第一个springmvc项目</p>
    <p><a href="some.do">发起some.do的请求</a> </p>
    <p><a href="other.do">发起other.do的请求</a> </p>
</body>
</html>

控制器类MyController:

package com.controller;

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

/**
 * @Controller:创建处理器对象,对象放在springmvc容器中
 * 位置:类的上面
 * 和spring中@Service是一样的都是创建对象的
 * 能处理请求的都是控制器(处理器): MyController能处理请求
 *                              叫做后端控制器(back controller)
 */
@Controller
public class MyController {
    /*
        处理用户提交的请求,springmvc中是使用方法来处理的
        方法是自定义的,可以有多种返回值,多种参数,方法名称自定义
    */
    /**
     * 准备使用doSome方法处理some.do的请求
     *  @RequestMapping: 请求映射,作用是吧一个请求地址和一个方法绑定在一起
     *                    一个请求指定一个方法来处理
     *          属性:1、value是一个String,表示请求的uri地址的(some.do)
     *                  value的值必须是唯一的,不能重复,在使用时,推荐地址以"/",斜杠表示根的意思
     *          位置:1、在方法的上面,常用的
     *               2、在类的上面
     *  说明:使用RequestMapping修饰的方法叫做处理器方法或者控制器方法
     *  使用 @RequestMapping修饰的方法可以处理请求的,类似Servlet中的doGet、doPost
     *  返回值ModelAndView:表示本次请求的处理结果
     *      Model:数据,请求处理完成后,要显示给用户的数据
     *      View:视图,比如jsp等等
     */
    @RequestMapping(value = {"/some.do","/first.do"})
    public ModelAndView doSome() {
        //处理some.do的请求了,相当于service调用处理完成了
        ModelAndView mv = new ModelAndView();
        //添加数据,框架在请求的最后把数据放入到request作用域
        mv.addObject("msg","欢迎使用springmvc做web开发");
        mv.addObject("fun","执行的是doSome方法");
        //指定视图,指定视图的完整路径
        //框架对视图执行forward操作(请求转发)
        //mv.setViewName("/WEB-INF/view/show.jsp");
        //mv.setViewName("/WEB-INF/view/other.jsp");
        //当配置了视图解析器后,可以使用逻辑名称(文件名),指定视图
        //框架会使用视图解析器的前缀 + 逻辑名称 + 后缀 组成完整的路径,这里就是字符串连接操作
        // /WEB-INF/view + show + .jsp
        mv.setViewName("show");
        //返回mv
        return mv;
    }
    @RequestMapping(value = {"/other.do","/second.do"})
    public ModelAndView doOther() {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","===欢迎使用springmvc做web开发===");
        mv.addObject("fun","执行的是doOther方法");
        mv.setViewName("other");
        //返回mv
        return mv;
    }
}

作为结果的视图(show.jsp、other.jsp)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>/WEB-INF/view/show.jsp从request作用域获取数据</h3> <br/>
    <h3>msg数据: ${msg}</h3> <br/>
    <h3>fun数据: ${fun}</h3> <br/>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>/WEB-INF/view/other.jsp从request作用域获取数据</h3> <br/>
    <h3>msg数据: ${msg}</h3> <br/>
    <h3>fun数据: ${fun}</h3> <br/>
</body>
</html>

把other.jsp、show.jsp放在WEB-INF目录下,保护视图,防止被用户访问。

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">

    <!--声明组件扫描器-->
    <context:component-scan base-package="com.controller" />

    <!--声明springmvc框架中的视图解析器,帮助开发人员设置文件的路径-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
        <!--前缀:视图文件的路径-->
        <property name="prefix" value="/WEB-INF/view/" />
        <!--后缀,视图文件的扩展名-->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

二、SpringMVC注解式开发

2.1、@RequestMapping定义请求规则

1、指定模块名称

​ 通过@RequestMapping注解可以定义处理器对于请求的映射规则。该注解可以注解在方法上,也可以注解在类上,但意义是不同的。@RequestMapping的value属性用于定义所匹配请求的URI。value属性值常以 “/” 开始。

​ 一个@Controller所注解的类中,可以定义多个处理器方法。当然,不同的方法所匹配的URI是不同的。这些不同的URI被指定在注解于方法之上的@RequestMapping的value属性中,但若这些请求具有相同的URI部分,则这些相同的URI,可以被抽取到注解在类之上的@RequestMapping的value属性中。此时的这个URI表示模块的名称。URI的请求是相对于Web的根目录。

2、对请求提交方式的定义

​ 对于@RequestMapping,其中有一个属性method,用于对被注解的方法所处理请求的提交方式进行限制,即只有满足该method属性指定的提交方式的请求,才会执行该被注解的方法。

​ method属性的取值为RequestMethod枚举常量。常用的为RequestMethod.GET 与 RequestMethod.POST,分别表示提交方式的匹配规则为GET与POST提交。

若不指定method属性,则无论是GET还是POST提交方式,均可匹配。即对于请求的提交方式无要求。

index.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>第一个springmvc项目</p>
    <p><a href="user/first.do">发起some.do的get请求</a> </p>
    <form action="user/first.do" method="post" >
        <input type="submit" value="post请求other.do">
    </form>
</body>
</html>

控制器类MyController:

package com.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;

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

@Controller
@RequestMapping("/user")
public class MyController {
    /**
     * @RequestMapping:请求映射
     *             属性:Method,表示请求的方式,他的值是RequestMethod类的枚举值
     *             例如表示get请求方式:RequestMethod.GET
     *             post方式:RequestMethod.POST
     *  你不用get方式,错误是:
     *  HTTP状态 405 - Request method 'GET' not supported
     */
    @RequestMapping(value = "/some.do",method = RequestMethod.GET)
    public ModelAndView doSome() {
        //处理some.do的请求了,相当于service调用处理完成了
        ModelAndView mv = new ModelAndView();

        mv.addObject("msg","欢迎使用springmvc做web开发");
        mv.addObject("fun","执行的是doSome方法的get请求");
        mv.setViewName("show");
        //返回mv
        return mv;
    }
    //你若不用post方式,错误是:HTTP状态 405 - 方法不允许
    @RequestMapping(value = "/other.do",method = RequestMethod.POST)
    public ModelAndView doOther() {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","===欢迎使用springmvc做web开发===");
        mv.addObject("fun","执行的是doOther方法的post请求");
        mv.setViewName("other");
        //返回mv
        return mv;
    }
    //不指定请求方式,没有限制
    @RequestMapping(value = "/first.do")
    public ModelAndView doFirst(HttpServletRequest request,
                                HttpServletResponse response,
                                HttpSession session) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","===欢迎使用springmvc做web开发===()"+ request.getParameter("name"));
        mv.addObject("fun","执行的是doFirst方法");
        mv.setViewName("other");
        //返回mv
        return mv;
    }
}

2.2、处理器方法的参数

处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可以在方法内直接使用。

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • 请求中所携带的请求参数
2.2.1、逐个参数接收

只要保证请求参数名与该请求处理方法的参数名相同即可。

2.2.2、请求参数中文乱码问题

对于前面所接收的请求参数,若含有中文,则会出现中文乱码问题。Spring对于请求参数中的中文乱码问题,给出了专门的字符集过滤器。

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

解决方案:

​ 在web.xml中注册字符集过滤器,即可解决Spring的请求参数的中文乱码问题。

<!--注册声明过滤器,解决post请求乱码的问题-->
    <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>
        <!--强制请求对象(HttpServletRequest)使用encoding编码的值-->
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <!--强制响应对象(HttpServletResponse)使用encoding编码的值-->
        <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>

源码分析:

​ 字符集1设置核心方法:

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

2.2.3、校正请求参数名@RequestParam

@RequestParam:解决请求中参数名和处理器的形参名不一样的问题。

校正请求参数,是指若请求URL所携带的参数名称与处理方法中指定的参数名不同时,则需要在处理方法参数前,添加一个注解

@RequestParam(“请求参数名”),指定请求URL所携带参数的名称。该注解是对处理器方法参数进行修饰的,value属性指定请求参数的名称。

修改index.jsp页面,将表单中的参数名称修改的与原来不一样。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>请求参数名和处理器的形参名不一样</p>
    <form action="receiveparam.do" method="post" >
        姓名:<input type="text" name="rname"> <br/>
        年龄:<input type="text" name="rage"> <br/>
        <input type="submit" value="提交参数">
    </form>

修改处理器:

 /**
     * 请求中参数名和处理器的形参名不一样
     * @RequestParam: 解决请求中参数名和处理器的形参名不一样的问题
     *  属性: 1、value:请求中的参数名称
     *        2、required 是一个boolean类型,默认是true
     *              true:表示请求中必须包含此参数
     *  位置:   在处理器方法的形参定义的前面
     */
    @RequestMapping(value = "/receiveparam.do")
    public ModelAndView receiveParam(@RequestParam("rname") String name,
                                     @RequestParam("rage")Integer age) {
        System.out.println("name= "+name+" age= " + age);
        //可以直接在方法中使用 name,age
        //处理some.do的请求了,相当于service调用处理完成了
        ModelAndView mv = new ModelAndView();

        mv.addObject("myName",name);
        mv.addObject("myAge",age);
        mv.setViewName("show");
        //返回mv
        return mv;
    }
2.2.4、对象参数接收

将处理器方法的参数定义为一个对象,只要保证请求参数名与这个对象的属性同名即可。

定义Person类:

public class Person {
    private String name;
    private Integer age;
    //setter and getter
    //toString()
}

修改MyController类:

    /**
     * 处理器方法形参是Java对象,这个对象的属性名和请求参数名一样的
     * 框架会创建形参的java对象,给属性赋值,请求中的参数是name,框架会调用setName()
     *
     */
    @RequestMapping(value = "/receiveobject.do")
    public ModelAndView receiveObject(Person person) {
        System.out.println("name= "+person.getName()+" age= " +person.getAge());
        //可以直接在方法中使用 name,age
        //处理some.do的请求了,相当于service调用处理完成了
        ModelAndView mv = new ModelAndView();

        mv.addObject("myName",person.getName());
        mv.addObject("myAge",person.getAge());
        mv.addObject("myPerson",person);
        mv.setViewName("show");
        //返回mv
        return mv;
    }

修改show.jsp视图页面:

<body>
    <h3>/WEB-INF/view/show.jsp从request作用域获取数据</h3> <br/>
    <h3>myName数据: ${myName}</h3> <br/>
    <h3>myAge数据: ${myAge}</h3> <br/>
    <h3>myPerson数据: ${myPerson}</h3> <br/>
</body>

2.3、处理器方法的返回值

使用@Controller注解的处理器的处理器方法,其返回值常用的有四种类型:

  • 第一种:ModelAndView
  • 第二种:String
  • 第三种:无返回值void
  • 第四种:返回自定义类型对象
2.3.1、返回ModelAndView

若处理器方法处理完后,需要跳转到其他资源,且又要在跳转的资源间传递数据,此时处理器方法返回ModelAndView比较好。当然,若要返回ModelAndView,则处理器方法中需要定义ModelAndView对象。

在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的Ajax异步响应),此时若返回ModelAndView,则将总是有一部分多余:要么Model多余,要么View多余,故此时返回ModelAndView将不合适。

2.3.2、返回String

处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址。

返回内部资源逻辑视图名

若要跳转的资源为内部资源,则视图解析器可以使用InternalResourceViewResolver内部资源视图解析器。此时处理器方法返回的字符串就是要跳转页面的文件名去掉文件扩展名后的部分,这个字符串与视图解析器中的prefix、suffix相结合,即可形成要访问的URI。

    <!--声明springmvc框架中的视图解析器,帮助开发人员设置文件的路径-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
        <!--前缀:视图文件的路径-->
        <property name="prefix" value="/WEB-INF/view/" />
        <!--后缀,视图文件的扩展名-->
        <property name="suffix" value=".jsp" />
    </bean>
@Controller
public class MyController {

    /**
     *处理器方法返回String ---表示逻辑视图名称,需要配置视图解析器
     */
    @RequestMapping(value = "/returnString-view.do")
public String doReturnView(HttpServletRequest req,String name,Integer age) {
        System.out.println("doReturnView,name="+name+" age="+age);
        //可以自己手工添加数据到request作用域
        req.setAttribute("name",name);
        req.setAttribute("age",age);
        //show是逻辑视图名称,项目中配置了视图解析器
        //框架对视图执行的是forward转发操作
        return "show";
    }
}

当然也可以直接返回资源的物理视图名。不过此时就不需要在视图解析器中再配置前缀与后缀了。

/**
     *处理器方法返回String ---表示完整返回路径,此时不能配置视图解析器
     */
    @RequestMapping(value = "/returnString-view2.do")
    public String doReturnView2(String name, Integer age) {
        System.out.println("doReturnView,name="+name+" age="+age);
        //完整返回路径,项目中不能配置视图解析器
        //框架对视图执行的是forward转发操作
        return "/WEB-INF/view/show.jsp";
    }
2.3.3、返回void

对于处理器方法返回void的应用场景,Ajax响应。

void:不能表示数据,也不能表示视图。在处理ajax的时候,可以使用void返回值,通过HttpServletResponse输出数据,响应ajax请求。

Ajax请求服务器端返回的就是数据,和视图无关

若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回void。

eg:对Ajax的异步请求的响应

maven中加入jackson依赖

​ 因为服务端向浏览器传回的是JSON数据,需要使用一个工具类将字符串包装为JSON格式,所以需要导入JSON的依赖。

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

引入jQuery库

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

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<script type="text/javascript" src="lib/jquery-3.6.0.js"></script>
<script type="text/javascript">
    $(function () {
        $("button").click(function () {
            $.ajax({
                url:"returnVoid-ajax.do",
                data:{
                    name:"zhangsan",
                    age:20
                },
                type:"post",
                //dataType:"json",
                success:function (resp) {
                    //resp从服务器端返回的时json格式的字符串
                    //jQuery会把字符串转为json对象,赋值给resp形参
                    alert(resp.name+ "   "+ resp.age);
                }
            })
        })
    })
</script>
<body>
<button id="button">发起Ajax请求</button>
</body>
</html>

修改MyController类

 //处理方法返回void,响应Ajax请求
    //手工实现ajax,json数据:代码有重复的 1、Java对象转为json
    // 2、通过HttpServletResponse输出json数据
    @RequestMapping(value = "/returnVoid-ajax.do")
    public void doReturnVoidAjax(HttpServletResponse resp, String name, Integer age) throws IOException {
        System.out.println("doReturnView,name="+name+" age="+age);
        //处理ajax,使用json的数据格式
        //认为service调用完成了,使用Person表示处理结果
        Person person = new Person();
        person.setName(name);
        person.setAge(age);
        String json = "";
        //把结果的对象转为json格式的数据
        if(person != null) {
            ObjectMapper om = new ObjectMapper();
            json = om.writeValueAsString(person);
            System.out.println("person转换的json===" + json);
        }
        //输出数据,响应Ajax的请求
        resp.setContentType("application/json;charset-utf-8");
        PrintWriter pw = resp.getWriter();
        pw.println(json);
        pw.flush();
        pw.close();
    }

由于是服务端直接向浏览器发回数据,所以也就无需视图页面了。

2.3.4、返回对象Object

处理器方法也可以返回Object这个对象。这个对象可以是Integer、String、自定义对象,Map、List等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面现实的数据出现的。

返回对象,需要使用@ResponseBody注解,将转换后的JSON数据放入到响应体中。

由于返回Object数据,一般都是将数据转化为JSON对象后传递给浏览器页面的。而这个由Object转换为JSON,是由Jackson工具完成的。故使用时需要导入Jackson的依赖。

声明注解驱动:

​ 将Object数据转化为JSON数据,需要由消息转换器HttpMessageConverter完成。而转换器的开启,需要由<mvc:annotation-driven/>来完成。

SpringMVC使用消息转换器实现请求数据和对象,处理器方法返回对象和响应输出之间的自动转换。

当Spring容器进行初始化过程中,在<mvc:annotation-driven/>处创建注解驱动时,默认创建了8个HttpMessageConverter对象。也就是说,我们注册<mvc:annotation-driven/>,就是为了让容器为我们创建HttpMessageConverter对象。

HttpMessageConverter接口:是Spring3.0新添加的一个接口,负责将请求信息转换为一个对象(类型为T),将对象(类型为T)输出为响应信息。

现在做ajax,主要使用json的数据格式(如何将Ajax请求的数据转为json格式),实现步骤:

1、加入处理json的工具库的依赖,SpringMVC默认使用的jackson

2、在SpringMVC配置文件之间加入mvc:annotation-driven 注解驱动。这个注解驱动的功能是把结果对象转换为json格式

相当于:json = om.writeValueAsString(person);

**3、在处理器方法的上面加入@ResponseBody注解。**该注解的功能如下:

resp.setContentType(“application/json;charset-utf-8”);

PrintWriter pw = resp.getWriter();

pw.println(json);

没有加入注解驱动标签时的状态
org.springframework.http.converter.ByteArrayHttpMessageConverter
org.springframework.http.converter.StringHttpMessageConverter
org.springframework.http.converter.xml.SourceHttpMessageConverter
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter

加入注解驱动标签时的状态
org.springframework.http.converter.ByteArrayHttpMessageConverter
org.springframework.http.converter.StringHttpMessageConverter
org.springframework.http.converter.ResourceHttpMessageConverter
org.springframework.http.converter.ResourceRegionHttpMessageConverter
org.springframework.http.converter.xml.SourceHttpMessageConverter
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter

SpringMVC处理器方法返回Object,可以转为json输出到浏览器,响应Ajax的内部原理
1、<mvc:annotation-driven> 注解驱动。
    注解驱动完成的功能是 完成java对象到json、xml、text、二进制等数据格式的转换
    <mvc:annotation-driven>在加入到SpringMVC配置文件后,会自动创建 HttpMessageConverter接口
    的八个实现类对象,包括MappingJackson2HttpMessageConverter(使用jackson工具库中的ObjectMapper实现java对象转为json字符串)


    HttpMessageConverter接口:消息转换器。是Spring3.0新添加的一个接口,负责将请求信息转换为一个对象,将对象输出为响应信息。
    功能:定义了java对象转为json、xml等数据格式的方法,这个接口有很多实现类
     这些实现类完成 java对象到json、java对象到xml,java对象到二进制数据的转换
    下面的两个方法是控制器类把结果输出给浏览器时使用的:
    boolean canWrite(Class<?> var1, @Nullable MediaType var2);
    void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
    1)canWrite作用检查处理器方法的返回值,能不能转为var2表示的数据格式
      MediaType:表示数格式的,例如json、xml等
    2)write:把处理器方法的返回值对象,调用jackson中的ObjectMapper转为json字符串
        json = om.writeValueAsString(student);
   2、@ResponseBody注解
       放在处理器方法的上面,通过HttpServletResponse输出数据,响应Ajax请求的
       做这些工作的:
       resp.setContentType("application/json;charset-utf-8");
       PrintWriter pw = resp.getWriter();
       pw.println(json);
(1)返回自定义类型的对象

返回自定义类型对象时,不能以对象的形式直接返回给客户端浏览器,而是将对象转换为JSON格式的数据发送给浏览器的。转换器底层使用了Jackson转换方式将对象转换为JSON数据。

定义Person类:

public class Person {
    private String name;
    private Integer age;
    //setter and getter
    //toString()
}

修改处理器MyController

/**
     * 处理器方法返回一个Student,通过框架转为json,响应Ajax请求
     * @ResponseBody
     *  作用:把处理器方法返回对象转为json后,通过HttpServletResponse输出给浏览器
     *  返回对象时框架的处理流程:
     *   1、框架会把返回Person对象类型,调用框架中的ArrayList<HttpMessageConverter>中每个类的canWrite()方法
     *    检查那个HttpMessageConverter接口的实现类是否能处理Person类型的数据--MappingJackson2HttpMessageConverter
     *
     *   2、框架会调用实现类的write(),MappingJackson2HttpMessageConverter的write方法
     *    把person对象转为json,调用Jackson的ObjectMapper实现转为json
     *      contentType:application/json;charset=utf-8
     *   3、框架会调用@ResponseBody,把2的结果输出到浏览器,Ajax请求处理完成
     */
    @RequestMapping(value = "/returnPersonJson.do")
    @ResponseBody
    public Person doPersonJsonObject(String name, Integer age) {
        //调用service,获取请求结果数据数据,Person对象表示结果数据
        Person person = new Person();
        person.setName("张三同学");
        person.setAge(20);
        return person; //会被框架转为Json
    }

修改index.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<script type="text/javascript" src="lib/jquery-3.6.0.js"></script>
<script type="text/javascript">
    $(function () {
        $("button").click(function () {
            $.ajax({
                url:"returnPersonJson.do",
                type:"post",
                dataType:"json",
                success:function (resp) {
                    alert(resp.name+ "   "+ resp.age);
                }
            })
        })
    })
</script>
<body>
<button id="button">发起Ajax请求</button>
</body>
</html>

(2)返回List集合

修改MyController类

/**
     *  处理器方法返回List<Person>
     *  返回对象时框架的处理流程:
     *   1、框架会把返回List<Person>对象类型,调用框架中的ArrayList<HttpMessageConverter>中每个类的canWrite()方法
     *    检查那个HttpMessageConverter接口的实现类是否能处理Person类型的数据--MappingJackson2HttpMessageConverter
     *   2、框架会调用实现类的write(),MappingJackson2HttpMessageConverter的write方法
     *    把person对象转为json,调用Jackson的ObjectMapper实现转为json array
     *      contentType:application/json;charset=utf-8
     *   3、框架会调用@ResponseBody,把2的结果输出到浏览器,Ajax请求处理完成
     */
    @RequestMapping(value = "/returnPersonJsonArray.do")
    @ResponseBody
    public List<Person> doPersonJsonObjectArray(String name, Integer age) {
        List<Person> list = new ArrayList<Person>();
        //调用service,获取请求结果数据数据,Person对象表示结果数据
        Person person = new Person();
        person.setName("张三同学");
        person.setAge(20);
        list.add(person);
        person = new Person();
        person.setName("李四同学");
        person.setAge(25);
        list.add(person);
        return list; //会被框架转为Json
    }

修改index.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<script type="text/javascript" src="lib/jquery-3.6.0.js"></script>
<script type="text/javascript">
    $(function () {
        $("button").click(function () {
            $.ajax({
                url:"returnPersonJsonArray.do",
                type:"post",
                dataType:"json",
                success:function (resp) {
                    //resp从服务器端返回的时json格式的字符串
                    //jQuery会把字符串转为json对象,赋值给resp形参
                    //[{"name":"张三同学","age":20},{"name":"李四同学","age":25}]
                    $.each(resp,function (i,n) {
                        //i代表循环变量,n代表的是数组中的对象
                        alert(n.name+"  "+n.age)
                    })
                }
            })
        })
    })
</script>
<body>
<button id="button">发起Ajax请求</button>
</body>
</html>

(3)返回字符串对象

若要返回非中文字符串,将前面返回数值型数据的返回值直接修改为字符串即可,但若返回的字符串中带有中文字符,则接受方页面将会出现乱码。此时需要使用@RequestMapping的produces属性指定字符集。

/**
     * 处理器方法返回的是String,String表示数据,不是视图
     * 区分返回值String是数据还是视图,看有没有@ResponseBody注解
     * 如果有@ResponseBody注解,返回String就是数据,反之就是视图
     *
     * 默认使用"text/plain;charset=ISO-8859-1"作为Content-Type,导致中文有乱码
     * 解决方案:给RequestMapping增加一个属性produces,使用这个属性指定新的Content-Type
     *
     * 返回对象时框架的处理流程:
     *  1、框架会把返回String对象类型,调用框架中的ArrayList<HttpMessageConverter>中每个类的canWrite()方法
     *   检查那个HttpMessageConverter接口的实现类是否能处理String类型的数据--StringHttpMessageConverter
     *  2、框架会调用实现类的write(),StringHttpMessageConverter的write方法
     *   把字符串按照指定的编码处理  text/plain;charset=ISO-8859-1
     *
     *  3、框架会调用@ResponseBody,把2的结果输出到浏览器,Ajax请求处理完成
     */
    @RequestMapping(value = "/returnStringData.do",produces = "text/plain;charset=utf-8")
    @ResponseBody
    public String doString(String name, Integer age) {
        return "Hello SpringMVC 返回对象表示数据";
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<script type="text/javascript" src="lib/jquery-3.6.0.js"></script>
<script type="text/javascript">
    $(function () {
        $("button").click(function () {
            $.ajax({
                url:"returnStringData.do",
                type:"post",
                //dataType:"json",
                success:function (resp) {
                    alert("返回的是文本数据: " + resp);
                }
            })
        })
    })
</script>
<body>
<button id="button">发起Ajax请求</button>
</body>
</html>

2.4、解读<url-pattern/>

2.4.1、配置详解

(1)*.do

​ 在没有特殊要求的情况下,SpringMVC中央调度器DispatcherServlet的<url-pattern/>常使用后缀的匹配方式,如写为*.do或者*.action,*.mvc等。

(2)/

​ 可以写为 /,因为DispatcherServlet会将向静态资源的获取请求,例如.css、.js、.jpg等资源的获取请求,当作是一个普通的Controller请求。中央调度器会调用处理器映射器为其查找相应的处理器。当然也是找不到的,所以在这种情况下,所有的静态资源获取请求也均会报404错误。

2.4.2、静态资源的访问

tomcat本身能处理静态资源的访问,像html,图片,js文件都是静态资源

tomcat的web.xml文件有一个servlet名称是default,在服务器启动时创建的。

 <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
	</servlet>

	  <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>  <!--表示静态资源和未映射的请求都这个default处理-->
    </servlet-mapping>

default这个servlet作用:

The default servlet for all web applications, that serves static
resources. It processes all requests that are not mapped to other
servlets with servlet mappings (defined either here or in your own
web.xml file).

1、处理静态资源

2、处理未映射到其它Servlet的请求。

(1)使用<mvc:default-servlet-handler/>

声明了<mvc:default-servlet-handler/>后,SpringMVC框架会在容器中创建DefaultServletHttpRequestHandler处理器对象。它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理。一般的服务器都有默认的Servlet。

(2)、使用<mvc:resources/>

在Spring3.0版本后,Spring定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandler。并且添加了<mvc:resources/>标签,专门用于解决静态资源无法访问问题。需要在SpringMVC配置文件中添加如下形式的配置:

  <!--      mvc:resources 加入后框架会创建ResourceHttpRequestHandler这个处理器对象
        让这个对象处理静态资源的访问,不依赖tomcat服务器
        mapping:访问静态资源的uri地址,使用通配符**
        location:静态资源在你的项目中的目录位置
    -->

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

(3)、声明注解驱动

解决动态资源和静态资源冲突的问题,在SpringMVC配置文件加入:

 <!--mvc:resources和@RequestMapping有一定的冲突-->
    <mvc:annotation-driven />

2.5、绝对路径和相对路径

在jsp,html中使用的地址,都是在前端页面中的地址,都是相对地址

地址分类:

1、绝对地址,带有协议名称的是绝对地址,http://www.baidu.com、ftp://202.122.23.1

2、相对地址,没有协议开头的,例如user/some.do、/user/some.do。相对地址不能独立使用,必须有一个参考地址。通过参考地址+相对地址本身才能指定资源。

3、参考地址

1)在你的页面中的访问地址不加"/"

​ 访问的是:http://localhost:8080/path/index.jsp

​ 路径:http://localhost:8080/path/

​ 资源:index.jsp

在index.jsp发起user/some.do请求,访问地址变为http://localhost:8080/path/user/some.do,当你的地址没有斜杠开头,例如user/some.do,当你点击链接时,访问地址是当前页面的地址加上链接的地址。

http://localhost:8080/path/ + user/some.do

index.jsp访问user/some.do,返回后现在的地址:http://localhost:8080/path/user/some.do

​ 路径:http://localhost:8080/path/user

​ 资源:some.do

在index.jsp再次访问user/some.do,就变为http://localhost:8080/path/user/user/some.do(❌)

解决方案:1.加入${pageContext.request.contextPath}

​ 2.加入一个base标签,是html语言中的标签。表示当前页面中访问地址的基地址。你的页面中所有没有"/"开头的地址,都是以base标签中的地址为参考地址。使用base中的地址 + user/some.do组成访问地址

2)在你的页面中的访问地址加"/"

​ 访问的是:http://localhost:8080/path/index.jsp

​ 路径:http://localhost:8080/path/

​ 资源:index.jsp

点击/user/some.do,访问地址变为http://localhost:8080/user/some.do,参考地址是你的服务器地址,也就是http://localhost:8080。如果你的资源不能访问:加入KaTeX parse error: Undefined control sequence: \< at position 37: …contextPath} \̲<̲a href="{pageContext.request.contextPath}/user/some.do">发起user/some.do的get请求</a>

三、SSM整合开发

SSM编程,即SpringMVC+Spring+MyBatis整合。其实SSM整合的实质,仅仅就是将MyBatis整合入Spring。因为SpringMVC原本就是Spring的一部分,不用专门整合。

SSM整合开发
SSM: SpringMVC + Spring + Mybatis

SpringMVC:视图层,界面层,负责接收请求,显示处理结果的
Spring: 业务层,管理service,dao,工具类对象的
Mybatis: 持久层,访问数据库的

用户发起请求--SpringMVC接收---Spring中的service对象--Mybatis处理数据

ssm整合也叫做SSI(Ibatis也就是Mybatis的前身),整合中有容器。
1、第一个容器SpringMVC容器,管理Controller控制器对象的
2、第二个容器Spring容器,管理Service、Dao,工具类对象的
我们要做的把使用的对象交给合适的容器创建,管理。把Controller还有Web开发的相关对象交给SpringMVC容器,这些web用的对象写在SpringMVC配置文件中

Service,Dao对象定义在spring的配置文件中,让Spring管理这些对象。

SpringMVC容器和Spring容器是有关系的,关系已经确定好了
SpringMVC容器是Spring容器的子容器,类似Java中的继承.子是可以访问父的内容
在子容器中的Controller可以访问父容器中的Service对象,就可以实现Controller使用Service对象
实现步骤:
使用ssm数据库的mysql库,表使用student(id auto_increment,name,age)
1、新建maven web项目
2、加入依赖
    springmvc、spring、mybatis三个框架的依赖、druid连接池、mysql驱动、jackson依赖
    jsp、servlet依赖
3、写web.xml
    1)注册DispatcherServlet,目的:1、创建Springmvc容器对象,才能创建Controller类对象
                                 2、创建的是Servlet,才能接收用户的请求
    2)注册spring的监听器:contextLoaderListener,目的:创建Spring的容器对象,才能创建service。dao等对象
    3)注册字符集过滤器,解决post请求乱码的问题
4、创建包:controller包、service、dao、实体类包名创建好
5、写SpringMVC、Spring、Mybatis的配置文件
    1)SpringMVC配置文件
    2)Spring配置文件
    3)Mybatis主配置文件
    4)数据库的属性配置文件
6、写代码:dao接口和mapper文件,service和实现类 ,controller,实体类。
7、写jsp页面

3.1、搭建SSM开发环境

在maven的pom.xml中添加相关依赖。

配置web.xml:

<?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>myweb</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:conf/DispatcherServlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>myweb</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

    <!--注册spring的监听器-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:conf/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--注册字符集过滤器-->
    <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>

3.2、SSM整合注解开发

需求:完成学生注册和信息浏览

1、建表Student

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

2、定义包,组织程序的结构

3、编写配置文件

JDBC属性配置文件jdbc.properties

jdbc.url=jdbc:mysql://localhost:3306/数据库名
jdbc.username=用户名
jdbc.password=数据库密码

Spring配置文件 applicationContext.xml


    <context:property-placeholder location="classpath:conf/jdbc.properties" />
    <!--声明数据源,连接数据库-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <!--SqlSessionFactoryBean创建SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:conf/mybatis.xml" />
    </bean>
    <!--声明mybatis的扫描器,创建dao对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
        <property name="basePackage" value="com.dao" />
    </bean>

    <!--声明service的注解,@Service所在的包名位置-->
    <context:component-scan base-package="com.service" />

    <!--事务的配置:注解的配置,aspectj的配置-->

SpringMVC配置文件:DispatcherServlet.xml

   <!--声明组件扫描器-->
    <context:component-scan base-package="com.controller" />
    <!--声明springmvc框架中的视图解析器,帮助开发人员设置文件的路径-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <mvc:annotation-driven />

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>
<!--    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>-->
    <!--设置别名-->
    <typeAliases>
        <!--name:实体类所在的包名。这个包下所有类的类名就是别名-->
        <package name="com.domain"/>
    </typeAliases>
    <!--sql映射文件的位置-->
    <mappers>
        <!--
          name是包名,这个包中的所有mapper.xml一次都能加载
          使用package的要求:
            1、mapper文件名称和dao接口名必须完全一样,包括大小写
            2、mapper文件和dao接口必须在同一目录
        -->
        <package name="com.dao"/>
    </mappers>
</configuration>

4、实体类Student

public class Student {
    private Integer id;
    private String name;
    private Integer age;
    //set,get方法
}

5、Dao接口和SQL映射文件

public interface StudentDao {

    int insertStudent(Student student);
    List<Student> selectStudents();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.StudentDao">
    <select id="selectStudents" resultType="com.domain.Student">
        select id,name,age from student order by id desc 
    </select>
    
    <insert id="insertStudent">
        insert into student(name,age) values(#{name},#{age})
    </insert>
</mapper>

6、Service接口和实现类

public interface StudentService {

    int addStudent(Student student);
    List<Student> findStudents();
}
@Service
public class StudentServiceImpl implements StudentService {
    //引用类型的自动注入@Autowired,@Resource
    @Resource
    private StudentDao studentDao;
    @Override
    public int addStudent(Student student) {
        int nums = studentDao.insertStudent(student);
        return nums;
    }

    @Override
    public List<Student> findStudents() {
        return studentDao.selectStudents();
    }
}

7、处理器定义

@Controller
@RequestMapping("/student")
public class StudentController {
    @Resource
    private StudentService studentService;

    //注册学生
    @RequestMapping("/addStudent.do")
    public ModelAndView addStudent(Student student) {
        ModelAndView mv = new ModelAndView();
        String tips = "注册失败";
        //调用service处理student
        int nums = studentService.addStudent(student);
        if(nums > 0) {
            //注册成功
            tips = "学生 [" + student.getName() + "] 注册成功";
        }
        //添加数据
        mv.addObject("tips",tips);
        //指定结果页面
        mv.setViewName("result");
        return mv;
    }
    //处理查询,响应Ajax
    @RequestMapping("/queryStudent.do")
    @ResponseBody
    public List<Student> queryStudent() {
        //参数检查,简单的数据处理
        List<Student> students = studentService.findStudents();
        return students;
    }
}

8、定义视图-首页文件—index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String basePath = request.getScheme() + "://" +
            request.getServerName() + ":" + request.getServerPort() +
            request.getContextPath() + "/";
%>
<html>
<head>
    <title>功能入口</title>
    <base href="<%=basePath%>" />
</head>
<body>
    <div align="center">
    <p>SSM整合的例子</p>
    <img src="Images/3.jpg" width="400" height="400" />
    <table>
        <tr>
            <td><a href="addStudent.jsp">注册学生</a></td>
        </tr>
        <tr>
            <td><a href="listStudent.jsp">浏览学生</a></td>
        </tr>
    </table>
    </div>
</body>
</html>

9、注册学生页面–addStudent.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String basePath = request.getScheme() + "://" +
            request.getServerName() + ":" + request.getServerPort() +
            request.getContextPath() + "/";
%>
<html>
<head>
    <title>注册学生</title>
    <base href="<%=basePath%>" />
</head>
<body>
    <div align="center">
        <form action="student/addStudent.do" method="post">
            <table>
                <tr>
                    <td>姓名:</td>
                    <td><input type="text" name="name"></td>
                </tr>
                <tr>
                    <td>年龄:</td>
                    <td><input type="text" name="age"></td>
                </tr>
                <tr>
                    <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
                    <td><input type="submit" value="注册"></td>
                </tr>
            </table>
        </form>
    </div>

</body>
</html>

10、浏览学生页面—listStudent.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String basePath = request.getScheme() + "://" +
            request.getServerName() + ":" + request.getServerPort() +
            request.getContextPath() + "/";
%>
<html>
<head>
    <title>查询学生Ajax</title>
    <base href="<%=basePath%>" />
    <script type="text/javascript" src="lib/jquery-3.6.0.js"></script>
    <script type="text/javascript">
        $(function () {
            //在当前页面DOM对象加载后,执行loadStudentData()
            loadStudentData();
            $("#btnLoader").click(function () {
                loadStudentData();
            })
        })
        function loadStudentData() {
            $.ajax({
                url:"student/queryStudent.do",
                type:"get",
                dataType:"json",
                success:function (data) {
                    //alert("data=" + data);
                    //清除旧的数据
                    $("#info").html("");
                    //增加新的数据
                    $.each(data,function (i,n) {
                        $("#info").append("<tr>")
                            .append("<td>"+n.id+"</td>")
                            .append("<td>"+n.name+"</td>")
                            .append("<td>"+n.age+"</td>")
                            .append("</tr>")
                    })
                }
            })
        }
    </script>
</head>
<body>
    <div align="center">
        <table>
            <thead>
            <tr>
                <td>学号:</td>
                <td>姓名:</td>
                <td>年龄:</td>
            </tr>
            </thead>
            <tbody id="info">

            </tbody>
        </table>
        <input type="button" id="btnLoader" value="查询数据">
    </div>
</body>
</html>

11、再添加两个jsp页面–注册成功页面和注册失败页面。

四、SpringMVC核心技术

4.1 请求转发和重定向

​ 当处理器对请求处理完毕后,向其他资源进行跳转时,有两种跳转方式:请求转发与请求重定向。而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其他处理器。

​ 注意:对于请求转发的页面,可以是WEB-INF中的页面;而重定向的页面,是不能为WEB-INF中页面的。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问WEB-INF中资源的

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

​ SpringMVC框架把原来Servlet中的请求转发和重定向操作进行了封装。现在可以使用简单的方式实现转发和重定向。

​ forward:表示转发,实现request.getRequestDispatcher(“xx.jsp”).forward(req,resp)

​ redirect:表示重定向,实现response.sendRedirect(“xxx.jsp”)

4.1.1 请求转发

​ 处理器方法返回ModelAndView时,需在setViewName()指定的视图前添加forward:,且此时的视图不再与视图解析器一同工作,这样可以在配置了解析器时指定不同位置的视图。视图页面必须写出相对于项目根的路径。forward操作不需要视图解析器。

    /**
     * 处理器方法返回ModelAndView,实现转发forward
     * 语法:setViewName("forward:视图文件完整路径")
     * forward特点:不和视图解析器一同使用,就当项目中没有视图解析器
     * @return
     */
    @RequestMapping(value = "doForward.do")
    public ModelAndView doSome() {
        //处理some.do的请求了,相当于service调用处理完成了
        ModelAndView mv = new ModelAndView();
        //添加数据,框架在请求的最后把数据放入到request作用域
        mv.addObject("msg","欢迎使用springmvc做web开发");
        mv.addObject("fun","执行的是doSome方法");
        //mv.setViewName("forward:/WEB-INF/view/show.jsp");
        mv.setViewName("forward:/hello.jsp");
        return mv;
    }
4.1.2、请求重定向

​ 在setViewName()指定的视图前添加redirect:,可实现重定向跳转。

/**
     * 处理器方法返回ModelAndView,实现重定向redirect
     * 语法:setViewName("redirect:视图文件完整路径")
     * redirect特点:不和视图解析器一同使用,就当项目中没有视图解析器
     * 框架对重定向的操作:
     * 1、框架会把Model中的简单类型的数据,转为String使用,作为hello.jsp的get请求参数使用
     *   目的是在 doRedirect.do和hello.jsp 两次请求之间传递数据
     * 2、在目标hello.jsp页面可以使用参数集合对象 ${param}获取请求参数值
     *    ${param.name}
     * 3、重定向不能访问/WEB-INF目录下的资源
     */
    @RequestMapping(value = "doRedirect.do")
    public ModelAndView doWithRedirect(String name, Integer age) {
        //处理some.do的请求了,相当于service调用处理完成了
        ModelAndView mv = new ModelAndView();
        //添加数据,框架在请求的最后把数据放入到request作用域
        mv.addObject("name",name);
        mv.addObject("age",age);
        //重定向
        mv.setViewName("redirect:/hello.jsp");
        //访问结果是404
        //因为重定向不能访问/WEB-INF目录下的资源
        //mv.setViewName("redirect:/WEB-INF/view/show,jsp");
        return mv;
    }

4.2、异常处理

​ SpringMVC框架处理异常的常用方式:使用@ExceptionHandler注解处理异常。

​ SpringMVC框架采用的是统一,全局的异常处理。

把Controller中的所有异常处理都集中到一个地方。采用的是AOP的思想。把业务逻辑和异常处理的代码分开,解耦合。

@ExceptionHandler注解

​ 使用注解@ExceptionHandler,可以将一个方法指定为异常处理方法。该注解只有一个可选属性value,为一个Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。而被注解的方法,其返回值可以是ModelAndView、String或void,方法名随意,方法参数可以是Exception及其子类对象、HttpServletRequest、HttpServletResponse等。系统会自动为这些方法参数赋值。

(1)自定义异常类

​ 定义三个异常类:NameException、AgeException、MyUserException。其中MyUserException是另外两个异常的父类。

//表示当用户的姓名有异常,抛出NameException
public class NameException extends MyUserException{
    public NameException() {
        super();
    }
    public NameException(String message) {
        super(message);
    }
}
//表示当用户的年龄有问题,抛出的异常
 public class AgeException extends MyUserException {
    public AgeException() {
        super();
    }
    public AgeException(String message) {
        super(message);
    }
}
public class MyUserException extends Exception{
    public MyUserException() {
        super();
    }
    public MyUserException(String message) {
        super(message);
    }
}

(2)修改Controller抛出异常

@Controller
public class MyController {
    @RequestMapping(value = "some.do")
    public ModelAndView doSome(String name, Integer age) throws MyUserException {
        //处理some.do的请求了,相当于service调用处理完成了
        ModelAndView mv = new ModelAndView();
        //根据请求参数抛异常
        if(!"zs".equals(name)) {
            throw new NameException("姓名不正确!!!");
        }

        if(age == null || age > 80) {
            throw new AgeException("年龄比较大!!!");
        }
        mv.addObject("name",name);
        mv.addObject("age",age);
        mv.setViewName("show");
        return mv;
    }
}

(3)定义异常响应页面

<body>
    ageError.jsp <br/>
    提示信息: ${msg} <br/>
    系统异常消息: ${e.message}
</body>

<body>
    nameError.jsp <br/>
    提示信息: ${msg} <br/>
    系统异常消息: ${e.message}
</body>

<body>
    defaultError.jsp <br/>
    提示信息: ${msg} <br/>
    系统异常消息: ${e.message}
</body>

(4)定义全局异常处理类

/**
 * @ControllerAdvice : 控制器增强(也就是说给我们的控制器类增加功能--异常处理功能)
 *                     位置:在类的上面
 *  特点:必须让框架指定这个注解所在的包名,需要在SpringMVC配置文件中声明组件扫描器
 *  指定@ControllerAdvice所在的包名
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    //定义方法,处理发生的异常
    /*
        处理异常的方法和控制器方法的定义一样,可以有多个参数,可以有ModelAndView,
        String,void,对象类型的返回值

        形参:Exception,表示Controller中抛出的异常对象
        通过形参可以获取发生的异常信息

        @ExceptionHandler(异常的class):表示异常的类型,当发生此类异常时,由当前方法处理
     */
    @ExceptionHandler(value = NameException.class)
    public ModelAndView doNameException(Exception e) {
        //处理NameException的异常。
        /*
            异常发生处理的逻辑:
            1、需要把异常记录下来,记录到数据库,日志文件。
                记录日志发生的时间,哪个方法发生的,异常错误内容。
            2、发送通知,把异常的信息通过邮件,短信、微信发送给相关人员
            3、给用户友好的提示
         */
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","姓名必须是zs,其他用户不能访问");
        mv.addObject("e",e);
        mv.setViewName("nameError");
        return mv;
    }

    @ExceptionHandler(value = AgeException.class)
    public ModelAndView doAgeException(Exception e) {
        //处理AgeException的异常。
        /*
            异常发生处理的逻辑:
            1、需要把异常记录下来,记录到数据库,日志文件。
                记录日志发生的时间,哪个方法发生的,异常错误内容。
            2、发送通知,把异常的信息通过邮件,短信、微信发送给相关人员
            3、给用户友好的提示
         */
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","你的年龄不能大于80");
        mv.addObject("e",e);
        mv.setViewName("ageError");
        return mv;
    }

    //处理其他异常,NameException、AgeException以外,不知类型的异常
    @ExceptionHandler
    public ModelAndView doOtherException(Exception e) {
        //处理其他异常
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","你的年龄不能大于80");
        mv.addObject("e",e);
        mv.setViewName("defaultError");
        return mv;
    }
}

(5)定义Spring配置文件

<!--声明组件扫描器-->
    <context:component-scan base-package="com.controller" />

    <!--声明springmvc框架中的视图解析器,帮助开发人员设置文件的路径-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
        <!--前缀:视图文件的路径-->
        <property name="prefix" value="/WEB-INF/view/" />
        <!--后缀,视图文件的扩展名-->
        <property name="suffix" value=".jsp" />
    </bean>

    <!--处理异常需要的两步-->
    <context:component-scan base-package="com.handler" />
    <mvc:annotation-driven />

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

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

4.3、拦截器

​ SpringMVC中的Interceptor拦截器是非常重要和相当有用的,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。

​ 拦截器是==全局的,可以对多个Controller做拦截。==一个项目中可以有0个或多个拦截器,他们在一起拦截用户的请求。拦截器常用在:用户登录处理、权限检查、记录日志。

​ 拦截器的使用步骤:

​ 1、定义类实现HandlerInterceptor接口

​ 2、在SpringMVC的配置文件中,声明拦截器,让框架知道拦截器的存在。

​ 拦截器的执行时间:

​ 1、在处理请求之前,也就是Controller类中的方法执行之前先被拦截

​ 2、在控制器方法执行之后也会执行拦截器。

​ 3、在请求处理完成后也会执行拦截器。

拦截器:看作是多个Controller中公用的功能,集中到拦截器统一处理。使用的是AOP的思想

(1)一个拦截器的执行

自定义拦截器

//拦截器类:拦截用户的请求
public class MyInterceptor implements HandlerInterceptor {
    private long btime;
    /*
       preHandle叫做预处理方法。
        这个方法很重要,是整个项目的入口。请求得从这进
        当preHandler返回true,请求可以被处理。当它返回false,请求到此方法为止
       参数:
        Object handler:被拦截的控制器对象
       返回值boolean
         true
         false

       特点:
        1、方法在控制器方法(MyController的doSome)之前先执行的
           用户的请求首先到达此方法
        2、在这个方法中可以获取请求的信息,验证请求是否符合要求
           可以验证用户是否登录,验证用户是否有权限访问某个链接地址(url)
           如果验证失败,可以截断请求,请求不能被处理
           如果验证成功,可以放行请求,此时控制器方法才能执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        btime = System.currentTimeMillis();
        System.out.println("拦截器的MyInterceptor的preHandler()");
        //计算的业务逻辑,根据计算结果,返回true或者是false
        //给浏览器一个返回结果
        //request.getRequestDispatcher("/tips.jsp").forward(request,response);
        return true;

    }

    /*
       postHandler:后处理方法。
       参数:
        Object handler:被拦截的处理器对象MyController
        ModelAndView modelAndView:处理器方法的返回值
       特点:
        1、在处理器方法之后执行的
        2、能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的
        数据和视图,可以影响到最后的执行结果。
        3、主要是对原来的执行结果做二次修正
     */
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器的MyInterceptor的postHandler()");
        //对原来的doSome执行结果,需要调整
        if(modelAndView != null) {
            //修改数据
            modelAndView.addObject("mydate",new Date());
            //修改视图
            modelAndView.setViewName("other");
        }
    }

    /*
       afterCompletion:最后执行的方法
       参数
        Object handler:被拦截的处理器对象
        Exception ex:程序中发生的异常
       特点:
        1、在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成
        2、一般是做资源回收工作的。程序请求过程中创建了一些对象,在这里可以删除,吧占用的内存回收。

     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        System.out.println("拦截器的MyInterceptor的afterCompletion()");
        long etime = System.currentTimeMillis();
        System.out.println("计算从preHandler到请求处理完成的时间: "+(etime-btime));
    }
}

在SpringMVC的配置文件中注册拦截器

    <!--声明拦截器:拦截器可以有0或多个-->
    <mvc:interceptors>
        <!--声明第一个拦截器-->
        <mvc:interceptor>
            <!--指定拦截的请求的uri地址
                path:就是uri地址,可以使用通配符 **
                ** : 表示任意的字符,文件或者多级目录和目录中的文件
            -->
            <mvc:mapping path="/**"/>
            <!--声明拦截器对象-->
            <bean class="com.handler.MyInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

修改处理器Controller:

@Controller
public class MyController {
    @RequestMapping(value = "/some.do")
    public ModelAndView doSome(String name, Integer age) {
        System.out.println("===执行MyController中的doSome方法===");
        //处理some.do的请求了,相当于service调用处理完成了
        ModelAndView mv = new ModelAndView();
        mv.addObject("name",name);
        mv.addObject("age",age);
        mv.setViewName("show");
        return mv;
    }
}

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

(2)多个拦截器的执行

再定义一个拦截器。在SpringMVC配置文件中注册

 <!--声明拦截器:拦截器可以有0或多个
        在框架中保存多个拦截器是ArrayList,
        按照声明的先后顺序放入到ArrayList
    -->
    <mvc:interceptors>
        <!--声明第一个拦截器-->
        <mvc:interceptor>
            <!--指定拦截的请求的uri地址
                path:就是uri地址,可以使用通配符 **
                ** : 表示任意的字符,文件或者多级目录和目录中的文件
            -->
            <mvc:mapping path="/**"/>
            <!--声明拦截器对象-->
            <bean class="com.handler.MyInterceptor" />
        </mvc:interceptor>
        <!--声明第二个拦截器-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.handler.MyInterceptor2" />
        </mvc:interceptor>
    </mvc:interceptors>

控制台执行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wU1i9KXA-1643549751890)(C:\Users\20127\AppData\Roaming\Typora\typora-user-images\image-20220130125239863.png)]

​ 当有多个拦截器时,形成拦截器链。拦截器链的执行顺序,与其注册顺序一致。需要再次强调一点的是:当某一个拦截器的preHandler()方法返回true并被执行时,会向一个专门的方法栈中放入该拦截器的afterCompletion()方法。

​ 多个拦截器中方法与处理器方法的执行顺序如下图:

​ 从图中可以看出,只要有一个preHandler()方法返回false,则上部的执行链将被断开,其后续的处理器方法与postHandler()方法将无法执行。但无论执行链执行情况怎样,只要方法栈中有方法,即执行链中只要有preHandler()方法返回true,就会执行方法栈中的afterCompletion()方法。最终都会给出响应。

(3)拦截器和过滤器的区别

1.过滤器是servlet中的对象, 拦截器是框架中的对象
2.过滤器实现Filter接口的对象, 拦截器是实现HandlerInterceptor
3.过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的。拦截器是用来验证请求的,能截断请求。
4.过滤器是在拦截器之前先执行的。
5.过滤器是tomcat服务器创建的对象, 拦截器是springmvc容器中创建的对象
6.过滤器是一个执行时间点。 拦截器有三个执行时间点
7.过滤器可以处理jsp,js,html等等;拦截器是侧重拦截对Controller的对象。 如果你的请求不能被DispatcherServlet接收, 这个请求不会执行拦截器内容
8.拦截器拦截普通类方法执行,过滤器过滤servlet请求响应

4.4、SpringMVC中文件的上传与下载

文件上传是项目开发中最常见的功能之一 ,SpringMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。

前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;

maven中加入commons依赖:

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

前端表单:

    <form action="some.do" enctype="multipart/form-data" method="post">
        <input type="file" name="file"> <br/>
        <input type="submit" value="upload">
    </form>

SpringMVC配置文件需要加入文件上传配置:

    <!--文件上传配置-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--请求的编码格式,必须和jsp的pageEncoding属性一致,以便正确读取表单内容,默认为ISO-8859-1-->
        <property name="defaultEncoding" value="utf-8" />
        <!--上传文件大小上限,单位为字节(10485760=10M)-->
        <property name="maxUploadSize" value="10485760" />
        <property name="maxInMemorySize" value="40960" />
    </bean>

Controller控制器:

 //上传文件
    @RequestMapping("/some.do")
    public String fileUpload(@RequestParam("file")CommonsMultipartFile file,
                             HttpServletRequest request) throws IOException {
        //获取文件名
        String uploadFileName = file.getOriginalFilename();
        //如果文件名为空,直接回到首页
        if("".equals(uploadFileName)) {
            return "redirect:/index.jsp";
        }
        System.out.println("上传文件名: "+uploadFileName);
        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        //如果路径不在,创建一个
        File realPath = new File(path);
        if(!realPath.exists()) {
            realPath.mkdir();
        }
        System.out.println("上传文件保存地址: " + realPath);
        InputStream is = file.getInputStream(); //文件输入流
        OutputStream os = new FileOutputStream(new File(realPath, uploadFileName));//文件输出流
        //读取写出
        int len = 0;
        byte[] buffer = new byte[1024];
        while((len= is.read(buffer)) != -1) {
            os.write(buffer,0,len);
            os.flush();
        }
        os.close();
        is.close();
        return "redirect:/index.jsp";
    }

文件下载步骤:

1、设置 response 响应头

2、读取文件 – InputStream

3、写出文件 – OutputStream

4、执行操作

5、关闭流 (先开后关)

前端页面:

<a href="down.do">点击下载</a>

Controller代码:

//下载文件
    @RequestMapping(value = "/down.do")
    public String downLoads(HttpServletResponse response,HttpServletRequest request) throws IOException {
        //要下载图片的地址
        String path = request.getServletContext().getRealPath("/img");
        String fileName = "1.jpg";
        //1、设置response响应头
        response.reset(); //设置页面不缓存,清空buffer
        response.setCharacterEncoding("UTF-8"); //字符编码
        response.setContentType("multipart/form-data"); //二进制传输数据
        //设置响应头
        response.setHeader("Content-Disposition","attachment;fileName="+
                URLEncoder.encode(fileName,"utf-8"));
        File file = new File(path, fileName);
        //2、读取文件--输入流
        InputStream input = new FileInputStream(file);
        //3、写出文件--输出流
        OutputStream out = response.getOutputStream();
        byte[] buff = new byte[1024];
        int index = 0;
        //执行写出操作
        while((index = input.read(buff)) != -1) {
            out.write(buff,0,index);
            out.flush();
        }
        out.close();
        input.close();
        return "ok";
    }
  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值