SpringMVC(看这一篇就够了)

SpringMVC(看这一篇就够了)

目录:

SpringMVC什么是MVC模型SpringMVC案例SpringMVC执行流程SpringMVC封装参数简单数据类型简单对象关联对象简单数据类型集合Map集合参数类型转换器编码过滤器Servlet原生对象

SpringMVC处理响应视图解析器返回值为void返回值为ModelAndView向request域设置数据向session域设置数据向context域设置数据请求转发&重定向

SpringMVC注解@Controlle r和 @RequestMapping@RequestParam@RequestHeader、@CookieValue@SessionAttributes@ModelAttributeRESTful风格支持@PathVariable@PostMapping、@GetMapping、@PutMapping、@DeleteMappingHiddenHttpMethodFilter@ResponseBody@RestController静态资源映射@RequestBody

SpringMVC上传原生方式上传SpringMVC方式上传上传多文件异步上传跨服务器上传查询可下载的文件下载文件

SpringMVC异常处理单个控制器异常处理全局异常处理自定义异常处理器

SpringMVC拦截器拦截器的使用全局拦截器拦截器链与执行顺序过滤敏感词

SpringMVC跨域请求同源策略跨域请求接收跨域请求

SpringMVC

什么是MVC模型

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

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

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

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

SpringMVC

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

案例

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

引入相关依赖

org.springframework

spring-context

6.0.11

org.springframework

spring-web

6.0.11

org.springframework

spring-webmvc

6.0.11

jakarta.servlet

jakarta.servlet-api

6.0.0

provided

jakarta.servlet.jsp

jakarta.servlet.jsp-api

3.0.0

provided

在web.xml中配置前端控制器DispatcherServlet。

Archetype Created Web Application

dispatcherServlet

org.springframework.web.servlet.DispatcherServlet

contextConfigLocation

classpath:springmvc.xml

1

dispatcherServlet

/

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

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

5.编写控制器

@Controller

public class MyController1 {

// 该方法的访问路径是/c1/hello1

@RequestMapping("/c1/hello1")

public void helloMVC(){

System.out.println("hello SpringMVC!");

}

}

配置tomcat10启动项目,访问 http://localhost:8080/c1/hello1

SpringMVC执行流程

SpringMVC的组件

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

组件的工作流程

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

SpringMVC封装参数

简单数据类型

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

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

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

编写控制器方法

// 封装为简单数据类型类型参数

@RequestMapping("/c1/param1")

public void simpleParam(String username,int age){

System.out.println(username);

System.out.println(age);

}

访问该方法时,请求参数名和方法参数名相同,即可完成自动封装。

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

简单对象

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

编写实体类

public class Student {

private int id;

private String name;

private String sex;

// 省略getter/setter/tostring

}

编写控制器方法

// 封装为简单对象类型参数

@RequestMapping("/c1/param2")

public void objParam(Student student){

System.out.println(student);

}

访问该方法时,请求参数名和对象参数的属性名相同,即可完成自动封装。

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

关联对象

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

编写实体类

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

}

编写控制器方法

// 获取关联对象类型参数

@RequestMapping("/c1/param3")

public void objParam2(Student student){

System.out.println(student);

}

访问该方法时,请求参数名和方法参数的属性名相同,即可完成自动封装。

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

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

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

表单提交

id:

姓名:

性别:

住址:

邮编:

简单数据类型集合

1.编写控制器方法

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

@RequestMapping("/c1/param4")

public void listParam(@RequestParam List users){

System.out.println(users);

}

该方式也可以封装为简单数据类型数组:

@RequestMapping("/c1/param5")

public void listParam2(@RequestParam String[] users){

System.out.println(users[0]);

System.out.println(users[1]);

}

请求的参数写法

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

Map集合

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

编写实体类

public class AddressMap {

private Map address;

// 省略getter/setter/tostring

}

编写控制器方法

// 对象中包含map属性

@RequestMapping("/c1/param7")

public void mapParam3(AddressMap addressMap){

System.out.println(addressMap.getAddress());

}

请求的参数写法

参数的写法为: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就无法解析参数。此时需要自定义参数类型转换器。

定义类型转换器类,实现Converter接口

// 类型转换器必须实现Converter接口,两个泛型代表转换前的类型,转换后的类型

public class DateConverter implements Converter {

/**

* 转换方法

* @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;

}

}

0注册类型转换器对象

此时再访问http://localhost:8080/c1/param9?birthday=2025-01-01时,SpringMVC即可将请求参数封装为Date类型的参数。

编码过滤器

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

编写jsp表单

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

编码过滤器

姓名:

编写控制器方法

@RequestMapping("/cn/code")

public void code(String username){

System.out.println(username);

}

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

encFilter

org.springframework.web.filter.CharacterEncodingFilter

encoding

utf-8

encFilter

/*

Servlet原生对象

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

// 使用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视图。

返回值为void

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

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

编写控制器方法

// 路径是helloMVC,方法执行完后会跳转到/helloMVC.jsp

@RequestMapping("/helloMVC")

public void helloMVC(){

System.out.println("hello SpringMVC!");

}

编写helloMVC.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

MVC

欢迎来到SpringMVC

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 model = modelAndView.getModel();

// 3.使用Model对象向request域设置数据

model.put("name","jjy");

// 4.使用View对象设置跳转的路径为/baizhan.jsp

modelAndView.setViewName("jjy");

return modelAndView;

}

编写jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java"%>

中北

你好!${requestScope.name}

修改web.xml命名空间,让jsp页面默认支持el表达式

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

向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页面

<%@ page contentType="text/html;charset=UTF-8" language="java"%>

中北

你好!${requestScope.name}

地址是!${sessionScope.address}

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

向context域设置数据

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

编写控制器方法

@RequestMapping("/c2/hello7")

public String setContextModel(HttpSession session){

ServletContext servletContext = session.getServletContext();

servletContext.setAttribute("age",10);

return "baizhan";

}

2.编写jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java"%>

中北

你好!${requestScope.name}

地址是!${sessionScope.address}

年纪是!${applicationScope.age}

请求转发&重定向

之前的案例,我们发现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风格支持

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请求改为指定的请求方式,发送给控制器的方法。

用法如下:

在web.xml中配置过滤器

httpMethodFilter

org.springframework.web.filter.HiddenHttpMethodFilter

httpMethodFilter

/*

编写控制器方法

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

}

}

在jsp中编写表单

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

DELETE、PUT提交

<%-- 提交DELETE、PUT请求,表单必须提交方式为post --%>

<%-- 表单中有一个隐藏域,name值为_method,value值为提交方式 --%>


@ResponseBody

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

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

编写jsp页面,发送ajax请求

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

ajax请求

姓名:

性别:

由于jsp页面中引入jQuery的js文件,而SpringMVC会拦截所有资源,造成jquery.js失效,需要在SpringMVC核心配置文件中放行静态资源。

编写结果实体类,该实体类会封装一个请求的结果

// 请求的结果对象

public class Result {

private boolean flag; // 请求是否成功

private String message; // 请求提示信息

// 省略getter/setter/构造方法

}

编写控制器

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

}

}

SpringMVC会将Result对象转为JSON格式写入输出流,而SpringMVC默认使用的JSON转换器是jackson,需要在pom中添加jackson依赖。

com.fasterxml.jackson.core

jackson-core

2.9.0

com.fasterxml.jackson.core

jackson-databind

2.9.0

com.fasterxml.jackson.core

jackson-annotations

2.9.0

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

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

修改SpringMVC核心配置文件:

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

修改SpringMVC核心配置文件:

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

修改web.xml:

default

*.jpg

default

*.css

default

*.js

default

*.png

@RequestBody

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

位置:写在方法参数前

AJAX请求发送JSON格式的参数

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

ajax请求


姓名:

性别:

编写控制器

@PostMapping("/addStudent2")

@ResponseBody

public Result addStudent2(@RequestBody Student student) {

System.out.println(student);

// 返回添加结果

Result result = new Result(true, "添加学生成功!");

return result;

}

SpringMVC上传

原生方式上传

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

编写上传表单

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

上传

文件上传

<%-- 上传表单的提交方式必须是post --%>

<%-- enctype属性为multipart/form-data,意思是不对表单数据进行编码 --%>

<%-- 文件上传控件,类型是file,必须要有name属性--%>

选择文件:

接收请求体数据:

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

}

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

引入文件上传依赖:

org.apache.commons

commons-fileupload2-jakarta

2.0.0-M1

编写控制器接收上传请求,控制器进行三步操作:

创建文件夹,存放上传文件。

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

将文件数据写入文件夹。

@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 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对象,从而省略原生上传中分析请求体的步骤。

在SpringMVC核心配置文件配置文件解析器

在web.xml中进行上传配置

dispatcherServlet

org.springframework.web.servlet.DispatcherServlet

contextConfigLocation

classpath:springmvc.xml

104857600

418018841

0

创建JSP表单

编写控制器接收上传请求

// 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支持一次性上传多个文件,写法如下:

创建JSP表单

用户名:

文件1:

文件2:

编写控制器接收上传请求

// 处理多文件上传,参数类型为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";

}

异步上传

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

编写JSP页面,引入jQuery和jQuery表单上传工具jquery.form.js

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

上传

文件上传

<%-- 按钮类型不能是submit,否则会刷新页面 --%>

<%-- 上传头像后展示的位置 --%>

编写控制器接收异步上传请求

@RequestMapping("/fileUpload4")

@ResponseBody

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

}

跨服务器上传

由于文件占据磁盘空间较大,在实际开发中往往会将文件上传到其他服务器中,此时需要使用跨服务器上传文件。

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

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

readonly

false

修改tomcat的conf/server.xml文件,修改tomcat端口,修改完开启tomcat服务器

编写JSP上传表单

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

上传

文件上传

添加跨服上传依赖

com.sun.jersey

jersey-core

1.18.1

com.sun.jersey

jersey-client

1.18.1

创建控制器方法,该方法在接受到上传请求后将文件保存到其他服务器上。

@RequestMapping("/fileUpload5")

@ResponseBody

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

}

查询可下载的文件

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

编写控制器方法,查询所有可下载的文件,并跳转到下载页面

// 查询可下载的文件

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

}

添加JSTL依赖

jakarta.servlet.jsp.jstl

jakarta.servlet.jsp.jstl-api

3.0.0

org.glassfish.web

jakarta.servlet.jsp.jstl

2.0.0

编写下载页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

下载

文件下载

<%-- 遍历文件集合 --%>

${file}

下载文件

// 文件下载

@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提供了异常处理器进行异常处理,这样可以提高开发效率。

处理单个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";

}

}

异常页面error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

出错了!

ERROR 发生异常!${msg}

异常页面error2.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

出错了!

ERROR2 发生严重异常!${msg}

全局异常处理

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

编写另一个有异常的控制器类

@Controller

@RequestMapping("/c10")

public class MyController10 {

@RequestMapping("/t1")

public String t1(){

String str = null;

str.length();

return "index";

}

}

编写全局异常处理器类

// 全局异常处理器类,需要添加@ControllerAdvice

@ControllerAdvice

public 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容器中

@Component

public 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的拦截器(Interceptor)也是AOP思想的一种实现方式。它与Servlet的过滤器(Filter)功能类似,主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。

拦截器和过滤器的区别

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

拦截器的使用

接下来我们使用SpringMVC拦截器

创建被拦截的方法

@RequestMapping("/m1")

public String m1(){

System.out.println("控制器方法");

return "result";

}

创建拦截器类,该类实现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);

}

}

编写JSP页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

结果

name:${requestScope.name}

age:${requestScope.age}

在SpringMVC核心配置文件中配置拦截器

全局拦截器

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

拦截器链与执行顺序

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

编写拦截器类

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后");

}

}

配置拦截器链

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

结论:

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

过滤敏感词

接下来我们编写一个拦截器案例,需求如下:

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

编写控制器方法

@RequestMapping("/t3")

public String t3(Model model){

model.addAttribute("name","大笨蛋");

return "result";

}

编写敏感词拦截器

// 敏感词拦截器

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 model = modelAndView.getModel();

Set> entries = model.entrySet();

// 遍历Model,将Model值和敏感词列表遍历比对,如果Model值包含敏感词,则替换

for (Map.Entry 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);

}

}

}

}

}

配置拦截器

SpringMVC跨域请求

同源策略

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

哪些不受同源策略限制:

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

结果:

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

http://localhost:8080/c12/t1 能发送异步请求;http://127.0.0.1:8080/c12/t1 由于同源无法发送异步请求,报以下异常:

接收跨域请求

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

@RequestMapping("/t1")

@ResponseBody

// 如果请求从http://localhost:8080发出,则允许跨域访问

@CrossOrigin("http://localhost:8080")

public String t1(){

System.out.println("测试跨域请求");

return "success";

}

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力

相关创意

一加 12 参数规格
注册送365

一加 12 参数规格

📅 07-07 👁️ 4812
为什么你记不住做过的梦?睡觉时,脑神经会偷偷删除记忆
客观评价七彩虹GTX1650灵动鲨显卡是否值得买?七彩虹GTX1650灵动鲨优缺点点评
DNF中冰龙副本如何进入?上冰龙的方法是什么?
365bet体育在线官

DNF中冰龙副本如何进入?上冰龙的方法是什么?

📅 06-30 👁️ 8137
【問題】靈犀一動是不是不用在該地點打坐??? @天涯明月刀 哈啦板
为什么说,歼-20战机的问世,拯救了所有中国人的性命和尊严?
为什么交易高手不愿意讨论行情和交易方法?
捷尼赛思-G90
365bet体育在线官

捷尼赛思-G90

📅 07-16 👁️ 1989