SpringMVC——看这一篇就够了

SpringMVC

1. SpringMVC介绍

Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建Web 应用程序的全功能 MVC 模块。 使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还是Struts 这样的 Web 框架。通过策略接口,Spring 框架是高度可配置的,而且包含多种视图技术,如JavaServerPages(JSP)技术、Velocity、Tiles、iText 和 POI。Spring MVC 框架并不知道使用的视图,所以不会强迫您只使用 JSP 技术。Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。

web请求过程

在这里插入图片描述

2. SpringMVC组件

SpringMVC中几个常用的核心组件如下:

  • DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。
  • HandlerMapping:通过扩展处理器映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
  • HandlerAdapter:通过扩展处理器适配器,支持更多类型的处理器,调用处理器传递参数等工作!
  • ViewResolver:通过扩展视图解析器,支持更多类型的视图解析,例如:jsp、freemarker、pdf、excel等。

2.1 SpringMVC执行过程

在这里插入图片描述

SpringMVC流程:

  1. 用户发送请求至前端控制器 DispatcherServlet。
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。HandlerMapping根据请求查找具体的Handler:HandlerExecution,HandlerExecution根据请求查找控制器,并将解析后的信息(如控制器映射)传递回DispatcherServlet。
  3. DispatcherServlet调用HandlerAdapter处理器适配器。HandlerAdapter经过适配调用具体的Controller处理器
  4. Controller执行完成后将具体的执行信息(如ModelAndView)返回给HandlerAdapter。
  5. HandlerAdapter 将 controller 执行结果 ModelAndView 返回给 DispatcherServlet。
  6. DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
  7. ViewReslover解析后返回具体View。
  8. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
  9. DispatcherServlet响应用户。
2.2 DispatcherServlet

DispatcherServlet主要用作职责调度工作,本身主要用于控制流程,主要职责如下:

  1. 文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
  2. 通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个HandlerInterceptor拦截器);
  3. 通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);
  4. 通过ViewResolver解析逻辑视图名到具体视图实现;
  5. 本地化解析;
  6. 渲染具体的视图等;
  7. 如果执行过程中遇到异常将交给HandlerExceptionResolver来解析。
    在这里插入图片描述

DispatcherServlet辅助类

spring中的DispatcherServlet使用一些特殊的bean来处理request请求和渲染合适的视图。

bean类型说明
Controller处理器/页面控制器,做的是MVC中的C的事情,控制逻辑转移到前端控制器,用于对请求进行处理
HandlerMapping请求到处理器的映射,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象;如BeanNameUrlHandlerMapping将URL与Bean名字映射,映射成功的Bean就是此处的处理器
HandlerAdapterHandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;如SimpleControllerHandlerAdapter将对实现了Controller接口的Bean进行适配,并且调用处理器的handleRequest方法进行功能处理
HandlerExceptionResolver
处理器异常解析器
处理器异常解析,可以将异常映射到相应的统一错误界面,从而显示用户友好的界面(而不是给用户看到具体的错误信息)
ViewResolver 视图解析器ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;如InternalResourceViewResolver将逻辑视图名映射为jsp视图
LocaleResolver &LocaleContextResolver
地区解析器和地区Context解析器
解析客户端中使用的地区和时区,用来提供不同的国际化的view视图。
ThemeResolver主题解析器,解析web应用中能够使用的主题,比如提供个性化的网页布局。
MultipartResolver多部件解析器,主要处理multi-part(多部件)request请求,例如:在HTML表格中处理文件上传。
FlashMapManagerFlashMap管理器储存并检索在「input」和「output」的FlashMap中可以在request请求间(通常是通过重定向)传递属性的FlashMap

3. SpingMVC框架搭建

1.新建一个maven的web项目,并建立好java和resources文件夹
在这里插入图片描述
在这里插入图片描述

2.在pom.xml文件中添加jar依赖

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

3.resources文件夹下新建一个spring.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:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 注解扫描-->
    <context:component-scan base-package="top.peng"/>
    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- jsp所在的位置,前缀--> <!--/ 表示在根目录webapp下 -->
        <property name="prefix" value="/" />	
        <!-- jsp文件的后缀名-->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

4.在web.xml文件中添加以下内容

<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:spring.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

在这里插入图片描述

//*的区别

< url-pattern > / </ url-pattern >不会匹配到*.jsp,即:*.jsp不会进入spring的DispatcherServlet类 。

< url-pattern > /* </ url-pattern > 会匹配*.jsp,会出现返回jsp视图时再次进入spring的DispatcherServlet 类,导致找不到对应的controller报404错。

可以配置/ ,此工程所有请求全部由springmvc解析,此种方式可以实现 RESTful方式,需要特殊处理对静态文件的解析不能由springmvc解析。

可以配置.do或.action,所有请求的url扩展名为.do或.action由springmvc解析,此种方法常用

不可以/*,如果配置/*,返回jsp也由springmvc解析,这是不对的。

url-pattern有5种配置模式

(1)/xxx:完全匹配/xxx的路径

(2)/xxx/*:匹配以/xxx开头的路径,请求中必须包含xxx。

(3)/*:匹配/下的所有路径,请求可以进入到action或controller,但是转发jsp时再次被拦截,不能访问jsp界面。

(4).xx:匹配以xx结尾的路径,所有请求必须以.xx结尾,但不会影响访问静态文件。

(5)/:默认模式,未被匹配的路径都将映射到servlet。jpg,js,css等静态文件也被拦截,不能访问。

5.创建控制器类
在这里插入图片描述

注解

@Controller 创建控制器对象

@RequestMapping("请求地址")

  • 加在类上: 给模块添加请求根路径

  • 加载方法上: 方法具体的请求路径

  • 设置method属性,即设置请求方式,取值如下。
    @RequestMapping(method=RequestMethod.GET,value="请求名")

在这里插入图片描述
各请求方式的含义可以看我之前这篇。

https://blog.csdn.net/qq_40932102/article/details/109816750

6.配置Tomcat

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

7.运行测试

前端代码如下:

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

4. SpringMVC数据传递

4.1 后台从前台接收参数

springMVC中接收参数的方式有以下几种

  1. 通过HttpServletRequestgetParameter()方法

  2. 页面传值时的key=处理请求的方法的参数名

    如请求为/login?username=abc&password=123那么处理该请求的方法可以定义为:

    @RequestMapping(value = "/login")
    public String login(String username,String password){
        //TODO:业务逻辑
    }
    

    方法的参数名与传参的name值不同

    如请求为/login?name=abc&password=123,页面传的参数的key分别为namepassword,方法的参数我们用的是usernamepassword,这时我们可以通过@RequestParam(value = "name")来绑定参数name。

    public String login(@RequestParam(value="name") String username,String password){}
    

    @RequestParam()还可以设置默认值:@RequestParam(defaultValue = "1")

    注意参数的类型问题,避免给定的参数不能由框架转换成目标格式而引起的错误。

  3. 使用控件名和对象的属性名一致的方式进行接收(常用)

    如请求为/login?username=abc&password=123,username和password都是user对象里的属性,可以通过下面的方式接收到参数

    @RequestMapping(value = "/login")
    public String login(User user){
        //TODO:业务逻辑
        String username = user.getUsername();	//得到参数username
        String password = user.getPassword();	//得到参数password
    }
    

springMVC中接收参数中有日期应该如何处理?

springmvc框架默认支持转换得日期格式为:yyyy/MM/dd

@RequestMapping(value = "/test")
public String test(Date birthday){
   System.out.println("Date:"+birthday);
   //返回结果页面的名称
   return "success";
}

如果前端传了yyyy-MM-dd格式的日期,就会报错。

解决日期问题方式:

  1. 使用string接收日期,接收后,再转换: SimpleDataFormate

  2. 使用工具类处理日期

    1. 添加日期处理工具包
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
        <version>2.9.9</version>
    </dependency>
    
    1. 在spring.xml文件中添加下面代码,注意配置mvc命名空间
    <mvc:annotation-driven/>
    
    1. 在对应的处理方法中关于日期的参数前用@DateTimeFormat(pattern = "前端传的日期的格式")
    @RequestMapping(value = "/test")
    public String test(@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday){
        System.out.println("Date:"+birthday);
        //返回结果页面的名称
        return "success";
    }
    

tips:参数类型使用引用数据类型,基本数据类型使用包装类

4.2 后台返回参数给前台

获得返回参数需要保证项目可以使用Jsp的EL表达式,修改web.xml 文件,删除2.3的版本,改为2.5,即在<web-app>标签内添加以下代码。

<!-- 删除这里 -->
<!-- <!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" > -->
<!-- 修改这里 -->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

springMVC中返回数据给前端的方式有以下几种

  1. 通过HttpServletRequestsetAttribute("key",value)方法,将后台数据存储到request域,前台用EL表达式展示数据。注意:由于数据存储在request域,页面重定向时会丢失数据。

  2. 使用ModelMapaddAttribute("key",value)方法存储后台数据 ,默认存储在request域。

  3. 使用ModeladdAttribute("key",value)方法存储后台数据

  4. 通过ModelAndView对象存数据,同时作为返回值类型,如下代码所示。

    @RequestMapping(value = "/test")
    public ModelAndView test(@DateTimeFormat(pattern = "yyyy-MM-dd")Date birthday){
        System.out.println("Date:"+birthday);
        ModelAndView modelAndView = new ModelAndView();
        //设置跳转页面
        modelAndView.setViewName("success");
        //存值
        modelAndView.addObject("mvKey",birthday);
        //返回页面和数据模型
        return modelAndView;
    }
    

总结:

Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;

ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;

ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

4.3 Session存值

SpringMVC中使用session的方式有以下几种:

  1. 使用HttpSession (request.getSession())的setAttribute("key",value)方法存值

  2. 在类上使用注解@sessionAttributes("key值") key是ModelMap中定义的key值

    注:该注解和ModelMap结合使用,当使用ModelMap存值时,会在session中同时存储一份数据@SessionAttributes()的小括号中如果是多个值,加{},清除注解(session)使用SessionStatussetComplete()方法。示例:

    @SessionAttributes("sessionMap")
    public class MyController {
        @RequestMapping(value = "/login")
        public String login(String username,String password,ModelMap map){
            map.addAttribute("sessionMap",username);
            //返回结果页面的名称
            return "success";
        }
        @RequestMapping(value = "/out")
        public String out(SessionStatus status){
            status.setComplete();	//清除注解session
            return "login";
        }
    }
    
    //有多个值存入session的写法
    @SessionAttributes({"key1","key2"})
    

4.4 Cookie、请求头的获取

@CookieValue注解可以获取请求中的cookie

@RequestMapping(value = "/test")
public String testCookie(@CookieValue("JSESSIONID")String cookie){
    System.out.println("cookie:"+cookie);
    return "result";
}

在这里插入图片描述

@RequestHeader注解可以获取请求头中的数据

@RequestMapping(value = "/test")
public String testHeader(@RequestHeader("User-Agent")String header){
    System.out.println("cookie:"+cookie);
    return "result";
}

请求头中包含如下信息,都可以通过@RequestHeader注解获取

在这里插入图片描述

4.5 弹窗响应(后台给前台返回一个弹窗)

假设一个场景:用户点击退出时,弹出一个退出成功的提示框,然后跳转页面。那这个弹窗如何实现呢?

@RequestMapping(value = "/out")
public void out(SessionStatus status, HttpServletResponse resp) throws IOException {
    status.setComplete();	//清除session
    resp.setContentType("text/html;charset=UTF-8"); //指定编码
    //响应流
    PrintWriter writer = resp.getWriter();
    writer.print("<script>alert('退出成功');location.href='login.jsp'</script>");//js代码
}

springMVC中处理post请求发送数据的乱码问题

在web.xml文件中加入springMVC定义的CharacterEncodingFilter过滤器。

<filter>
   <filter-name>charset</filter-name>
   <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
   <init-param>
     <param-name>encoding</param-name>
     <param-value>utf-8</param-value>
   </init-param>
 </filter>
 <filter-mapping>
   <filter-name>charset</filter-name>
   <url-pattern>/*</url-pattern>
 </filter-mapping>

4.6 结果跳转方式

该部分内容参考自https://mp.weixin.qq.com/s?__biz=Mzg2NTAzMTExNg==&mid=2247483998&idx=1&sn=97c417a2c1484d694c761a2ad27f217d&scene=19

4.6.1 ModelAndView方式

设置ModelAndView对象 ,根据view的名称 ,和视图解析器跳到指定的页面 。

页面 : {视图解析器前缀} + viewName +{视图解析器后缀}

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <!-- 前缀 -->
   <property name="prefix" value="/" />
   <!-- 后缀 -->
   <property name="suffix" value=".jsp" />
</bean>
@Controller
public class myController{
    @RequestMapping(value = "/test")
	public ModelAndView test() throws Exception {
       //返回一个模型视图对象
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","ControllerTest");//存值
       mv.setViewName("success");		//跳转到success.jsp
       return mv;
  }
}
4.6.2 ServletAPI方式

通过设置ServletAPI ,不需要视图解析器 。

  1. 通过HttpServletResponse进行输出

  2. 通过HttpServletResponse实现重定向

  3. 通过HttpServletResponse实现转发

@Controller
public class myController {
    @RequestMapping("/test1")
    public void test1(HttpServletRequest req, HttpServletResponse resp) throws IOException{
       rsp.getWriter().println("Hello,Spring BY servlet API");
    }

    @RequestMapping("/test2")
    public void test2(HttpServletRequest req, HttpServletResponse resp) throws IOException{
        rsp.sendRedirect("/success.jsp");	//重定向
    }

    @RequestMapping("/test3")
    public void test3(HttpServletRequest req, HttpServletResponse resp) throws Exception{
       req.getRequestDispatcher("/success.jsp").forward(req,resp);//转发
    }
}
4.6.3 SpringMVC转发和重定向

通过SpringMVC来实现转发和重定向 - 无需视图解析器;return的页面这里将路径写全,注意测试时将视图解析器注释掉。

@Controller
public class MyController {
   @RequestMapping("/test1")
   public String test1(){
       return "/index.jsp"; //转发
   }

   @RequestMapping("/test2")
   public String test2(){
       return "forward:/index.jsp";	//转发
   }

   @RequestMapping("/test3")
   public String test3(){
       return "redirect:/success.jsp"; //重定向
   }
}

通过SpringMVC来实现转发和重定向 - 有视图解析器;

重定向会忽略视图解析器 ,本质就是重新请求一个新地方,所以注意路径问题。

也可以重定向到另外一个请求实现 。

@Controller
public class MyController {
   @RequestMapping("/test1")
   public String test1(){
       return "success";		//默认转发,走视图解析器
   }
    
   @RequestMapping("/test2")
   public String test2(){
       return "redirect:/success.jsp";	//重定向,不走视图解析器,注意路径问题。
       //return "redirect:hello.do"; //hello.do为另一个请求/
   }
}

4.7 异常处理

springMVC中我们可以很方便的将错误都跳转到一个error页面,比如404页面

方法1:在web.xml响应状态码配置一个对应页面

<error-page>
    <error>404</error>
    <location>/404.html</location>
</error-page>

方法2:在Controller类中定义一个发生就跳转到异常结果界面的方法,该方法加@ExceptionHandler注解

@ExceptionHandler(Exception.class)
public String error(){
    return "error";			//跳转异常页面
}

@ControllerAdvice注解

在一个Contoller类上添加@ControllerAdvice注解可以使这个类成为全局异常处理类,类中用@ExceptionHandler方法注解的方法可以处理所有Controller发生的异常

4.8 SpringMVC访问静态资源

需要注意:DispatcherServlet拦截资源设置成了/ 避免了死循环, /不拦截jsp资源,但是它会拦截其他静态资源,例如 html,js,css,image等等, 那么我们在使用jsp内部添加静态资源就无法成功,所以我们需要单独处理下静态资源!

处理方案:

方式1: 在springmvc的配置文件中添加mvc命名空间下的<mvc:resources>标签!指定静态资源路径

  1. 修改Spring MVC对应配置文件,添加mvc命名空间和约束

    xmlns:mvc="http://www.springframework.org/schema/mvc"
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd
    

在这里插入图片描述

  1. 添加处理标签<mvc:resources>

    <mvc:annotation-driven /> <!--注解驱动-->
    <!--/**表示当前目录及其子目录 -->
    <mvc:resources mapping="/img/**" location="/images/" ></mvc:resources>
    <mvc:resources mapping="/css/**" location="/css/"/>
    <mvc:resources mapping="/html/**" location="/html"/>
    

方式2: 在springmvc的配置文件中添加mvc命名空间下的<mvc:default-servlet-handler>标签!统一配置

这种方式简单实用,比较常用

<mvc:default-servlet-handler></mvc:default-servlet-handler>

5. RestFul风格

5.1 概述

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

资源:互联网所有的事物都可以被抽象为资源

资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。

它们分别代表着四种基本操作:

  • GET用来获取资源
  • POST用来创建新资源
  • PUT用来更新资源
  • DELETE用来删除资源

传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get

​ http://127.0.0.1/item/queryItem.action?id=1 查询,GET

​ http://127.0.0.1/item/addItem.action 新增,POST

​ http://127.0.0.1/item/updateItem.action 更新,POST

​ http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST

使用RESTful操作资源 :可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能可以不同!

​ http://127.0.0.1/item/1 查询,HTTP GET :得到id = 1 的 item

​ http://127.0.0.1/item/ 新增,HTTP POST : 新增 item

​ http://127.0.0.1/item 更新,HTTP PUT : 更新item

​ http://127.0.0.1/item/1 删除,HTTP DELETE:删除 id=1 的item

5.2 Spring中实现RESTful风格

1.web.xml添加HiddenHttpMethodFilter过滤器。

浏览器form表单只支持GET和POST,不支持DELETE和PUT请求,Spring添加了一个过滤器,可以将这些请求转换为标准的http方法,支持GET、POST、DELETE、PUT等请求!

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

2.编写controller

@Controller
public class RestFulController {
    @RequestMapping(value = "/testRest",method = RequestMethod.GET)
    public String get(){
        System.out.println("get请求");
        return "get_success";
    }
    @RequestMapping(value = "/testRest",method = RequestMethod.POST)
    public String post(){
        System.out.println("post请求");
        return "post_success";
    }
    @RequestMapping(value = "/testRest",method = RequestMethod.PUT)
    public String put(){
        System.out.println("put请求");
        return "put_success";
    }
    @RequestMapping(value = "/testRest",method = RequestMethod.DELETE)
    public String delete(){
        System.out.println("delete请求");
        return "delete_success";
    }
}

jsp页面代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/testRest" method="get">
        <input type="submit" value="get"/>
    </form>
    <form action="/testRest" method="post">
        <input type="submit" value="get"/>
    </form>
    <!--由于doFilterInternal方法只对method为post的表单进行过滤,所以在页面中必须如下设置:代表post请求,但是HiddenHttpMethodFilter将把本次请求转化成标准的put请求方式! name="_method"固定写法!-->
    <form action="/testRest" method="post">
    <!--注意:这里name必须是"_method",不可修改,HiddenHttpMethodFilter中会根据这个name来转换请求-->
        <input type="hidden" name="_method" value="PUT">
        <input type="submit" value="put"/>
    </form>
    <form action="/testRest" method="post">
        <input type="hidden" name="_method" value="DELETE">
        <input type="submit" value="delete"/>
    </form>
</body>
</html>

这样我们就可以通过/testRest这一个请求完成不同的操作。

5.3 RESTful传值

该部分内容参考自https://mp.weixin.qq.com/s?__biz=Mzg2NTAzMTExNg==&mid=2247483993&idx=1&sn=abdd687e0f360107be0208946a7afc1d&scene=19#wechat_redirect

可以通过 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量上。

@Controller
public class RestFulController {
   //映射访问路径
   @RequestMapping("/commit/{p1}/{p2}")
   public String index(@PathVariable int p1, @PathVariable("p2") int par2, Model model){
       int result = p1+p2;
       //Spring MVC会自动实例化一个Model对象用于向视图中传值
       model.addAttribute("msg", "结果:"+result);
       //返回视图位置
       return "test";  
  }  
}

我们来测试请求查看下

图片

思考:使用路径变量的好处?

  1. 使路径变得更加简洁;

  2. 获得参数更加方便,框架会自动进行类型转换。

  3. 通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法,如这里访问是的路径是/commit/1/a,则路径与方法不匹配,而不会是参数转换失败。

图片 我们来修改下对应的参数类型,再次测试

//映射访问路
@RequestMapping("/commit/{p1}/{p2}")
public String index(@PathVariable int p1, @PathVariable String p2, Model model)
	String result = p1+p2;
    //Spring MVC会自动实例化一个Model对象用于向视图中传值
    model.addAttribute("msg", "结果:"+result);
    //返回视图位置
    return "test"           
}

图片

5.4 小结

Spring MVC的 @RequestMapping 注解能够处理HTTP请求的方法,比如 GET,PUT,POST,DELETE 以及 PATCH等。

**所有的地址栏请求默认都会是 HTTP GET 类型的。**看下面例子

//映射访问路径,必须是POST请求
@RequestMapping(value = "/hello",method = {RequestMethod.POST})
public String index2(Model model){
   model.addAttribute("msg", "hello!");
   return "test";
}

我们使用浏览器地址栏进行访问默认是Get请求,会报错405:

图片

如果将POST修改为GET则正常了;

//映射访问路径,必须是Get请求
@RequestMapping(value = "/hello",method = {RequestMethod.GET})
public String index2(Model model){
   model.addAttribute("msg", "hello!");
   return "test";
}

图片

方法级别的注解变体有如下几个:组合注解

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

@GetMapping 是一个组合注解,平时使用的会比较多。

它所扮演的是 @RequestMapping(method=RequestMethod.GET)的一个快捷方式。

6. SpringMVC中使用Json和Ajax

1.首先我们需要导json依赖包

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

2.创建一个User类

public class User {
    private String username;
    private int age;
    //constructor getter setter toString等
}

3.创建Controller

在需要转换JSON数据的方法上添加@ResponseBody注解,该注解会自动将我们返回的数据解析为Json格式

public class UserController {
    @RequestMapping(value = "/getUser")
    @ResponseBody
    public User getUser() {
        User user = new User("张三",20);
        return user;
    }
    @RequestMapping(value = "/getUsers")
    @ResponseBody
    public List<User> getUsers() {
        List<User> list = new ArrayList<>();
        list.add(new User("张三",20));
        list.add(new User("李四",20));
        list.add(new User("王五",20));
        return list;
    }
}

4.启动Tomcat,访问对应请求

  • http://localhost:8080/getUser

在这里插入图片描述

  • http://localhost:8080/getUsers

在这里插入图片描述

返回Json数据的另一种方式,使用ObjectMapper对象,具体用法如下:

注意:这种方法会出现乱码,可以在@RequestMapping中添加produces指定编码和返回类型。

每次都指定比较麻烦,统一处理乱码问题见下面链接。

参考自https://mp.weixin.qq.com/s?__biz=Mzg2NTAzMTExNg==&mid=2247484014&idx=1&sn=9bab2e9a658c32b1877c6015a3bd5f7d&scene=19

@Controller
public class UserController {
  @RequestMapping(value = "/getUser2",produces = "application/json;charset=utf-8")
  @ResponseBody
  public String getUser() throws JsonProcessingException {
      //创建一个jackson的对象映射器,用来解析数据
      ObjectMapper mapper = new ObjectMapper();
      //创建一个对象
      User user = new User("张三", 20);
      //将我们的对象解析成为json格式
      String str = mapper.writeValueAsString(user);
      //由于@ResponseBody注解,这里会将str转成json格式返回;十分方便
      return str;
 }
}

在类上直接使用 @RestController注解 ,这样,类里面所有的方法都只会返回 json字符串,不用再每一个方法都添加@ResponseBody。我们在前后端分离开发中,一般都使用 @RestController,十分便捷!

@RestController
public class UserController {
   @RequestMapping(value = "/getUser")
   public User getUser() {
      //code
   }

   @RequestMapping(value = "/getUsers")
   public List<User> getUsers() {
       //code
   }
}

5.写一个jsp页面getUsers.jsp,通过Ajax来展示后台得到的Json数据

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
	<input type="button" id="btn" value="获取数据"/>
    <table id="table" width="100px" border="1" style="border-collapse: collapse; ">
        <tr>
            <td>姓名</td>
            <td>年龄</td>
        </tr>
        <tbody id="content">
        </tbody>
    </table>
<!--引入jquery文件,注意配置静态资源的访问-->
<script src="${pageContext.request.contextPath}/js/jquery-3.5.1.min.js"></script>
<script>
    $(function () {
        $("#table").hide();
        $("#btn").click(function () {
            $("#table").show();
            $.post("/getUsers",function (data) {
                console.log(data)
                var html="";
                for (var i = 0; i <data.length ; i++) {
                    html+= "<tr>" +
                        "<td>" + data[i].username + "</td>" +
                        "<td>" + data[i].age + "</td>" +
                        "</tr>"
                }
                $("#content").html(html);
            },"JSON");
        })
    })
</script>
</body>
</html>

启动Tomcat,访问http://localhost:8080/getUsers.jsp
在这里插入图片描述

SpringMVC使用Ajax更多操作看下面链接

https://mp.weixin.qq.com/s?__biz=Mzg2NTAzMTExNg==&mid=2247484021&idx=1&sn=9c3e5538167483d1d6ba4c5daaea9ca1&scene=19

7. SpirngMVC拦截器

7.1 概述

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

过滤器与拦截器的区别:拦截器是AOP思想的具体应用。

过滤器

  • servlet容器规范中的一部分,任何java web工程都可以使用
  • 在实现上基于函数回调,可以对几乎所有请求进行过滤
  • 一个过滤器实例只能在容器初始化时调用一次
  • 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截

拦截器

  • 拦截器是依赖于web框架,是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
  • 在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。
  • 一个拦截器实例在一个controller生命周期之内可以多次调用。
  • 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的

7.2 自定义拦截器的使用

想要自定义拦截器,必须实现 HandlerInterceptor 接口,重写以下三个方法。

  • preHandle() 拦截器开始
  • postHandle() 拦截器结束
  • afterCompletion() 最后执行
public class MyInterceptor implements HandlerInterceptor {
   //在请求处理的方法之前执行
   //如果返回true执行下一个拦截器
   //如果返回false就不执行下一个拦截器
   public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
       System.out.println("------------处理前,拦截开始------------");
       return true;
   }
    
   //在请求处理方法执行之后执行
   public void postHandle(HttpServletRequest req,HttpServletResponse resp,Object handler, ModelAndView mv) throws Exception {
       System.out.println("------------处理后,拦截结束------------");
   }

   //在DispatcherServlet处理后执行,做清理工作。
   public void afterCompletion(HttpServletRequest req,HttpServletResponse resp,Object handler, Exception e) throws Exception {
       System.out.println("------------清理,无论如何都会执行------------");
   }
}

然后在springmvc的配置文件中配置拦截器

<!--关于拦截器的配置-->
<mvc:interceptors>
   <mvc:interceptor>
       <!--指定需要拦截的请求 /** 包括当前路径及其子路径 /*不包含子路径-->
       <mvc:mapping path="/请求名"/>
       <mvc:mapping path="/请求根路径名/**"/>
       <mvc:mapping path="/请求名根路径名/*"/>
       <!--bean配置的就是要执行拦截操作的拦截器类的全路径-->
       <bean class="top.peng.interceptor.MyInterceptor"/>
   </mvc:interceptor>
</mvc:interceptors>

配置完成后就可以对请求进行拦截了

注:如果使用MyInterceptor类拦截所有请求,可以简单配置为:

<mvc:interceptors>
   <!--bean配置的就是要执行拦截操作的拦截器类的全路径-->
   <bean class="top.peng.interceptor.MyInterceptor"/>
</mvc:interceptors>

比如我们去访问上面的getUsers请求。

我们在UserController类的getUsers()方法中加一个输出语句

public List<User> getUsers() {
    System.out.println("执行/getUsers请求...");
    List<User> list = new ArrayList<>();
    list.add(new User("张三",20));
    list.add(new User("李四",20));
    list.add(new User("王五",20));
    return list;
}

看一下执行结果:

在这里插入图片描述

多个拦截器的执行顺序效果

配置如下,拦截器1在上

<mvc:interceptors>
   <!--bean配置的就是要执行拦截操作的拦截器类的全路径-->
   <bean class="top.peng.interceptor.MyInterceptor1"/>
   <bean class="top.peng.interceptor.MyInterceptor2"/>	<!--拦截器栈-->
</mvc:interceptors>

在这里插入图片描述

符合栈的特性,先进后出,先开始的后结束,所以也叫拦截器栈。

多个过滤器与拦截器的代码执行顺序

  • 过滤器的运行是依赖于servlet容器的,跟springmvc等框架并没有关系。并且,多个过滤器的执行顺序跟web.xml文件中定义的先后关系有关
  • 对于多个拦截器它们之间的执行顺序跟在SpringMVC的配置文件中定义的先后顺序有关

在这里插入图片描述

拦截器的使用场景

  1. 日志记录:记录请求信息的日志
  2. 权限检查:如登录检查
  3. 性能检测:检测方法的执行时间

拦截器的使用案例可以参考https://mp.weixin.qq.com/s?__biz=Mzg2NTAzMTExNg==&mid=2247484026&idx=1&sn=eba24b51963e8c3293d023cbcf3318dc&scene=19

8. SpringMVC文件上传下载

在2003年,Apache Software Foundation发布了开源的Commons FileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。而SpringMVC则提供了更简单的封装。

SpringMVC为文件上传提供了直接支持,这种支持是通过即插即用的MultipartResolver实现。

SpringMVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver。

在SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要先在上下文中配置MultipartResolver。

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

对表单中的 enctype 属性做个详细的说明:

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
<form action="" enctype="multipart/form-data" method="post">
   <input type="file" name="file"/>
   <input type="submit">
</form>

8.1文件上传和下载步骤

8.1.1. 添加JAR包
<!--文件上传-->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
<!--文件下载-->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>
8.2.2. 在SpringMVC的配置文件中配置MultipartResolver对象

注意:这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!

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

前端代码

<form action="/upload" method="post" enctype="multipart/form-data">
    选择文件:<input type="file" name="file">
    <input type="submit" value="上传">
</form>

编写Controller

@Controller
public class FileController {
    @RequestMapping(value = "/upload")
    public String upload(@RequestParam("file")MultipartFile file, HttpServletRequest req) {
        //上传路径设置,文件上传到项目根目录的upload文件夹内
        String path=req.getServletContext().getRealPath("/upload");
        //如果路径不存在,创建一个
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        System.out.println("上传文件保存地址:"+realPath);
        //上传并保存到指定路径
        if (!file.isEmpty())
        {
            try {
                //file.getOriginalFilename()获取上传的文件的原名
                file.transferTo(new File(realPath+"/"+file.getOriginalFilename()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //测试:将文件名存入request域,在成功页面展示。
        req.setAttribute("filename",file.getOriginalFilename());
        return "success";
    }
}

success界面

<h1>文件${filename}上传成功</h1>
<img src="upload/${filename}" width="288px" height="180px">

看一下效果:

在这里插入图片描述

8.2.3.2文件下载

在fileController中添加一个download方法

@RequestMapping(value = "/download")
public ResponseEntity<byte[]> download(String filename, HttpServletRequest req) throws IOException {
    //获取文件在服务器中的路径
    String  realPath = req.getServletContext().getRealPath("/upload");
    //要下载的文件地址
    String filePath = realPath+"/"+filename;
    //设置响应头信息
    HttpHeaders headers = new HttpHeaders();
    //以流的形式传输数据
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    //attachment表示将文件以附件的形式响应给客户端
    headers.setContentDispositionFormData("attachment",URLEncoder.encode(filename,"UTF-8"));
    File file = new File(filePath);
    //将文件进行返回
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
    return responseEntity;
}

我们在上面的文件上传的成功的界面(success.jsp)加一个下载链接

<h1>文件${filename}上传成功</h1>
<img src="upload/${filename}" width="288px" height="180px">
<!--添加下面代码-->
<a href="/download?filename=${filename}">下载</a>

看下效果:
在这里插入图片描述

CommonsMultipartFile 的 常用方法:

方法名称解释
byte [] getBytes()获取文件数据
String getContentType()获取文件MIMETYPE类型,如image/jpeg,text/plain等
InputStream getInputStream()获取文件输入流
String getName()获取表单中文件组件的名称 name值
String getOriginalFilename()获取上传的文件的原名
long getSize()获取文件的字节大小,单位为byte
boolean isEmpty()是否有上传的文件
void transferTo(File dest)将上传的文件保存到指定的文件路径中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AnswerCoder

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

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

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

打赏作者

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

抵扣说明:

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

余额充值