【补】2. 为什么要使用SpringMVC框架
在原生的Java EE技术体系中,处理用户请求的是Servlet
组件,通常情况下,每个Servlet
组件处理1种请求,例如“用户注册”的请求可能由UserRegServlet
来处理,“用户登录”的请求可能由UserLoginServlet
来处理……在比较复杂的业务系统中,用户提交的请求的种类可能特别多,就会导致Servlet
组件的数量也特别多!进而导致代码的管理难度很大,同时,在项目运行时,诸多的Servlet
组件也会占用较多的内存空间……
在SpringMVC框架的设计中,就解决了以上问题,它使用1个DispatcherServlet
的组件,用于接收所有请求(当然,也可以配置为某些特定的请求,例如配置为仅处理*.do
的请求),以此减少Servlet
组件的数量!
原本Servlet
是用于处理请求的,而在SpringMVC框架中,DispatcherServlet
的主要作用是接收到请求后,分发到具体处理请求的Controller
组件,其本身并不处理请求!而SpringMVC中的每个Controller
组件都可以有若干个处理请求的方法,也就是每个Controller
组件都可以处理若干种请求,所以,即使项目很复杂,请求的种类很多,但是,Controller
组件的数量并不会太多!
DispatcherServlet
与Controller
的关系就好比银行大厅的取号机和业务柜台的关系。
SpringMVC框架的使用比原生的Java EE更加简单!
1. 显示页面
在项目的pom.xml中添加thymeleaf
和thymeleaf-spring5
这2个依赖:
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf-spring5 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
注意:同一个groupId
下,各artifactId
不同,但是,如果version
的编号规则是一样的,并且,在同一个项目中需要使用不同artifactId
对应的依赖,必须使用相同的version
!
package cn.tedu.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
@ComponentScan("cn.tedu.spring")
public class SpringMvcConfigurer implements WebMvcConfigurer {
private String characterEncoding = "utf-8";
@Bean
public ViewResolver configViewResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setCharacterEncoding(characterEncoding);
templateResolver.setTemplateMode("HTML");
templateResolver.setCacheable(false);
templateResolver.setPrefix("/templates/");
templateResolver.setSuffix(".html");
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setCharacterEncoding(characterEncoding);
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
}
2. 接收客户端提交的请求参数
2.1. 使用HttpServletRequest接收请求参数
在处理请求的方法的参数列表中,添加HttpServletRequest
类型的参数,在处理过程中,调用参数对象的getParameter()
方法即可获取请求参数:
@RequestMapping("handle_reg.do")
@ResponseBody
public String handleReg(HttpServletRequest request) {
System.out.println("HelloController.handleReg()");
String username = request.getParameter("username");
String password = request.getParameter("password");
Integer age = Integer.valueOf(request.getParameter("age"));
String phone = request.getParameter("phone");
String email = request.getParameter("email");
System.out.println("username=" username);
System.out.println("password=" password);
System.out.println("age=" age);
System.out.println("phone=" phone);
System.out.println("email=" email);
return "OK."; // 暂时不关心如何响应,所以使用@ResponseBody返回某个字符串,避免程序报错
}
2.2. 将请求参数直接声明为处理请求的方法的参数
可以将客户端提交的请求参数直接声明为处理请求的方法的参数,例如:
代码语言:javascript复制@RequestMapping("handle_reg.do")
@ResponseBody
public String handleReg(String username, String password, Integer age,
String phone, String email) {
System.out.println("HelloController.handleReg()");
System.out.println("[2] username=" username);
System.out.println("[2] password=" password);
System.out.println("[2] age=" age);
System.out.println("[2] phone=" phone);
System.out.println("[2] email=" email);
return "OK."; // 暂时不关心如何响应,所以使用@ResponseBody返回某个字符串,避免程序报错
}
在声明参数时,需要保证名称的一致,即“客户端提交的请求参数的名称”与“处理请求的方法的参数名称”必须是一致的!如果服务器端声明的参数,在客户端提交请求时并没有提交,则服务器端声明的参数值将是null
。
在处理请求的方法中,声明参数时,可以将参数的类型声明为所期望的类型,例如以上代码中,就将age
声明为Integer
类型,当然,前提是客户端最终提交的数据是可以转换为Integer
,如果客户端提交的数据根本就无法转换,则会报错。
使用这种虽然简单、方便,但是,不适用于请求参数过多的应用场景!
2.3. 使用封装的参数对象接收请求参数
可以将客户端将要提交的各请求参数封装到一个自定义的数据类型中,例如:
代码语言:javascript复制package cn.tedu.spring;
public class User {
private String username;
private String password;
private Integer age;
private String phone;
private String email;
// Getters & Setters
// toString()
}
然后,将以上自定义的数据类型添加到处理请求的方法的参数列表中即可:
代码语言:javascript复制@RequestMapping("handle_reg.do")
@ResponseBody
public String handleReg(User user) {
System.out.println("HelloController.handleReg()");
System.out.println(user);
return "OK."; // 暂时不关心如何响应,所以使用@ResponseBody返回某个字符串,避免程序报错
}
2.4. 小结
首先,在2.1介绍的使用HttpServletRequest
接收请求参数的做法是不推荐使用的;
如果请求参数的数量较少,并且固定时,应该优先使用2.2介绍的做法;
如果请求参数的数量较多,或以后存在调整的可能性时,应该优先使用2.3介绍的做法。
另外,以上2.2和2.3的做法可以同时存在!
3. 向模版页面转发数据
当需要向Thymeleaf模版页面转发数据时,应该先在处理请求的方法的参数列表中添加ModelMap
类型的参数,并在需要转换数据时,调用ModelMap
参数对象的addAttribute()
方法将数据封装进去:
@RequestMapping("handle_login.do")
public String handleLogin(String username, String password, ModelMap modelMap) {
System.out.println("UserController.handleLogin()");
System.out.println("username=" username);
System.out.println("password=" password);
// 假设root/1234是正确的用户名/密码
// 判断用户名
if ("root".equals(username)) {
// 用户名正确,判断密码
if ("1234".equals(password)) {
// 密码也正确,则登录成功
return "OK";
} else {
// 密码错误
String message = "登录失败!密码错误!";
modelMap.addAttribute("errorMessage", message);
return "error";
}
} else {
// 用户名错误
String message = "登录失败!用户名错误!";
modelMap.addAttribute("errorMessage", message);
return "error";
}
}
后续,在Thymeleaf模版页面中,使用Thymeleaf表达式即可显示此前封装的数据:
代码语言:javascript复制<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>操作错误</title>
</head>
<body>
<h1>操作错误:<span th:text="${errorMessage}"></span></h1>
</body>
</html>