> 技术文档 > SpringMVC(看这一篇就够了)

SpringMVC(看这一篇就够了)


目录:

  • SpringMVC
    • 什么是MVC模型
    • SpringMVC
    • 案例
    • SpringMVC执行流程
    • SpringMVC封装参数
      • 简单数据类型
      • 简单对象
      • 关联对象
      • 简单数据类型集合
      • Map集合
      • 参数类型转换器
      • 编码过滤器
      • Servlet原生对象
    • SpringMVC处理响应
      • 视图解析器
      • 返回值为void
      • 返回值为ModelAndView
      • 向request域设置数据
      • 向session域设置数据
      • 向context域设置数据
      • 请求转发&重定向
    • SpringMVC注解
      • @Controlle r和 @RequestMapping
      • @RequestParam
      • @RequestHeader、@CookieValue
      • @SessionAttributes
      • @ModelAttribute
      • RESTful风格支持
      • @PathVariable
      • @PostMapping、@GetMapping、@PutMapping、@DeleteMapping
      • HiddenHttpMethodFilter
      • @ResponseBody
      • @RestController
      • 静态资源映射
      • @RequestBody
    • SpringMVC上传
      • 原生方式上传
      • SpringMVC方式上传
      • 上传多文件
      • 异步上传
      • 跨服务器上传
      • 查询可下载的文件
      • 下载文件
    • SpringMVC异常处理
      • 单个控制器异常处理
      • 全局异常处理
      • 自定义异常处理器
    • SpringMVC拦截器
      • 拦截器的使用
      • 全局拦截器
      • 拦截器链与执行顺序
      • 过滤敏感词
    • SpringMVC跨域请求
      • 同源策略
      • 跨域请求
      • 接收跨域请求

SpringMVC(看这一篇就够了)

SpringMVC

什么是MVC模型

MVC全称Model View Controller,是一种设计创建Web应用程序的模式。这三个单词分别代表Web应用程序的三个部分:

Model(模型):指数据模型。用于存储数据以及处理用户请求的业务逻辑。在Web应用中,JavaBean对象,业务模型等都属于Model。

View(视图):用于展示模型中的数据的,一般为jsp或html文件。

Controller(控制器):是应用程序中处理用户交互的部分。接受视图提出的请求,将数据交给模型处理,并将处理后的结果交给视图显示。
SpringMVC(看这一篇就够了)

SpringMVC

SpringMVC是一个基于MVC模式的轻量级Web框架,是Spring框架的一个模块,和Spring可以直接整合使用,我们使用的版本是Spring6,所以JDK需要17以上。SpringMVC代替了Servlet技术,它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。

案例

  1. 使用maven创建web项目,补齐包结构。

  2. 引入相关依赖

<dependencies>  <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.11</version> </dependency>  <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>6.0.11</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>6.0.11</version> </dependency>  <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.0.0</version> <scope>provided</scope> </dependency>  <dependency> <groupId>jakarta.servlet.jsp</groupId> <artifactId>jakarta.servlet.jsp-api</artifactId> <version>3.0.0</version> <scope>provided</scope> </dependency></dependencies>
  1. 在web.xml中配置前端控制器DispatcherServlet。
<web-app> <display-name>Archetype Created Web Application</display-name>  <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app>

4.编写SpringMVC核心配置文件springmvc.xml,该文件和Spring配置文件写法一样。

<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:mvc=\"http://www.springframework.org/schema/mvc\" xmlns:context=\"http://www.springframework.org/schema/context\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">  <context:component-scan base-package=\"com.jjy\"/>  <mvc:annotation-driven/></beans>

5.编写控制器

@Controllerpublic class MyController1 { // 该方法的访问路径是/c1/hello1 @RequestMapping(\"/c1/hello1\") public void helloMVC(){ System.out.println(\"hello SpringMVC!\"); }}
  1. 配置tomcat10启动项目,访问 http://localhost:8080/c1/hello1

SpringMVC执行流程

SpringMVC(看这一篇就够了)
SpringMVC的组件

  • DispatcherServlet:前端控制器,接受所有请求,调用其他组件。
  • HandlerMapping:处理器映射器,根据配置找到方法的执行链。
  • HandlerAdapter:处理器适配器,根据方法类型找到对应的处理器。
  • ViewResolver:视图解析器,找到指定视图。

组件的工作流程

  1. 客户端将请求发送给前端控制器。
  2. 前端控制器将请求发送给处理器映射器,处理器映射器根据路径找到方法的执行链,返回给前端控制器。
  3. 前端控制器将方法的执行链发送给处理器适配器,处理器适配器根据方法类型找到对应的处理器。
  4. 处理器执行方法,将结果返回给前端控制器。
  5. 前端控制器将结果发送给视图解析器,视图解析器找到视图文件位置。
  6. 视图渲染数据并将结果显示到客户端。

SpringMVC封装参数

简单数据类型

在Servlet中我们通过request.getParameter(name)获取请求参数。该方式存在两个问题:

  • 请求参数较多时会出现代码冗余。
  • 与容器紧耦合。

而SpringMVC支持参数注入的方式用于获取请求数据,即将请求参数直接封装到方法的参数当中。用法如下:

  1. 编写控制器方法
// 封装为简单数据类型类型参数@RequestMapping(\"/c1/param1\")public void simpleParam(String username,int age){ System.out.println(username); System.out.println(age);}
  1. 访问该方法时,请求参数名和方法参数名相同,即可完成自动封装。

http://localhost:8080/c1/param1?username=bz&age=10

简单对象

SpringMVC支持将参数直接封装为对象,写法如下:

  1. 编写实体类
public class Student { private int id; private String name; private String sex; // 省略getter/setter/tostring}
  1. 编写控制器方法
// 封装为简单对象类型参数@RequestMapping(\"/c1/param2\")public void objParam(Student student){ System.out.println(student);}
  1. 访问该方法时,请求参数名和对象参数的属性名相同,即可完成自动封装。

http://localhost:8080/c1/param2?id=1&name=bz&sex=female

关联对象

SpringMVC还可以将请求参数封装为关联对象,即对象的属性也是一个对象。写法如下:

  1. 编写实体类
public class Student { private int id; private String name; private String sex; private Address address; // 地址对象 // 省略getter/setter/tostring}public class Address { private String info; //地址信息 private String postcode; //邮编 // 省略getter/setter/tostring}
  1. 编写控制器方法
// 获取关联对象类型参数@RequestMapping(\"/c1/param3\")public void objParam2(Student student){ System.out.println(student);}
  1. 访问该方法时,请求参数名和方法参数的属性名相同,即可完成自动封装。

http://localhost:8080/c1/param3?id=1&name=bz&sex=female&address.info=beijing&address.postcode=030000

我们也可以使用表单发送带有参数的请求:

<html> <head> <title>表单提交</title> </head> <body> <form action=\"/c1/param3\" method=\"post\"> id:<input name=\"id\"> 姓名:<input name=\"name\"> 性别:<input name=\"sex\"> 住址:<input name=\"address.info\"> 邮编:<input name=\"address.postcode\"> <input type=\"submit\"> </form> </body></html>

简单数据类型集合

1.编写控制器方法

// 封装为简单数据类型集合,参数前必须添加@RequestParam注解

@RequestMapping(\"/c1/param4\")public void listParam(@RequestParam List<String> users){ System.out.println(users);}
  1. 该方式也可以封装为简单数据类型数组:
@RequestMapping(\"/c1/param5\")public void listParam2(@RequestParam String[] users){ System.out.println(users[0]); System.out.println(users[1]);}
  1. 请求的参数写法

http://localhost:8080/c1/param4?users=bj&users=sxt

Map集合

同样,SpringMVC要想把参数封装到Map集合中,需要封装到有Map属性的对象中。

  1. 编写实体类
public class AddressMap { private Map<String,Address> address; // 省略getter/setter/tostring}
  1. 编写控制器方法
// 对象中包含map属性@RequestMapping(\"/c1/param7\")public void mapParam3(AddressMap addressMap){ System.out.println(addressMap.getAddress());}
  1. 请求的参数写法

参数的写法为:map属性名[键].值中的属性,例如:address[one].info

请求的路径如下:

http://localhost:8080/c1/param7?address[one].info=bj&address[one].postcode=100010&address[two].info=sh&address[two].postcode=100011

编码后为:

http://localhost:8080/c1/param7?address%5Bone%5D.info=bj&address%5Bone%5D.postcode=100010&address%5Btwo%5D.info=sh&address%5Btwo%5D.postcode=100011

参数类型转换器

前端传来的参数全部为字符串类型,SpringMVC使用自带的转换器将字符串参数转为需要的类型。如:

// 获取简单类型参数@RequestMapping(\"/c1/param1\")public void simpleParam(String username,int age){ System.out.println(username); System.out.println(age);}

但在某些情况下,无法将字符串转为需要的类型,如:

@RequestMapping(\"/c1/param8\")public void dateParam(Date birthday){ System.out.println(birthday);}

由于日期数据有很多种格式,SpringMVC默认只能转换2050/1/1这样的日期格式。但假如前端传来的参数格式为2025-01-01时,SpringMVC就无法解析参数。此时需要自定义参数类型转换器。

  1. 定义类型转换器类,实现Converter接口
// 类型转换器必须实现Converter接口,两个泛型代表转换前的类型,转换后的类型public class DateConverter implements Converter<String, Date> { /** * 转换方法 * @param source 转换前的数据 * @return 转换后的数据 */ @Override public Date convert(String source) { SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\"); Date date = null; try { date = sdf.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; }}
  1. 0注册类型转换器对象
<bean id=\"dateConverter\" class=\"org.springframework.context.support.ConversionServiceFactoryBean\">  <property name=\"converters\"> <set>  <bean class=\"com.jjy.converter.DateConverter\"></bean> </set> </property></bean> <mvc:annotation-driven conversion-service=\"dateConverter\"></mvc:annotation-driven>
  1. 此时再访问http://localhost:8080/c1/param9?birthday=2025-01-01时,SpringMVC即可将请求参数封装为Date类型的参数。

编码过滤器

在传递参数时,tomcat10以上不会出现中文乱码,tomcat8以上能处理get请求的中文乱码,但不能处理post请求的中文乱码。

编写jsp表单

<html> <head> <title>编码过滤器</title> </head> <body> <form action=\"/cn/code\" method=\"post\"> 姓名:<input name=\"username\"> <input type=\"submit\"> </form> </body></html>

编写控制器方法

@RequestMapping(\"/cn/code\")public void code(String username){ System.out.println(username);}

SpringMVC提供了处理中文乱码的过滤器,在web.xml中配置该过滤器即可解决中文乱码问题:

<filter> <filter-name>encFilter</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>encFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>

Servlet原生对象

SpringMVC的方法中已经封装好了Servlet原生对象,在方法参数中定义HttpServletRequestHttpServletResponseHttpSession等类型的参数即可直接在方法中使用。

// 使用Servlet原生对象@RequestMapping(\"/c1/param9\")public void servletParam(HttpServletRequest request, HttpServletResponse response, HttpSession session){ // 原生对象获取参数  System.out.println(request.getParameter(\"name\")); System.out.println(response.getCharacterEncoding()); System.out.println(session.getId());}

访问该方法即可:http://localhost:8080/c1/param9?name=bjsxt

一般情况下,在SpringMVC中都有对Servlet原生对象的方法的替代,推荐使用SpringMVC的方式代替Servlet原生对象。

SpringMVC处理响应

视图解析器

SpringMVC默认情况下会在控制器执行完成后跳转到视图页面,视图解析器能找到相应的视图,之前的404异常就是由于没有配置视图解析器导致找不到视图。

在SpringMVC中提供了13个视图解析器,用于支持不同的视图技术。InternalResourceViewResolver是SpringMVC的默认视图解析器,用来解析JSP视图。

<bean id=\"viewResolver\" class=\"org.springframework.web.servlet.view.InternalResourceViewResolver\">  <property name=\"prefix\" value=\"/\" />  <property name=\"suffix\" value=\".jsp\" /></bean>

返回值为void

我们可以通过控制器方法的返回值设置跳转的视图,控制器方法支持的返回值类型为voidStringModelAndView,我们以此进行介绍。

当控制器方法的返回值为void时,会跳转到名字是 前缀+方法路径名+后缀 的jsp页面

  1. 编写控制器方法
// 路径是helloMVC,方法执行完后会跳转到/helloMVC.jsp@RequestMapping(\"/helloMVC\")public void helloMVC(){ System.out.println(\"hello SpringMVC!\");}
  1. 编写helloMVC.jsp
<html> <head> <title>MVC</title> </head> <body> <h1>欢迎来到SpringMVC</h1> </body></html>6

返回值为ModelAndView

ModelAndView是SpringMVC提供的对象,该对象可以向request域设置数据并指定跳转的页面。该对象中包含Model对象和View对象。

Model:向request域中设置数据。 View:指定跳转的页面。

编写控制器方法

// 返回值为ModelAndView@RequestMapping(\"/c2/hello2\")public ModelAndView useMAV(){ System.out.println(\"返回值类型为ModelAndView\"); // 1.创建ModelAndView对象 ModelAndView modelAndView = new ModelAndView(); // 2.获取Model对象,本质是一个Map Map<String, Object> model = modelAndView.getModel(); // 3.使用Model对象向request域设置数据 model.put(\"name\",\"jjy\"); // 4.使用View对象设置跳转的路径为/baizhan.jsp modelAndView.setViewName(\"jjy\"); return modelAndView;}
  1. 编写jsp页面
<html> <head> <title>中北</title> </head> <body> <h1>你好!${requestScope.name}</h1> </body></html>
  1. 修改web.xml命名空间,让jsp页面默认支持el表达式
<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_3_1.xsd\" version=\"3.1\"></web-app>

向request域设置数据

当控制器返回值为ModelAndView时我们可以向request域设置数据,我们还有以下方法可以向request域设置数据:

使用原生的HttpServletRequest

@RequestMapping(\"/c2/hello3\")public String setRequestModel(HttpServletRequest request){ request.setAttribute(\"name\",\"中北\"); return \"baizhan\";}

使用Model、ModelMap
SpringMVC提供了Model接口和ModelMap类,控制器方法添加这两个类型的参数,使用该参数设置数据,该数据就会存到request域中。

@RequestMapping(\"/c2/hello4\")public String setRequestModel2(Model model, ModelMap modelMap){ // model.addAttribute(\"name\",\"中北2\"); modelMap.addAttribute(\"name\",\"中北3\"); return \"baizhan\";}

使用Map集合
Model接口底层就是一个Map集合,我们可以给控制器方法设置Map类型的参数,向Map中添加键值对,数据也会存到request域中。

@RequestMapping(\"/c2/hello5\")public String setRequestModel3(Map map){ map.put(\"name\",\"中北4\"); return \"baizhan\";}

向session域设置数据

Session作用域表示在当前会话中有效。我们可以在控制器方法的参数中使用HttpSession对象来向session域设置数据。

编写控制器方法

@RequestMapping(\"/c2/hello6\")public String setSeesionModel(HttpSession session){ session.setAttribute(\"address\",\"北京\"); return \"baizhan\";}

编写jsp页面

<html> <head> <title>中北</title> </head> <body> <h1>你好!${requestScope.name}</h1> <h1>地址是!${sessionScope.address}</h1> </body></html>

除了使用Servlet原生对象,我们也可以使用SpringMVC提供的注解向session域设置数据,后面我们会讲到。

向context域设置数据

context作用域表示在整个应用范围都有效。在SpringMVC中对context作用域传值,可以使用ServletContext对象来实现。但是该对象不能直接注入到方法参数中,需要通过HttpSession对象获取。

  1. 编写控制器方法
@RequestMapping(\"/c2/hello7\")public String setContextModel(HttpSession session){ ServletContext servletContext = session.getServletContext(); servletContext.setAttribute(\"age\",10); return \"baizhan\";}

2.编写jsp页面

<html> <head> <title>中北</title> </head> <body> <h1>你好!${requestScope.name}</h1> <h1>地址是!${sessionScope.address}</h1> <h1>年纪是!${applicationScope.age}</h1> </body></html>

请求转发&重定向

之前的案例,我们发现request域中的值可以传到jsp页面中,也就是通过视图解析器跳转到视图的底层是请求转发。

如果我们跳转时不想使用视图解析器,可以使用原生HttpServletRequest进行请求转发或HttpServletResponse进行重定向:

@RequestMapping(\"/c2/hello8\")public void myForward1(HttpServletRequest request, HttpServletResponse response) throws Exception{ request.setAttribute(\"name\",\"中北\"); // 请求转发 // request.getRequestDispatcher(\"/c2/hello9\").forward(request,response); // 原生重定向 response.sendRedirect(\"/c2/hello9\");}@RequestMapping(\"/c2/hello9\")public void myForward2(HttpServletRequest request){ System.out.println(\"hello\"); System.out.println(request.getAttribute(\"name\"));}

SpringMVC还提供了一种更简单的请求转发和重定向的写法:

@RequestMapping(\"/c2/hello10\")public String myForward3(HttpServletRequest request){ request.setAttribute(\"name\",\"中北\"); // 请求转发 return \"forward:/c2/hello9\"; // 重定向 // return \"redirect:/c2/hello9\";}

SpringMVC注解

@Controlle r和 @RequestMapping

@Controller

作用:标记控制器,将控制器交给Spring容器管理。

位置:类上方

@RequestMapping

作用:给控制器方法设置请求路径

位置:方法或类上方。用于类上,表示类中的所有控制器方法都是以该地址作为父路径。

属性:

  • value/path:请求路径
  • method:指定请求方式
  • params:规定必须发送的请求参数
  • headers:规定请求必须包含的请求头
@Controller@RequestMapping(\"/c3\")public class MyController3 { /** * 访问路径是 /c3/a1 * 支持post和get请求 * 请求必须带有username参数 * 请求必须带有User-agen请求头 * */ @RequestMapping(path = \"/a1\", method = {RequestMethod.POST, RequestMethod.GET}, params = {\"username\"}, headers = {\"User-agent\"}) public String annotation1(String username) { System.out.println(username); return \"baizhan\"; }}

@RequestParam

作用:在控制器方法中封装请求参数

位置:方法参数前

属性:

  • value/name:指定请求参数名称
  • defaultValue: 为参数设置默认值
  • required:设置是否是必须要传入的参数
/* 定义请求的参数名为name,默认值为sxt,不是必须的参数 */@RequestMapping(\"/a2\")public String annotation2(@RequestParam(name = \"name\",defaultValue = \"sxt\",required = false) String username) { System.out.println(username); return \"baizhan\";}

请求URL的写法:http://localhost:8080/c3/a2?name=bz

@RequestHeader、@CookieValue

@RequestHeader

作用:将请求头数据封装到控制器方法参数中

位置:方法参数前

@CookieValue

作用:将Cookie数据封装到控制器方法参数中

位置:方法参数前

/* 获取User-Agent请求头 获取JSESSIONID的Cookie值 */@RequestMapping(\"/annotation3\")public String annotation3(@RequestHeader(\"User-Agent\") String userAgent, @CookieValue(\"JSESSIONID\") String jSessionId){ System.out.println(userAgent); System.out.println(jSessionId); return \"baizhan\";}

@SessionAttributes

作用:将Model模型中的数据存到session域中

位置:类上方

@Controller@RequestMapping(\"/c4\")// 将模型中的name数据保存到session中@SessionAttributes(\"name\")public class MyController4 { @RequestMapping(\"/t1\") public String t1(Model model){ // model中保存name数据 model.addAttribute(\"name\",\"中北 1111\"); return \"baizhan\"; } @RequestMapping(\"/t2\") public String t2(HttpSession session){ // 从session中获取name数据 Object name = session.getAttribute(\"name\"); System.out.println(name); return \"baizhan\"; }}

@ModelAttribute

作用1:设置指定方法在控制器其他方法前执行

位置:
方法上方

@Controller@RequestMapping(\"/c5\")public class MyController5 { @ModelAttribute public void before(){ System.out.println(\"前置方法\"); }@RequestMapping(\"/t1\") public Stringt1(){ System.out.println(\"t1\"); return \"zb\"; }}

作用2: 从Model模型中获取数据给参数赋值
位置:方法参数前

@Controller@RequestMapping(\"/c6\")public class MyController6 { //前置方法向Model中设置数据 @ModelAttribute public void before(Model model){ model.addAttribute(\"name\",\"中北\"); } //该参数不是从请求中获取,而是从Model中获取 @RequestMapping(\"/t1\") public String t1(@ModelAttribute(\"name\") String name){ System.out.println(name); return \"baizhan\"; }}

RESTful风格支持

SpringMVC(看这一篇就够了)

RESTful风格是一种URL路径的设计风格。在RESTful风格的URL路径中,网络上的任意数据都可以看成一个资源,它可以是一段文本、一张图片,也可以是一个Java对象。而每个资源都会占据一个网络路径,无论对该资源进行增删改查,访问的路径是一致的。

传统URL:

  • 查找id为1的学生:

http://localhost:8080/student/findById?id=30

  • 删除id为1的学生:

http://localhost:8080/student/deleteById?id=30

RESTful风格URL:

  • 查找id为30的学生:

http://localhost:8080/student/30

  • 删除id为30的学生:

http://localhost:8080/student/30

那么如何区分对该资源是哪一种操作?通过请求方式不同,判断进行的是什么操作。

之前我们学过两种请求方式,GET请求和POST请求,而访问RESTful风格的URL一共有四种请求方式:

  • GET请求:查询操作
  • POST请求:新增操作
  • DELETE请求:删除操作
  • PUT请求:修改操作

RESTful风格URL

  • 查找id为30的学生:

http://localhost:8080/student/30 GET方式请求

  • 删除id为30的学生:

http://localhost:8080/student/30 DELETE方式请求

RESTful风格的优点:

结构清晰、符合标准、易于理解、扩展方便。

@PathVariable

作用:在RESTful风格的URL中获取占位符的值

位置:方法参数前

属性:

value:获取哪个占位符的值作为参数值,如果占位符和参数名相同,可以省略该属性。

@Controller@RequestMapping(\"/student\")// 模拟学生的增删改查控制器public class MyController6 { // 路径中的/{id}表示占位符,最后会封装到方法的参数中使用 // 删除学生 @RequestMapping(value = \"/{id}\",method = RequestMethod.DELETE) public String deleteStudent(@PathVariable(\"id\") int id){ System.out.println(\"删除id为\"+id+\"的学生\"); return \"baizhan\"; } // 如果占位符和参数名相同,可以省略@PathVariable中的value属性 // 根据id查询学生 @RequestMapping(value = \"/{id}\",method = RequestMethod.GET) public String findStudentById(@PathVariable int id){ System.out.println(\"查找id为\"+id+\"的学生\"); return \"baizhan\"; } // 新增学生 @RequestMapping(value = \"/{id}\",method = RequestMethod.POST) public String addStudent(@PathVariable int id, Student student){ System.out.println(\"新增id为\"+id+\"的学生\"); System.out.println(student); return \"baizhan\"; } // 修改学生 @RequestMapping(value = \"/{id}\",method = RequestMethod.PUT) public String updateStudent(@PathVariable int id, Student student){ System.out.println(\"修改id为\"+id+\"的学生\"); System.out.println(student); return \"baizhan\"; }}

访问方式:

  • 新增学生:POST localhost:8080/student/100?name=百战&sex=女
  • 修改学生:PUT localhost:8080/student/100?name=百战1&sex=女1
  • 删除学生:DELETE localhost:8080/student/100
  • 查询学生:GET localhost:8080/student/100

@PostMapping、@GetMapping、@PutMapping、@DeleteMapping

作用:简化设置请求方式的@RequestMapping写法

位置:方法上方。

@Controller@RequestMapping(\"/student\")public class StudentController { // 删除学生 @DeleteMapping(\"/{id}\") public String deleteStudent(@PathVariable(\"id\") int id){ System.out.println(\"删除id为\"+id+\"的学生\"); return \"baizhan\"; } // 根据id查询学生 @GetMapping(\"/{id}\") public String findStudentById(@PathVariable int id){ System.out.println(id); System.out.println(\"根据id查询学生\"); return \"baizhan\"; } // 新增学生 @PostMapping(\"/{id}\") public String addStudent(@PathVariable int id, Student student){ System.out.println(id); System.out.println(student); System.out.println(\"新增学生\"); return \"baizhan\"; } // 修改学生 @PutMapping(\"/{id}\") public String updateStudent(@PathVariable int id, Student student){ System.out.println(id); System.out.println(student); System.out.println(\"修改学生\"); return \"baizhan\"; }}

HiddenHttpMethodFilter

由于浏览器的表单只支持GET与POST请求,不支持DELETE、PUT请求。所以SpringMVC提供了一个过滤器,可以将浏览器的POST请求改为指定的请求方式,发送给控制器的方法。

用法如下:

  1. 在web.xml中配置过滤器
<filter> <filter-name>httpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class></filter><filter-mapping> <filter-name>httpMethodFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
  1. 编写控制器方法
@Controller@RequestMapping(\"/c7\")public class MyController7 { @DeleteMapping(\"/delete\") public String testDelete(){ System.out.println(\"删除方法\"); return \"baizhan\"; } @PutMapping(\"/put\") public String testPut(){ System.out.println(\"修改方法\"); return \"baizhan\"; }}
  1. 在jsp中编写表单
<html> <head> <title>DELETE、PUT提交</title> </head> <body>    <form action=\"/c7/delete\" method=\"post\"> <input type=\"hidden\" name=\"_method\" value=\"DELETE\"> <input type=\"submit\" value=\"删除\"> </form> <hr/>  <form action=\"/c7/put\" method=\"post\"> <input type=\"hidden\" name=\"_method\" value=\"PUT\"> <input type=\"submit\" value=\"修改\"> </form> </body></html>

@ResponseBody

作用:方法返回的对象转换为JSON格式,并将JSON数据直接写入到输出流中,使用此注解后不会再经过视图解析器。使用该注解可以处理Ajax请求。

位置:方法上方或方法返回值前

  1. 编写jsp页面,发送ajax请求
<html> <head> <title>ajax请求</title> <script src=\"/js/jquery-2.1.1.min.js\"></script> <script> $(function () { $(\"#btn\").click(function () { var name = $(\"#name\").val(); var sex = $(\"#sex\").val(); $.get(\"/c8/addStudent\",{\"name\":name,\"sex\":sex},function (data){ console.log(data);  }); }); }); </script> </head> <body> 姓名:<input id=\"name\"/><br/> 性别:<input id=\"sex\"/><br/> <input type=\"button\" value=\"提交\" id=\"btn\"/> </body></html>
  1. 由于jsp页面中引入jQuery的js文件,而SpringMVC会拦截所有资源,造成jquery.js失效,需要在SpringMVC核心配置文件中放行静态资源。
<mvc:default-servlet-handler />
  1. 编写结果实体类,该实体类会封装一个请求的结果
// 请求的结果对象public class Result { private boolean flag; // 请求是否成功  private String message; // 请求提示信息  // 省略getter/setter/构造方法}
  1. 编写控制器
@Controller@RequestMapping(\"/c8\")public class MyController8 { @GetMapping(\"/addStudent\") @ResponseBody public Result addStudent(String name,String sex){ System.out.println(name+\":\"+sex); Result result = new Result(true, \"添加学生成功\"); return result; }}
  1. SpringMVC会将Result对象转为JSON格式写入输出流,而SpringMVC默认使用的JSON转换器是jackson,需要在pom中添加jackson依赖。
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0</version></dependency><dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version></dependency><dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.0</version></dependency>

@RestController

如果一个控制器类下的所有控制器方法都返回JSON格式数据且不进行跳转,可以使用@RestController代替@Controller,此时每个方法上的@ResponseBody都可以省略。

@RestController@RequestMapping(\"/c8\")public class MyController8 { @GetMapping(\"/addStudent\") public Result addStudent(String name, String sex) { // 输出接受到的参数,模拟添加学生  System.out.println(name+\":\"+sex); // 返回结果  Result result = new Result(true, \"添加学生成功!\"); return result; }}

静态资源映射

当在DispatcherServlet的<url-pattern>中配置拦截 “/” 时,除了jsp文件不会拦截以外,其他所有的请求都会经过前端控制器进行匹配。此时静态资源例如css、js、jpg等就会被前端控制器拦截,导致不能访问,出现404问题。想要正常映射静态资源共有三种方案:

配置静态资源筛查器
在SpringMVC的配置文件中配置后,会在Spring容器中创建一个资源检查器,它对进入DispatcherServlet的URL进行筛查,如果不是静态资源,才由DispatcherServlet处理。

修改SpringMVC核心配置文件:

配置静态资源映射器
SpringMVC模块提供了静态资源映射器组件,通过标签配置静态资源映射器,配置后的路径不会由DispatcherServlet处理。

修改SpringMVC核心配置文件:

<!--配置静态资源映射器--><!-- mapping:配置请求的URL location:资源路径--><mvc:resources mapping=\"/img/\" location=\"/img/\"/><mvc:resources mapping=\"/js/\" location=\"/js/\"/>

配置默认Servlet处理静态资源
在web.xml可以配置默认Servlet处理静态资源,该Servlet由tomcat提供,它会直接访问静态资源不进行其他操作。这样就避免了使用DispatcherServlet对静态资源的拦截:

修改web.xml:

<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.jpg</url-pattern></servlet-mapping><servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern></servlet-mapping><servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern></servlet-mapping><servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.png</url-pattern></servlet-mapping>

@RequestBody

作用:将请求中JSON格式的参数转为JAVA对象

位置:写在方法参数前

  1. AJAX请求发送JSON格式的参数
<html> <head> <title>ajax请求</title> <script src=\"/js/jquery-2.1.1.min.js\"></script> <script> $(function (){ $(\"#btn\").click(function (){ var name=$(\"#name\").val(); var sex=$(\"#sex\").val(); var param = JSON.stringify({\"name\":name,\"sex\":sex}); $.ajax({ url:\"/c8/addStudent2\", contentType:\"application/json\", type:\"post\", data:param, success:function (data){  console.log(data); }  }) }) }) </script> </head> <br> 姓名:<input id=\"name\"><br /> 性别:<input id=\"sex\"><br /> <input type=\"button\" value=\"提交\" id=\"btn\"> </body></html>
  1. 编写控制器
@PostMapping(\"/addStudent2\")@ResponseBodypublic Result addStudent2(@RequestBody Student student) { System.out.println(student); // 返回添加结果 Result result = new Result(true, \"添加学生成功!\"); return result;}

SpringMVC上传

SpringMVC(看这一篇就够了)

原生方式上传

上传是Web工程中很常见的功能,SpringMVC框架简化了文件上传的代码,我们首先使用JAVAEE原生方式上传文件:

  1. 编写上传表单
<html> <head> <title>上传</title> </head> <body> <h3>文件上传</h3>   <form action=\"/fileUpload\" method=\"post\" enctype=\"multipart/form-data\">  选择文件:<input type=\"file\" name=\"upload\"/> <input type=\"submit\" value=\"上传\"/> </form> </body></html>
  1. 接收请求体数据:
@RequestMapping(\"/fileUpload\")public String upload(HttpServletRequest request) throws Exception { // 获取输入流 ServletInputStream is = request.getInputStream(); // 从输入流获取请求体数据 int i = 0; while ((i=is.read())!=-1){ System.out.print((char)i); } return \"baizhan\";}

接下来需要分析请求体中的文件项,并将数据写入磁盘,此时需要借助文件上传工具

  1. 引入文件上传依赖:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-fileupload2-jakarta</artifactId> <version>2.0.0-M1</version></dependency>
  1. 编写控制器接收上传请求,控制器进行三步操作:
  • 创建文件夹,存放上传文件。

  • 分析请求体,找到上传文件数据。

  • 将文件数据写入文件夹。

@RequestMapping(\"/fileUpload\")public String upload(HttpServletRequest request) throws Exception { // 创建文件夹,存放上传文件 // 1.设置上传文件夹路径 String realPath = \"E:\\\\springmvc\\\\mvc_demo1\\\\upload\"; // 2.判断该目录是否存在,如果不存在,创建该目录 File file = new File(realPath); if (!file.exists()){ file.mkdirs(); } // 分析请求体,找到上传文件数据 // 1.创建磁盘文件工厂 DiskFileItemFactory factory = DiskFileItemFactory.builder().get(); // 2.创建上传数据分析器对象 JakartaServletDiskFileUpload upload = new JakartaServletDiskFileUpload(factory); // 3.利用分析器对象解析请求体,返回所有数据项 List<DiskFileItem> fileItems = upload.parseRequest(request); // 4.遍历所有数据,找到文件项(非表单项) for (FileItem fileItem : fileItems) { if (!fileItem.isFormField()){ // 将文件数据写入文件夹 // 1.获取文件名 String name = fileItem.getName(); // 2.将文件写入磁盘 fileItem.write(Paths.get(realPath,name)); // 3.删除内存中的临时文件 fileItem.delete(); } } return \"baizhan\";}

SpringMVC方式上传

SpringMVC使用框架提供的文件解析器对象,可以直接将请求体中的文件数据转为MultipartFile对象,从而省略原生上传中分析请求体的步骤。

  1. 在SpringMVC核心配置文件配置文件解析器
<bean id=\"multipartResolver\" class=\"org.springframework.web.multipart.support.StandardServletMultipartResolver\"></bean>
  1. 在web.xml中进行上传配置
<servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <multipart-config>  <max-file-size>104857600</max-file-size>  <max-request-size>418018841</max-request-size>  <file-size-threshold>0</file-size-threshold> </multipart-config></servlet>
  1. 创建JSP表单
<form action=\"/fileUpload2\" method=\"post\" enctype=\"multipart/form-data\"> <input type=\"file\" name=\"file\"/> <input type=\"submit\" value=\"上传\"/></form>
  1. 编写控制器接收上传请求
// MultipartFile参数名必须和JSP文件空间的name属性一致@RequestMapping(\"/fileUpload2\")public String upload2(MultipartFile file,HttpServletRequest request) throws IOException { // 创建文件夹,存放上传文件 String realPath = \"E:\\\\springmvc\\\\mvc_demo1\\\\upload\"; File dir = new File(realPath); if (!dir.exists()){ dir.mkdirs(); } // 将上传的数据写到文件夹的文件中 // 1.拿到上传的文件名 String filename = file.getOriginalFilename(); filename = UUID.randomUUID()+\"_\"+filename; // 2.创建空文件 File newFile = new File(dir,filename); // 3.将数据写入空文件中 file.transferTo(newFile); return \"baizhan\";}

上传多文件

SpringMVC支持一次性上传多个文件,写法如下:

  1. 创建JSP表单
<form action=\"/fileUpload3\" method=\"post\" enctype=\"multipart/form-data\"> 用户名:<input name=\"username\"/> 文件1:<input type=\"file\" name=\"files\"/> 文件2:<input type=\"file\" name=\"files\"/> <input type=\"submit\" value=\"上传\"/></form>
  1. 编写控制器接收上传请求
// 处理多文件上传,参数类型为MultipartFile数组,参数名和JSP文件控件的name属性一致@RequestMapping(\"/fileUpload3\")public String upload3(MultipartFile files[],String username,HttpServletRequest request) throws Exception { System.out.println(username); //1.设置上传文件保存的文件夹 String realPath = \"E:\\\\springmvc\\\\mvc_demo1\\\\upload\"; File dir = new File(realPath); if (!dir.exists()){ dir.mkdirs(); } //2.遍历数组,将上传文件保存到文件夹 for(MultipartFile file:files){ String filename = file.getOriginalFilename(); filename = UUID.randomUUID()+\"_\"+filename; File newFile = new File(dir, filename); file.transferTo(newFile); } return \"baizhan\";}

异步上传

SpringMVC(看这一篇就够了)
之前的上传方案,在上传成功后都会跳转页面。而在实际开发中,很多情况下上传后不进行跳转,而是进行页面的局部刷新,比如:上传头像成功后将头像显示在网页中。这时候就需要使用异步文件上传。

  1. 编写JSP页面,引入jQuery和jQuery表单上传工具jquery.form.js
<html><head> <title>上传</title> <script src=\"/js/jquery-2.1.1.min.js\"></script> <script src=\"/js/jquery.form.js\"></script></head><body><h3>文件上传</h3><form id=\"ajaxForm\" enctype=\"multipart/form-data\" > <input type=\"file\" name=\"file\"/>  <input type=\"button\" value=\"上传头像\" id=\"btn\"/></form><img src=\"/\" width=\"100\" id=\"img\"><script> $(function () { $(\"#btn\").click(function () { // 异步提交表单 $(\"#ajaxForm\").ajaxSubmit({ url:\"/fileUpload4\", type:\"post\", success:function (data) { $(\"#img\").attr(\"src\",data); } }) }) })</script></body></html>
  1. 编写控制器接收异步上传请求
@RequestMapping(\"/fileUpload4\")@ResponseBodypublic String upload4(MultipartFile file,HttpServletRequest request) throws Exception { // 创建文件夹,存放上传文件 String realPath = request.getServletContext().getRealPath(\"/upload\"); File dir = new File(realPath); if (!dir.exists()){ dir.mkdirs(); } // 将上传的数据写到文件夹的文件中 String filename = file.getOriginalFilename(); filename = UUID.randomUUID()+\"_\"+filename; File newFile = new File(dir, filename); file.transferTo(newFile); // 返回文件的路径 return \"/upload/\"+filename;}

跨服务器上传

SpringMVC(看这一篇就够了)
由于文件占据磁盘空间较大,在实际开发中往往会将文件上传到其他服务器中,此时需要使用跨服务器上传文件。

  1. 解压tomcat作为图片服务器,在tomcat的webapps下创建upload目录作为文件上传目录。

  2. 修改tomcat的conf/web.xml文件,支持跨服上传。

<servlet> <init-param> <param-name>readonly</param-name> <param-value>false</param-value> </init-param></servlet>
  1. 修改tomcat的conf/server.xml文件,修改tomcat端口,修改完开启tomcat服务器
<Connector port=\"8081\" protocol=\"HTTP/1.1\" connectionTimeout=\"20000\" redirectPort=\"8443\" />
  1. 编写JSP上传表单
<html> <head> <title>上传</title> <script src=\"/js/jquery-2.1.1.min.js\"></script> <script src=\"/js/jquery.form.js\"></script> </head> <body> <h3>文件上传</h3> <form id=\"ajaxForm\" enctype=\"multipart/form-data\" > <input type=\"file\" name=\"file\"/> <input type=\"button\" value=\"上传头像\" id=\"btn\"/> </form> <img src=\"/\" width=\"100\" id=\"img\"> <script> $(function () { $(\"#btn\").click(function () { $(\"#ajaxForm\").ajaxSubmit({ url:\"/fileUpload5\", type:\"post\", success:function (data) {  $(\"#img\").attr(\"src\",data); }  }) }) }) </script> </body></html>
  1. 添加跨服上传依赖
<dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-core</artifactId> <version>1.18.1</version></dependency><dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-client</artifactId> <version>1.18.1</version></dependency>
  1. 创建控制器方法,该方法在接受到上传请求后将文件保存到其他服务器上。
@RequestMapping(\"/fileUpload5\")@ResponseBodypublic String upload4(MultipartFile file) throws Exception { // 设置跨服上传的服务器路径 String path = \"http://localhost:8081/upload/\"; // 获取上传的文件名 String filename = file.getOriginalFilename(); filename = UUID.randomUUID()+\"_\"+filename; // 跨服上传: // 1.创建客户端对象 Client client = Client.create(); // 2.使用客户端对象连接图片服务器 WebResource resource = client.resource(path + filename); //3.传输数据 resource.put(file.getBytes()); return path+filename;}

查询可下载的文件

将文件上传到服务器后,有时我们需要让用户下载上传的文件,下载分为两步,第一步查询可以下载的文件,第二步选中文件进行下载。首先我们编写查询所有可下载文件的功能:

  1. 编写控制器方法,查询所有可下载的文件,并跳转到下载页面
// 查询可下载的文件@RequestMapping(\"/showFiles\")public String showFileDown(Model model){ // 1.获取下载文件的路径集合。注:跨服务器上传中,网络路径无法获取文件列表。 String path = \"E:\\\\springmvc\\\\mvc_demo1\\\\upload\"; File file = new File(path); String[] files = file.list(); // 2.将路径放入Model中,跳转到JSP页面 model.addAttribute(\"files\",files); return \"download\";}
  1. 添加JSTL依赖
<dependency> <groupId>jakarta.servlet.jsp.jstl</groupId> <artifactId>jakarta.servlet.jsp.jstl-api</artifactId> <version>3.0.0</version></dependency><dependency> <groupId>org.glassfish.web</groupId> <artifactId>jakarta.servlet.jsp.jstl</artifactId> <version>2.0.0</version></dependency>
  1. 编写下载页面
<html> <head> <title>下载</title> </head> <body> <h3>文件下载</h3>  <c:forEach items=\"${files}\" var=\"file\"> <a href=\"/download?fileName=${file}\">${file}</a><br/> </c:forEach> </body></html>

下载文件

// 文件下载@RequestMapping(\"/download\")public void fileDown(HttpServletResponse response,String fileName) throws IOException { // 设置响应头 response.setHeader(\"Content-Disposition\",\"attachment;filename=\"+fileName); // 获取文件路径 String path = \"E:\\\\springmvc\\\\mvc_demo1\\\\upload\"; File file = new File(path,fileName); // 获取字节输出流 ServletOutputStream os = response.getOutputStream(); // 使用输出流写出文件 os.write(FileUtils.readFileToByteArray(file)); os.flush(); os.close();}

SpringMVC异常处理

单个控制器异常处理

在开发过程中, Dao、Service、Controller层代码出现都可能抛出异常。如果哪里产生异常就在哪里处理,则会降低开发效率。所以一般情况下我们会让异常向上抛出,最终到达DispatcherServlet中,此时SpringMVC提供了异常处理器进行异常处理,这样可以提高开发效率。
SpringMVC(看这一篇就够了)

  1. 处理单个Controller的异常:
@Controller@RequestMapping(\"/c9\")public class MyController9 { @RequestMapping(\"/t1\") public String t1(){ String str = null; str.length(); return \"index\"; } @RequestMapping(\"/t2\") public String t2(){ int i = 1/0; return \"index\"; } @RequestMapping(\"/t3\") public String t3(){ int[] arr = new int[1]; arr[2] = 10; return \"index\"; } /** * 异常处理方法 * @param ex 异常对象 * @param model 模型对象 * @return */ // 添加@ExceptionHandler,表示该方法是处理异常的方法,属性为处理的异常类 @ExceptionHandler({java.lang.NullPointerException.class,java.lang.ArithmeticException.class}) public String exceptionHandler1(Exception ex, Model model){ // 向模型中添加异常对象 model.addAttribute(\"msg\",ex); // 跳转到异常页面 return \"error\"; } // 方法1不能处理的交给方法2处理,不同的处理策略 @ExceptionHandler(java.lang.Exception.class) public String exceptionHandler2(Exception ex, Model model){ // 向模型中添加异常对象 model.addAttribute(\"msg\",ex); // 跳转到异常页面 return \"error2\"; }}
  1. 异常页面error.jsp
<html> <head> <title>出错了!</title> </head> <body> <h3>ERROR 发生异常!${msg}</h3> </body></html>异常页面error2.jsp<html> <head> <title>出错了!</title> </head> <body> <h3>ERROR2 发生严重异常!${msg}</h3> </body></html>

全局异常处理

在控制器中定义异常处理方法只能处理该控制器类的异常,要想处理所有控制器的异常,需要定义全局异常处理类。

  1. 编写另一个有异常的控制器类
@Controller@RequestMapping(\"/c10\")public class MyController10 { @RequestMapping(\"/t1\") public String t1(){ String str = null; str.length(); return \"index\"; }}
  1. 编写全局异常处理器类
// 全局异常处理器类,需要添加@ControllerAdvice@ControllerAdvicepublic class GlobalExceptionHandler { /** * 异常处理方法 * * @param ex 异常对象 * @param model 模型对象 * @return */ // 添加@ExceptionHandler,表示该方法是处理异常的方法,属性为处理的异常类 @ExceptionHandler({java.lang.NullPointerException.class, java.lang.ArithmeticException.class}) public String exceptionHandle1(Exception ex, Model model) { // 向模型中添加异常对象 model.addAttribute(\"msg\", ex); // 跳转到异常页面 return \"error\"; } // 方法一不能处理的异常交给方法二处理 @ExceptionHandler(java.lang.Exception.class) public String exceptionHandle2(Exception ex, Model model) { model.addAttribute(\"msg\", ex); return \"error2\"; }}

自定义异常处理器

以上方式都是使用的SpringMVC自带的异常处理器进行异常处理,我们还可以自定义异常处理器处理异常:

// 自定义异常处理器实现HandlerExceptionResolver接口,并放入Spring容器中@Componentpublic class MyExceptionHandler implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView modelAndView = new ModelAndView(); if (ex instanceof NullPointerException){ modelAndView.setViewName(\"error\"); }else{ modelAndView.setViewName(\"error2\"); } modelAndView.addObject(\"msg\",ex); return modelAndView; }}

SpringMVC拦截器

SpringMVC(看这一篇就够了)
SpringMVC的拦截器(Interceptor)也是AOP思想的一种实现方式。它与Servlet的过滤器(Filter)功能类似,主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。

拦截器和过滤器的区别

  • 拦截器是SpringMVC组件,而过滤器是Servlet组件。
  • 拦截器不依赖Web容器,过滤器依赖Web容器。
  • 拦截器只能对控制器请求起作用,而过滤器则可以对所有的请求起作用。
  • 拦截器可以直接获取IOC容器中的对象,而过滤器就不太方便获取。

拦截器的使用

接下来我们使用SpringMVC拦截器

  1. 创建被拦截的方法
@RequestMapping(\"/m1\")public String m1(){ System.out.println(\"控制器方法\"); return \"result\";}
  1. 创建拦截器类,该类实现HandlerInterceptor接口,需要重写三个方法:
  • preHandle:请求到达Controller前执行的方法,返回值为true通过拦截器,返回值为false被拦截器拦截。
  • postHandle:跳转到JSP前执行的方法
  • afterCompletion:跳转到JSP后执行的方法
// 拦截器类public class MyInterceptor implements HandlerInterceptor { // 请求到达Controller前执行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println(\"请求到达Controller前\"); // 如果return false则无法到达Controller return true; } // 跳转到JSP前执行,此时可以向Request域添加数据 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { System.out.println(\"跳转到JSP前\"); request.setAttribute(\"name\",\"zb\"); } // 跳转到JSP后执行,此时已经不能向Request域添加数据 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println(\"跳转到JSP后\"); request.setAttribute(\"age\",10); }}
  1. 编写JSP页面
<html> <head> <title>结果</title> </head> <body> <h3>name:${requestScope.name}</h3> <h3>age:${requestScope.age}</h3> </body></html>
  1. 在SpringMVC核心配置文件中配置拦截器
<mvc:interceptors> <mvc:interceptor>  <mvc:mapping path=\"/**\"/>  <bean class=\"com.jjy.interceptor.MyInterceptor\"/> </mvc:interceptor></mvc:interceptors>

全局拦截器

全局拦截器可以拦截所有控制器处理的URL,作用等于/**,配置方式如下:

<mvc:interceptors>  <bean class=\"com.jjy.interceptor.MyInterceptor\"> </bean></mvc:interceptors>

拦截器链与执行顺序

SpringMVC(看这一篇就够了)

如果一个URL能够被多个拦截器所拦截,全局拦截器最先执行,其他拦截器根据配置文件中配置的从上到下执行,接下来我们再配置一个拦截器:

  1. 编写拦截器类
public class MyInterceptor2 implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println(\"拦截器2:请求到达Controller前\"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { System.out.println(\"拦截器2:跳转到JSP前\"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println(\"拦截器2:跳转到JSP后\"); }}
  1. 配置拦截器链
<mvc:interceptors>  <mvc:interceptor> <mvc:mapping path=\"/**\"/> <bean class=\"com.jjy.interceptor.MyInterceptor\"/> </mvc:interceptor>  <mvc:interceptor> <mvc:mapping path=\"/**\"/> <bean class=\"com.jjy.interceptor.MyInterceptor2\"/> </mvc:interceptor></mvc:interceptors>

访问控制器方法后输出如下:

SpringMVC(看这一篇就够了)

结论

preHandle()顺序执行,postHandle()、afterComletion()逆序执行。
只要有一个preHandle()拦截,后面的preHandle(),postHandle()都不会执行。
只要相应的preHandle()放行,afterComletion()就会执行。

过滤敏感词

接下来我们编写一个拦截器案例,需求如下:
SpringMVC(看这一篇就够了)

在系统中,我们需要将所有响应中的一些敏感词替换为***,此时可以使用拦截器达到要求:

  1. 编写控制器方法
@RequestMapping(\"/t3\")public String t3(Model model){ model.addAttribute(\"name\",\"大笨蛋\"); return \"result\";}
  1. 编写敏感词拦截器
// 敏感词拦截器public class SensitiveWordInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 敏感词列表 String[] sensitiveWords = {\"坏人\",\"暴力\",\"笨蛋\"}; // 获取Model中所有数据 Map<String, Object> model = modelAndView.getModel(); Set<Map.Entry<String, Object>> entries = model.entrySet(); // 遍历Model,将Model值和敏感词列表遍历比对,如果Model值包含敏感词,则替换 for (Map.Entry<String, Object> entry : entries) { String key = entry.getKey(); String value = entry.getValue().toString(); for (String sensitiveWord : sensitiveWords) { if (value.contains(sensitiveWord)){ String newStr = value.replace(sensitiveWord, \"***\"); model.put(key,newStr); } } } }}
  1. 配置拦截器
<mvc:interceptors>  <mvc:interceptor> <mvc:mapping path=\"/**\"/> <bean class=\"com.jjy.interceptor.SensitiveWordInterceptor\"></bean> </mvc:interceptor></mvc:interceptors>

SpringMVC跨域请求

同源策略

同源策略是浏览器的一个安全功能。同源,指的是两个URL的协议,域名,端口相同。浏览器出于安全方面的考虑,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。

哪些不受同源策略限制:

  1. 页面中的跳转、表单提交不会受到同源策略限制的。
  2. 静态资源引入也不会受到同源策略限制。如嵌入到页面中的等。

最容易受到同源策略影响的就是Ajax请求。

跨域请求

当请求URL的协议、域名、端口三者中任意一个与当前页面URL不同时即为跨域。浏览器执行JavaScript脚本时,会检查当前请求是否同源,如果不是同源资源,就不会被执行。

当前页面URL 被请求URL 是否跨域 原因 http://www.baidu.com/ http://www.baidu.com/index.html 否 http://www.baidu.com/ https://www.baidu.com/index.html 跨域 协议不同 http://www.zb.com/ http://www.baidu.com/ 跨域 主域名不同 http://www.baidu.com/ http://zb.baidu.com/ 跨域 子域名不同 http://www.baidu.com:8080/ http://www.baidu.com:7001/ 跨域 端口号不同

比如:

  1. 编写控制器方法
@RequestMapping(\"/t1\")@ResponseBodypublic String t1(){ System.out.println(\"测试跨域请求\"); return \"success\";}
  1. 编写JSP页面,发送异步请求
<html> <head> <title>跨域请求</title> <script src=\"/js/jquery-2.1.1.min.js\"></script> <script> $(function (){ $(\"#btn\").click(function () { // $.get(\"http://localhost:8080/c12/t1\",function (data) { // console.log(data); // }) $.get(\"http://127.0.0.1:8080/c12/t1\",function (data) { console.log(data);  }) }) }) </script> </head> <body> <button id=\"btn\">异步请求</button> </body></html>

结果:

当浏览器通过http://localhost:8080/cross.jsp访问JSP页面时

  • http://localhost:8080/c12/t1 能发送异步请求;
  • http://127.0.0.1:8080/c12/t1 由于同源无法发送异步请求,报以下异常:
    SpringMVC(看这一篇就够了)

接收跨域请求

SpringMVC提供了注解@CrossOrigin解决跨域问题。用法如下:

@RequestMapping(\"/t1\")@ResponseBody// 如果请求从http://localhost:8080发出,则允许跨域访问@CrossOrigin(\"http://localhost:8080\")public String t1(){ System.out.println(\"测试跨域请求\"); return \"success\";}

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力
在这里插入图片描述