2-处理器方法

已经学了创建控制器类,控制器类里的一个方法对应一个请求或者多个请求。使用@RequestMapping来进行配置。

下面具体来讲一下处理器方法。

@RequestMapping

@RequestMapping注解可以放在类上面,也可以放在方法上。但是含义是完全不一样的。

放在类上面

放在类上面,其实很简单。只有一种情况,就是当这个类中的所有方法对应的请求都有公共部分时,就可以把该公共部分提取出来放到类上。

表示类中的响应请求的方法都是以该地址作为父路径。

例如MyController类中有doSome()方法和doOther()方法,他们对应的请求分别是/test/some.do/test/other.do,那么就可以把/test抽取出来,放在类定义上面,也就是@RquestMapper(value="/test"),然后在方法上只使用@RequestMapping(value="/some.do")@RequestMapping(value="/other.do")即可。代码更简便,同时也便于修改。

放在方法上面

之前的例子已经用过了。@RequestMapping注解放在方法上。但只用到了value属性。@RequestMapping注解还有其他的属性。

下面来讲@RequestMapping的属性

value

用来指定请求的实际地址,指定的地址是URI格式。前端代码的请求地址要和这个值保持一致。

注意:只能一个方法对应多个请求,而不能一个请求对应多个方法,那样的话会报错。

前端代码中不能加/

method

指定请求的方式,例如GET、POST、PUT等

它的值是RequestMethod类的枚举值。

image-20201208131258509

例如:@RequestMapping(method=RequestMethod.GET)

如果不指定请求方式,那么没有限制,任意一种请求方式都可以。

consumes

这个参数指定处理请求的提交内容类型Content-Type,例如application/json,application/html

也是是说,如果这个请求没有包含这个参数指定的提交内容类型,那么就不会处理。

produces

指定返回的内容类型,只有当请求头中的Accpet的值包含这个参数指定的值,才会处理。

同时其实也暗含了这个方法会返回指定类型的值。

params

指定request中必须包含某些指定参数值,才会让该方法处理。

格式为params="key=value,.."

headers

指定request中必须包含某些指定的header值,才让该方法处理请求。

格式为:headers="key=value,.."

处理器方法参数

处理器方法可以包含以下四类参数,这些参数会在调用时由框架自动赋值,只要在方法形参中定义好,我们可以直接在方法内使用

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

例如想要使用前两个参数,直接在方法签名的形参列表定义即可:public ModelAndView doSome(HttpServletRequest request, HttpServletResponse response)

逐个参数接受

接受请求中所携带的请求参数,可以逐个参数接受。框架会自动进行类型转换。

只需要保证,前端代码的请求参数名和处理方法的参数名相同即可。

例如:
前端代码:

1
2
3
4
5
<form action="/some.do" method="post" >
姓名:<input type="text" name="name" ><br>
年龄:<input type="text" name="age" ><br>
<input type="submit" value="提交信息">
</form>

那么处理方法中的参数名和前端提交的参数名保持一致

1
2
3
4
5
6
7
8
9
@RequestMapping(value = "/some.do")
public ModelAndView doSome(String name, int age) {
ModelAndView mv = new ModelAndView();
mv.addObject("name", name);
mv.addObject("age", age);
mv.setViewName("hello");
return mv;

}

运行结果:

image-20201208134551362

可以看到参数正常接收到了。

POST方式解决乱码问题

当使用POST方式提交值为中文的参数时,会出现乱码。

我们可以使用以往的解决方法,就是调用请求对象request.setCharacterEncoding(),设为UTF-8。但是这样太麻烦,需要重复编写。

可以使用过滤器,来统一设置POST请求的编码方式。

可以自定义过滤器类,也可以使用SpringMVC提供的过滤器类CharacterEncodingFilter类。

在web.xml配置过滤器

需要在配置文件web.xml中注册过滤器。

该过滤器类有三个成员变量:

  • encoding:字符串类型,指定项目使用的字符编码,一般用utf-8
  • forceRequestEncoding:布尔类型,指定请求对象request是否使用字符集encoding,默认是false,需要改为true
  • forceResponseEncoding布尔类型,指定响应对象response是否使用字符集encoding,默认是false,需要改为true

同时设置中的值为/*,表示强制所有的请求都必须通过该过滤器。

即:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

@RequestParam

当请求URL的请求参数名和处理方法的形参名不相同时,使用@RequestParam注解放在形参名前,用来指定请求提交的参数名。

该注解有三个属性:

  • value:请求参数名
  • required:指定该参数是否是必需,默认为true,即请求中必须包含该参数,如果没有,会抛出异常
  • defaultValue:默认值,如果设置了该值,那么required自动设为false。无论你是否手动配置了required。

一般将required设为false。

例子:
修改前端代码:

1
2
3
4
5
<form action="some.do" method="post" >
姓名:<input type="text" name="myName" ><br>
年龄:<input type="text" name="myAage" ><br>
<input type="submit" value="提交信息">
</form>

那么方法签名改为:

1
2
public ModelAndView doSome(@RequestParam("myName", required = false)String name,
@RequestParam("myAge", required = false) int age)

对象参数接受

若把处理器方法的参数定义为一个Java对象,那么只要保证请求的参数名和这个对象的属性名相同即可。

与逐个参数接受差不多。

只不过变成了和Java对象的属性名相同。

适合用于请求参数比较多的情况。

这种方法在实际中应用的比较多。

例如:

前端代码:

1
2
3
4
5
<form action="some.do" method="post" >
姓名:<input type="text" name="name" ><br>
年龄:<input type="text" name="age" ><br>
<input type="submit" value="提交信息">
</form>

创建一个类Student

1
2
3
4
5
6
public class Student {

private String name;
private Integer age;
//set和get方法,构造器方法
}

那么在处理器方法中:

1
2
3
4
5
6
7
@RequestMapping(value = "/some.do")
public ModelAndView doSome(Student student) {
ModelAndView mv = new ModelAndView();
mv.addObject("myStudent", student);
mv.setViewName("hello");
return mv;
}

hello.jsp代码:

1
2
3
4
5
6
7
8
9
10
11
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>欢迎界面</title>
</head>
<body>
<h1>欢迎使用SpringMVC开发Web应用</h1>
<h2>姓名是:${myStudent.name}</h2>
<h2>年龄是:${myStudent.age}</h2>
</body>
</html>

处理器方法返回值

常见的返回值有四种类型:

  • ModelAndView
  • String
  • void,无返回值
  • 自定义类型的对象

ModelAndView

如果处理器方法处理完以后,需要跳转到其他资源,并且要在跳转的资源间传递数据,那么处理器方法返回ModelAndView比较好的。

但如果,处理器方法只是进行跳转不传递数据,或者只传递数据并不向任何资源跳转(例如异步请求ajax),则此时不应该返回ModelAndView。

String

如果处理器方法执行完后,只进行资源跳转。那么可以返回String类型。返回的字符串可以指定逻辑视图名,视图解析器会将其解析,转换为物理视图地址(就是html、jsp等资源文件)。

如果跳转的资源是内部资源,那么因为已经在springmvc.xml文件中注册了视图解析器。所以直接返回方法名即可,视图解析器会将路径前缀和后缀拓展名进行拼接。

例如:

1
2
3
4
@RequestMapping(value = "/some.do")
public String doSome() {
return "hello";
}

发起请求就可以跳转到hello.jsp文件。

void

如果处理器方法不需要跳转到其他资源,可以让处理器方法返回void。

例如对于ajax异步请求的响应。

Object

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

返回对象,需要使用@ResponseBody注解,将转换后的JSON数据放入响应体中。通常是用来响应AJAX请求的。

使用步骤

导入pom依赖

因为返回Object数据,一般是将数据转换为JSON对象后传递给浏览器页面的。由Object转换为JSON,是由Jackson工具库完成的。需要导入Jackson的相关jar包。

声明注解驱动

将Object数据转换为JSON数据,需要由消息转换器HttpMessageConverter完成。转换器的开启,需要声明注解驱动。

需要在springmvc配置文件中声明注解驱动,加上:<mvc:annotation-driven />。

消息转换器的功能:完成Java对象到json、xml、text和二进制等数据格式的转换。或反过来的转换。

HttpMessageConverter

HttpMessageConverter是一个接口,负责将请求信息转换为一个对象(类型为T),将对象(类型为T)输出为响应信息。

定义了Java对象转换为json、xml等数据格式的方法。

该接口中有四个方法:

  • boolean canRead(Class<?> clazz, MediaType mediaType):指定转换器可以读取的对象类型,即转换器是否可以将请求信息转换为clazz类型的对象。同时指定支持MIME类型(text/html, application/json)。

    简单来说,这个 方法是用来检查输入流的数据格式是否可以转换为对应的Java对象

  • boolean canWrite(Class<?> clazz, MediaType mediaType):指定转换器是否可以将clazz类型的对象写入到响应流中,响应流支持的媒体类型在MediaType中定义

    简单来说,这个 方法是用来检查处理器方法的返回值是否可以转换为mediaType中的数据格式

  • T read(Class<?> clazz, HttpInputMessage inputMessage):将请求信息流转换为T类型的对象

  • T write(Class<?> clazz, HttpOutputMessage outputMessage):将T类型的对象写入到响应流中,同时指定响应媒体类型为contentType。就是将处理器方法返回的对象转换成指定数据格式。如果是json,就会使用Jackson的工具库来进行转换。

    其中MdeiaType用来表示数据格式,例如json、xml等

这个接口有很多实现类,这些实现类分别完成了Java对象到json、xml等数据格式的转换。

当Spring容器进行初始化时,在读取<mvc:annotation-driven />时,默认会创建七个HttpMessageConverter接口的实现类对象。

这七个实现类对象分别是:

  • ByteArrayHttpMessageConverter:负责读取二进制格式数据和写出二进制格式数据
  • StringHttpMessageConverter:负责读取字符串格式数据和写出字符串格式数据
  • ResourceHttpMessageConverter:读取资源文件和写出资源文件数据
  • SourceHttpMessageConverter:能够读、写来自HTTP请求与相应的javax.xml.transform.Source,支持DOMSource,SAXSource和StreamSource的XML格式
  • AllEncodingpassingFormHttpMessageConverter:负责处理表单数据
  • Jaxb2RootElementHttpMessageConverter:使用JAXB负责读取和写入xml格式的数据
  • MapperJackson2HttpMessageConverter:负责读取和写入json格式的数据。使用Jackson的ObjectMapper读写json数据。操作Object类型数据。可读取application/json,相应媒体类型为application/json

其中加粗的两个比较常用。

添加@ResponseBody注解

将该注解放在对应处理器方法上面。通过HttpServletResponse相应请求,返回数据的。

和其他注解没有顺序先后。

例子

输出自定义对象

向浏览器返回一个自定义的对象Student,转换为json格式。

处理器方法:

1
2
3
4
5
6
7
8
@RequestMapping(value = "/returnStudent.do")
@ResponseBody
public Student doStudent() {
Student student = new Student();
student.setName("赵大宝");
student.setAge(21);
return student;
}

异步请求代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
$(function (){
$("#btn").click(function () {
$.ajax({
url:"returnStudent.do",
type:"get",
dataType:"json",
success:function (data) {
alert(data);
}
})

})
})

运行结果:
image-20201217132846957

返回对象Json数据的处理流程:

  • 框架会将Student类型,调用框架中的ArrayList中每个实现类对象的canWrite方法,检查哪个HttpMessageConverter接口的实现类可以处理Student类型的数据,找到MapperJackson2HttpMessageConverter实现类
  • 框架调用实现类的write方法,把student对象转换为json数据,调用Jackson的ObjectMapper
  • 框架调用@ResponseBody把结果输出到浏览器,ajax请求处理完毕。
输出List集合

将List集合转换为JSON对象数组输出。

和上个例子差不多

处理器方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RequestMapping(value = "/returnList.do")
@ResponseBody
public List<Student> doList() {
List<Student> list = new ArrayList<>();
Student student = new Student();
student.setName("赵大宝");
student.setAge(21);
list.add(student);
Student student1 = new Student();
student1.setName("李二狗");
student1.setAge(22);
list.add(student1);
return list;
}

JS代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$(function (){
$("#btn").click(function () {
$.ajax({
url:"returnList.do",
type:"get",
dataType:"json",
success:function (data) {
$.each(data, function (i, n){
alert(n.name + " " + n.age);
})
}
})

})
})

运行结果:

image-20201217134624351 image-20201217134610237
返回文本数据

处理器方法返回的是String类型,这里String表示数据,而不是视图。

区分返回值String是数据还是视图的方法:

如果有注解@ResponseBody,那么就是数据;没有,就是视图

返回String时,默认使用"text/plain;charset=ISO-8859-1"作为contentType,中文会出现乱码。(虽然在web.xml设置了拦截器,强制设置编码方式,但好像对这个没有效果)

解决方案:在@RequestMapping注解中加上属性produces = “text/plain;charset=utf-8”,来指定新的contentType

处理器方法:

1
2
3
4
5
@RequestMapping(value = "/returnStringData.do", produces = "text/plain;charset=utf-8")
@ResponseBody
public String doStringData() {
return "返回的是文本数据";
}

JS代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
$(function (){
$("#btn").click(function () {
$.ajax({
url:"returnStringData.do",
type:"get",
// dataType:"json",
success:function (data) {
alert(data);
}
})

})
})

运行结果:
image-20201217141214840


2-处理器方法
https://zhaoquaner.github.io/2022/05/11/Spring-MVC/2-处理器方法/
更新于
2022年5月22日
许可协议