day61(接受客户端请求,@RequestMapping注解,响应正文,接受请求参数,POJO)
1. 接收客户端的请求
1.创建Spring MVC工程
-
请参考 http://doc.canglaoshi.org/doc/idea_tomcat/index.html创建项目,首次练习的项目名称请使用springmvc01。
-
【案例目标】开发使用Spring MVC框架的项目,将其部署到Tomcat,最终,部署此项目启动Tomcat,用户在浏览器中输入指定的URL提交请求后,项目可以对此进行简单的响应
-
在pom.xml中添加spring-webmvc依赖项:
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.14version>
dependency>-
提示:如果后续运行时提示不可识别Servlet相关类,则补充添加以下依赖项:
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency> -
以上代码中的provided表示此依赖不会参与测试或部署,因为当Web项目部署到Tomcat中后,Tomcat环境会包含此依赖项
-
-
准备2个配置类
-
一个是Spring框架的配置类:
package cn.tedu.springmvc.config;
import org.springframework.context.annotation.Configuration; -
另一个是Spring框架的配置类:
package cn.tedu.springmvc.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-
需要创建项目的初始化类,此类必须继承自AbstractAnnotationConfigDispatcherServletInitializer,并在此类中重写父类的3个抽象方法,返回正确的值(各方法的意义请参见以下代码中的注释):
package cn.tedu.springmvc;
import cn.tedu.springmvc.config.SpringConfig;
import cn.tedu.springmvc.config.SpringMvcConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;/**
* Spring MVC项目的初始化类
*/
public class SpringMvcInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer { -
最后,创建控制器类,用于接收客户端的某个请求,并简单的响应结果:
package cn.tedu.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; -
全部完成后,启动项目,会自动打开浏览器并显示主页,在主页的地址栏URL上补充/login.do即可实现访问,并看到结果
-
在启动过程中,你还可以在IntelliJ IDEA的控制台中看到控制器类的构造方法中输出的内容
-
关于以上案例:
-
当启动Tomcat时,会自动将项目打包并部署到Tomcat,通过自动打开的浏览器中的URL即可访问主页,在URL中有很长一段是例如 springmvc01_war_explored 这一段是不可以删除的,其它的各路径必须补充在其之后,例如 /login.do 就必须在此之后
-
当启动Tomcat时,项目一旦部署成功,就会自动创建并加载AbstractAnnotationConfigDispatcherServletInitializer的子类,即当前项目中自定义的SpringMvcInitialier,无论这个类放在哪个包中,都会自动创建并加载
-
由于会自动调用这个类中所有方法,所以会将Spring MVC框架处理的请求路径设置为*.do,并执行对 cn.tedu.springmvc 的组件扫描,进而会创建 UserController 的对象
-
由于在 UserController 中配置的方法使用了 @RequestMapping("/login.do"),则此时还将此方法与/login.do进行了绑定,以至于后续随时访问/login.do时都会执行此方法
-
-
关于以上案例的注意事项:
-
注意:组件扫描必须配置在Spring MVC的配置类中
-
注意:控制器类上的注解必须是@Controller,不可以是@Component、@Service、@Repository
-
各注解的作用并不是注解自身决定的,而是运行环境或框架决定的,在Spring框架中,这4个注解是完全等效的,但是,在部分版本的Spring MVC框架中并不相同
-
-
注意:方法返回的值应该是ASCII码字符,不推荐使用中文、中文标点符号等非ASCII码字符,否则可能显示为乱码
-
某些版本的Spring MVC默认的字符编码是ISO-8859-1,只支持ASCII字符
-
乱码的问题暂不解决
-
-
2. @RequestMapping注解
-
@RequestMapping注解的主要作用是配置请求路径与处理请求的方法的映射关系,例如将此注解添加在控制器中某个方法之前:
// http://localhost:8080/springmvc01_war_exploded/login.do-
注意:配置的请求路径值必须是初始化类getServletMapping()方法返回值可以匹配的,例如方法返回值是 *.do,则配置的请求路径必须以.do作为后缀
-
-
除了方法之前,此注解还可以添加在控制器类之前,例如:
-
在类上添加了此注解并配置路径后,每个方法实际映射到的请求路径都是“类上的配置的路径 + 方法上的配置的路径”,例如/user/login.do
-
通常,在开发实践中,推荐为每一个控制器类都配置此注解,以指定某个URL前缀
-
在使用@RequestMapping配置路径时,无意义的/ 会被忽略,例如在类上配置为 /user/,在方法上配置为 /login.do,则拼接的结果会是/user//login.do,实际有效值为 /user/login.do
-
必要的 / 会被补充,例如在类上配置为user,在方法上配置为login.do,实际有效值仍是 /user/login.do
-
在开发实践中,应该保持统一风格,例如:无论在类上,还是在方法上,配置的请求路径均以 / 作为第1个字符(某些特殊的URL配置例外)
-
-
在@RequestMapping还可以配置:
-
method:请求方式
-
headers:请求头
-
params:请求参数
-
consumes:请求文档类型
-
produces:响应文档类型
-
-
例如,在@RequestMapping注解中,增加配置method属性,可以限制客户端的请求方式:
-
如果按照以上代码,则/login.do路径只能通过POST方式发起请求才可以被正确的处理,如果使用其它请求方式(例如GET),则会导致HTTP的405错误。
-
-
在部分版本的Spring MVC框架中,响应结果不会自动添加响应头的文档类型,可通过配置@RequestMapping的produces属性显式的添加,以解决中文乱码问题
-
可以在类上进行此配置,则类中每个处理请求的方法均应用此配置
-
-
如果没有配置method属性,则表示可以使用任何请求方式,包括:GET,HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRAC
-
另外,Spring MVC框架还提供了@RequestMapping的相关注解,例如:
-
@GetMapping –
-
@PostMapping –
-
@PutMapping –
-
@DeleteMapping 等等
-
-
@GetMapping等注解是已经限制了请求方式的注解
-
以@GetMapping为例,限制了请求方式必须是GET,除此以外,使用方式与@RequestMapping完全相同
-
-
在开发实践中,在类的上方肯定使用@RequestMapping(其它的@XxxMapping不可以加在类上),方法上一般都使用@GetMapping、@PostMapping等注解,除非在极特殊的情况下,某些请求同时允许多种请求方式,才会在方法上使用@RequestMapping
2.小结
-
使用@RequestMapping的主要作用是配置请求路径
-
@RequestMapping可以添加在类上,也可以添加在方法上,同时配置时,最终的URL是由类上的与方法上的配置值组合的
-
当需要限制请求方式为某1种时,应该在处理请求的方法上使用@GetMapping、@PostMapping等注解
-
在开发实践中,通常:
-
在类上使用@RequestMapping,配置请求路径,并配置produces以指定响应的文档类型
-
在方法上使用@GetMapping、@PostMapping等限制了请求类型的注解,配置请求路径
-
3.响应正文
1.概念
-
@ResponseBody注解表示:响应正文
-
一旦配置为“响应正文” ,则处理请求的方法的返回值就会直接响应到客户端去
-
如果没有配置为“响应正文” ,则处理请求的方法的返回值表示“视图组件的名称” ,当方法返回后,服务器端并不会直接响应,而是根据“视图组件的名称”在服务器端找到对应的视图组件,并处理,最后,将处理后的视图响应到客户端去,这不是前后端分离的做法
2.用法
-
@ResponseBody注解可以添加在方法上,则仅作用于当前方法,也可以添加在类上,则作用于当前类的所有方法
-
在Spring MVC框架中,还提供了@RestController注解,它同时具有@Controller和@ResponseBody注解的效果,所以,在响应正文的控制器上,只需要使用@RestController即可,不必再添加@Controller和@ResponseBody注解。
-
Spring MVC内置了一系列的转换器(Converter),用于将方法的返回值转换为响应到客户端的数据(并补充其它必要数据),并且,SpringMVC会根据方法的返回值不同,自动选取某个转换器,例如,当方法的返回值是String时,会自动使用StringHttpMessageConverter,这个转换器的特点就是直接将方法返回的字符串作为响应的正文,并且,在许多版本的Spring MVC框架中,其默认的响应文档的字符集是ISO-8859-1,所以在在许多版本的Spring MVC中响应String正文时默认不支持非ASCII字符(例如中文)
3.响应JSON格式的正文
-
在开发实践中,不会使用String作为处理请求的方法的返回值类型,主要是因为普通的字符串不足以清楚的表现多项数据,通常建议使用XML或JSON语法来组织数据
-
主流的做法是向客户端响应JSON格式的字符串,需要在项目中添加jackson-databind的依赖项:
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.12.3version>
dependency> -
以上jackson-databind依赖项中也有一个转换器,当SpringMVC调用的处理请求的方法的返回值是Spring MVC没有匹配的默认转换器时,会自动使用jackson-databind的转换器,而jackson-databind转换器就会解析方法的返回值,并将其处理为JSON格式的字符串,在响应头中将Content-Type设置为application/json
-
注意:在Spring MVC项目中,还需要在Spring MVC的配置类上添加@EnableWebMvc注解,否则响应时将导致出现HTTP的406错误。
-
示例代码(返回值类型):
public class UserVO {
private String username;
private String password;
private String email;
// 请自行补充以上3个属性的Setter & Getter
} -
注意:jackson-databind会自动调用属性的Setter / Getter方法
// http://localhost:8080/springmvc01_war_exploded/user/info.do@GetMapping("/info.do")
public UserVO info() {
UserVO userVO = new UserVO();
userVO.setUsername("chengheng");
userVO.setPassword("1234567890");
userVO.setEmail("chengheng@qq.com");
return userVO;
} -
示例代码(Spring MVC配置类):
4.小结
-
响应正文的定义:将处理请求的方法的返回值作为响应到客户端的正文数据
-
响应正文的做法:
-
在方法上添加@ResponseBody
-
或,在类上添加@ResponseBody
-
或,在类上使用@RestController取代@Controller
-
-
响应正文的格式:响应的结果通常需要包含多项数据,响应1个字符串并不便于表示这些数据,通常响应JSON格式的字符串
-
响应正文的原理:Spring MVC内置了一系列的转换器(Converter),根据处理请求的方法的返回值不同,自动选取某个转换器,将方法的返回值转换为响应到客户端的数据,当方法的返回值没有匹配的默认转换器时,会自动使用jackson-databind的转换器
-
当需要响应JSON格式的正文时,你需要:–
-
添加jackson-databind依赖 –
-
在Spring MVC配置类上添加@EnableWebMvc注解–
-
自定义类,作为处理请求的方法的返回值类型 –
-
类的属性必须添加Setter & Getter –
-
-
使得处理请求的方法是响应正文的
-
4. 接收请求参数
-
在Spring MVC中,当需要接收客户端的请求参数时,只需要将各参数直接声明为处理请求的方法的参数即可,例如:
// /user/reg.do?username=root&password=123456&age=25 -
在声明参数时,你可以将参数声明为你期望的数据类型,Spring会自动的执行类型转换
-
经过网络传输得到的数据,最原始的类型都是String
-
Spring会自动尝试转换类型,通常,类型必须是基础数据类型及其包装类、常用类型(例如String等)、Spring定义的类型等
-
如果自动转换类型失败,会抛出相应的异常
-
-
如果客户端提交的请求中根本没有匹配名称的参数,则以上获取到的值将是null
-
例如:http://localhost/user/login.do
-
-
如果客户端仅提交了参数名称,却没有值,则以上获取到的值将是""(长度为0的字符串)
-
例如:http://localhost/user/login.do?username=&password=&age=
-
-
如果客户端提交了匹配名称的参数,并且值是有效的,则可以获取到值
-
例如:http://localhost/user/login.do?username=admin&password=1234&age=27
-
-
以上名称应该是由服务器端决定的,客户端需要根据以上名称来提交请求参数
-
当有必要的情况下,可以在以上各参数的声明之前添加@RequestParam注解,其作用主要有:
-
配置name属性:客户端将按照此配置的值提交请求参数,而不再是根据方法的参数名称来提交请求参数
-
配置required属性:是否要求客户端必须提交此请求参数,默认为true,如果不提交,则出现400错误,当设置为false时,如果不提交,则服务器端将此参数值视为null
-
配置defaultValue属性:配置此请求参数的默认值,当客户端没有提交此请求参数时,视为此值
-
-
如果需要客户端提交的请求参数较多,可以将这些参数封装为自定义的数据类型,并将自定义的数据类型作为处理方法的参数即可,例如:
public class UserRegDTO {
private String username;
private String password;
private Integer age;
// 生成Setters & Getters
// 生成toString(),为了方便查看数据
} -
在控制器中:
// /user/reg.do?username=root&password=123456&age=25 -
你也可以将多个请求参数区分开来,一部分直接声明为处理请求的方法的参数,另一部分封装起来。
-
理论上来说,由于一个个的声明请求参数更加简单并且直观,所以,当请求参数数量非常少时,应该使用这种做法,当参数较多,或参数数量可能调整(例如需求变化引起的调整),则应该使用封装的数据类型
-
在开发实践中,考虑到需要使用到的其它框架的特性,使用封装的做法更为常见
2.小结
-
你可以将请求参数一个个的声明为处理请求方法的参数,也可以将多个参数封装到一个自定义类中,使用自定义类作为处理请求的方法的参数,Spring MVC框架会自动接收客户端提交的请求参数,并用于调用你编写的处理请求的方法
-
在大部分情况下,推荐使用将参数封装到自定义类的做法
-
你需要保证非String类型的参数是Spring框架可以成功自动转换类型的,或者,对转换失败有进一步的处理,或可以接受转换失败带来的后果
5. 课后阅读
1. 关于POJO
-
POJO:Plain Ordinary Java Object,即:普通Java对象
-
所有用于封装属性的类型都可以统称为POJO
-
常见的POJO的类名后缀有:BO、DO、VO、DTO等,不同的后缀表示不同的意义,例如:
-
VO = Value Object / View Object
-
DTO = Data Transfer Object
-
-
所有POJO类都应该遵循以下规范:
-
实现Serializable接口 –
-
所有属性都是私有的 –
-
所有属性都有规范的Setter & Getter
-
由开发工具生成的,即是规范的
-
-
规范的重写了hashCode()和equals()
-
2个类型相同、所有属性值都相同的对象,equals()应该返回true,否则,返回false
-
-
由开发工具生成的,即是规范的,不同开发工具生成的方法源码可能不同,这不重要
-
-
通常,应该重写toString()输出每个属性的值
-
-
所有POJO类的类名后缀:
-
在一个项目中,哪些情景下使用哪种后缀并没有统一的规定,通常是各项目内部决定
-
注意:在为封装属性的类进行命名时,以上BO、DO、VO、DTO等这些后缀的每一个字母都应该是大写的!
-
参考资料:在《阿里巴巴Java开发手册》中要求POJO类名后缀的每个字母都是大写的
-
【强制】类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO
-
正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
-
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
-
-
-
参考资料:在《阿里巴巴Java开发手册》中要求不允许使用POJO作为类名后缀
-
领域模型命名规约
-
1) 数据对象:xxxDO,xxx 即为数据表名。
-
2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
-
3) 展示对象:xxxVO,xxx 一般为网页名称。
-
4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
-
-
参考资料:在《阿里巴巴Java开发手册》中要求所有POJO类的属性都不要设置默认值
-
定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值。
-
反例:POJO 类的 gmtCreate 默认值为 new Date();但是这个属性在数据提取时并没有置入具 体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。
-
-
-
参考资料:在《阿里巴巴Java开发手册》中提供了使用类名后缀的参考,但不是强制约定
-
分层领域模型规约:
-
DO(Data Object):与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
-
DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
-
BO(Business Object):业务对象。由 Service 层输出的封装业务逻辑的对象。
-
AO(Application Object):应用对象。在 Web 层与 Service 层之间抽象的复用对象模型, 极为贴近展示层,复用度不高。
-
VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
-
-
-
-