Java - SpringMVC 框架详解(二)

目录

      • 3. SpringMVC Helloworld
        • 3.5. 使用Thymeleaf显示页面
      • 4. 接收客户端提交的请求参数
        • 4.1. 准备案例
        • 4.2. 通过HttpServletRequest对象获取请求参数【不推荐】
        • 4.3. 将请求参数声明为处理请求的方法的参数【推荐】
        • 4.4. 使用封装的类型作用处理请求的方法的参数【推荐】
        • 4.5. 小结
      • 5. 转发数据
        • 5.1. 准备工作
        • 5.2. 使用HttpServletRequest封装需要转发的数据【不推荐】
        • 5.3. 使用ModelMap封装需要转发的数据【推荐】
        • 5.4. 使用ModelAndView作为处理请求方法的返回值【不推荐】
      • 6. 重定向
      • ----------------------------------------------
      • 附1:关于封装
      • 附2:转发与重定向

3. SpringMVC Helloworld

3.5. 使用Thymeleaf显示页面

当项目中需要使用Thymeleaf模版时,就需要添加thymeleaf的依赖,并且,由于是应用在SpringMVC项目中,还需要添加thymeleaf-spring4的依赖:

<dependency>
	<groupId>org.thymeleaf</groupId>
	<artifactId>thymeleaf</artifactId>
	<version>3.0.11.RELEASE</version>
</dependency>

<dependency>
	<groupId>org.thymeleaf</groupId>
	<artifactId>thymeleaf-spring4</artifactId>
	<version>3.0.11.RELEASE</version>
</dependency>

以上依赖的备选版本还有:3.0.2,3.0.9。

由于的是Thymeleaf模版技术来显示页面,所以,还应该创建对应的HTML页面,这个页面将是客户端发出请求后,最后能够显示在浏览器中的页面!

可以在src/main/resources下创建web文件夹(该文件夹的名称是自定义的),然后在该文件夹下创建welcome.html文件:

请添加图片描述

创建好HTML文件好,可以自行设计页面的内容。

然后,需要配置ViewResolver(视图解析器),在SpringMVC中,ViewResolver是一个接口,可以使用的实现类的种类较多,对应使用不同的技术呈现页面,甚至使用不同的页面,在Java EE的默认体系下,可以使用InternalResourceViewResolver呈现JSP页面,此次使用的是Thymeleaf模版技术,所以,应该使用ThymeleafViewResolver,需要在spring.xml中进行配置,关于这个类,主要配置thymeleafEnginecharacterEncoding,而thymeleafEngine的值应该是SpringTemplateEngine的对象,所以,还需要在spring.xml中配置这个类,但是,在模版引擎中,还需要配置模版解析器TemplateResolver,此次可用的TemplateResolverClassLoaderTemplateResolver,所以,继续添加配置!完整的配置代码例如:

<!-- 配置模版解析器 -->
<bean id="templateResolver"
	class="org.thymeleaf.templateresolver.ClassLoaderTemplateResolver">
	<!-- 前缀 -->
	<property name="prefix" value="/web/" />
	<!-- 后缀 -->
	<property name="suffix" value=".html" />
	<!-- 字符编码 -->
	<property name="characterEncoding" value="UTF-8" />
	<!-- 模版模式 -->
	<property name="templateMode" value="HTML" />
	<!-- 是否缓存 -->
	<property name="cacheable" value="false" />
</bean>
	
<!-- 配置模版引擎 -->
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
	<!-- 模版解析器 -->
	<property name="templateResolver" ref="templateResolver" />
</bean>
	
<!-- 配置视图解析器:ThymeleafViewResolver -->
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
	<!-- 模版引擎 -->
	<property name="templateEngine" ref="templateEngine" />
	<!-- 字符编码 -->
	<property name="characterEncoding" value="UTF-8" />
</bean>

完成后,还需要修改HelloController中处理请求的方法的返回值为welcome,要求根据“前缀 + 方法返回值 + 后缀”能得到页面文件的位置!所以:

@RequestMapping("hello.do")
public String showHello() {
	System.out.println("HelloController.showHello()");
	return "welcome";
}

如果处理请求的方法返回null,等同于返回映射的地址中小数点左侧的名称!但是,并不推荐这样处理!

全部完成后,启动项目,打开浏览器,输入网址即可访问!

另外,在SpringMVC + Thymeleaf项目中,可使用的模版解析器有ClassLoaderTemplateResolverServletContextTemplateResolver,使用前者,应该将HTML模版文件放在src/main/resources下,使用后者,应该将HTML模版文件放在webapp下。

4. 接收客户端提交的请求参数

4.1. 准备案例

目标:在浏览器访问http://localhost:8080/项目名称/reg.do时,可以打开注册页面,页面中至少包括用户名、密码、年龄、手机号码、电子邮箱这5项数据的输入框和1个提交按钮!

首先,创建Maven Project项目,创建过程中勾选Create a simple projectGroup Idcn.teduArtifact IdSpringMVC02Packaging选择war

当项目创建出来后,需要:

  • 生成web.xml
  • 从前序项目中复制依赖的代码到当前项目的pom.xml中;
  • 从前序项目中复制spring.xml到当前项目中;
  • 对项目点右键,在属性中勾选Tomcat;
  • 打开前序项目的web.xml,将DispatcherServlet的配置复制到当前项目的web.xml中。

然后创建cn.tedu.spring.UserController控制器类,需要注意:该类必须放在组件扫描的包下,所以,应该检查spring.xml中的组件扫描的配置。

在控制器类中添加处理请求的方法:

@Controller
public class UserController {

	@RequestMapping("reg.do")
	public String showReg() {
		System.out.println("UserController.showReg()");
		return "reg";
	}
	
}

结合spring.xml中的配置,应该在src/main/resources下创建web文件夹,并在该文件夹下创建页面。

创建完成后,在浏览器中打开http://localhost:8080/SpringMVC02/reg.do即可看到所创建的页面。

如果需要获取客户端提交的注册请求,应该先在控制器类中再添加一个接收这个请求的方法:

@RequestMapping("handle_reg.do")
public String handleReg() {
	System.out.println("UserController.handleReg()");
	return null;
}

然后,在页面中,将<form>action属性设置为handle_reg.do即可,当在页面点击提交按钮时,就会将请求提交到以上控制器方法!

4.2. 通过HttpServletRequest对象获取请求参数【不推荐】

可以在处理请求的方法的参数列表中添加HttpServletRequest类型的参数,在方法的处理过程中,调用该参数对象的String getParameter(String name)方法,即可获取客户端提交的请求参数:

@RequestMapping("handle_reg.do")
public String handleReg(HttpServletRequest request) {
	System.out.println("UserController.handleReg()");
	
	String username = request.getParameter("username");
	String password = request.getParameter("password");
	String age = 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 null;
}

这种存在几个明显的问题:

  1. 获取参数的过程比较麻烦;

  2. 需要自行处理非String类型的数据的类型转换;

  3. 不便于执行单元测试。

4.3. 将请求参数声明为处理请求的方法的参数【推荐】

在处理请求的方法的参数列表中,一一添加客户端提交的请求参数,并保持使用相同的名称即可:

@RequestMapping("handle_reg.do")
public String handleReg(String username, String password, 
		Integer age, String phone, String email) {
	System.out.println("UserController.handleReg()");
	
	System.out.println("username=" + username);
	System.out.println("password=" + password);
	System.out.println("age=" + (age + 1));
	System.out.println("phone=" + phone);
	System.out.println("email=" + email);
	
	return null;
}

默认情况下,处理请求的方法的参数名称,必须与客户端提交的参数名称保持一致,否则,处理请求的方法的参数值将是null

在处理请求的方法中,参数列表中的各参数是不区分先后顺序的!

如果客户端提交了多个同名的请求参数,在控制器中处理请求的方法的参数列表中,可以使用数组格式来接收这些请求参数,例如客户端添加了“专业技能”:

<td>专业技能</td>
<td>
	<input type="checkbox" name="skill" value="Java OOP" />Java OOP
	<input type="checkbox" name="skill" value="Java SE" />Java SE
	<input type="checkbox" name="skill" value="MySQL" />MySQL
	<input type="checkbox" name="skill" value="JDBC" />JDBC
	<input type="checkbox" name="skill" value="Java EE" />Java EE
</td>

则服务器端的控制器中处理请求的方法:

@RequestMapping("handle_reg.do")
public String handleReg(String username, String password, 
		Integer age, String phone, String gender, String email, String[] skill) {
	System.out.println("UserController.handleReg()");
	
	System.out.println("username=" + username);
	System.out.println("password=" + password);
	System.out.println("gender=" + gender);
	System.out.println("age=" + (age + 1));
	System.out.println("phone=" + phone);
	System.out.println("email=" + email);
	System.out.println("skill=" + skill);
	System.out.println("skill.length=" + skill.length);
	for (int i = 0; i < skill.length; i++) {
		System.out.println("skill[" + i + "]=" + skill[i]);
	}
	
	return null;
}

使用这种方式获取请求参数非常简单,但是,不太适用于请求参数较多的应用场景!

4.4. 使用封装的类型作用处理请求的方法的参数【推荐】

当请求参数的数量较多时,可以将这些数据都封装在一个自定义的数据类型中,例如:

public class User {

	private String username;
	private String password;
	private Integer age;
	private String phone;
	private String gender;
	private String email;
	private String[] skill;

	// 规范的SET/GET方法

}

然后,使用User作为彼得请求的方法的参数即可:

@RequestMapping("handle_reg.do")
public String handleReg(User user) {
	System.out.println("UserController.handleReg()");
	
	System.out.println(user);
	
	return null;
}

使用这种做法时,必须保证请求参数的名称与封装类(例如以上User类)的属性的名称是一致的

由于SpringMVC框架在处理时,会自动调用封装类(例如以上User类)相关属性的SET/GET方法,所以,必须保证相关属性有规范的SET/GET方法!

4.5. 小结

以上介绍了3种获取请求参数的做法,其中,第1种使用HttpServletRequest的做法是不推荐的!另外,还有第2种在参数列表中穷举请求参数,和第3种封装请求参数的做法都是推荐使用的!

当请求参数的数量较少(例如少于4个),并且参数的数量相对固定,或各数据都不可以归属于同一种类型时,应该优先使用第2种做法;

当请求参数的数量较多(例如超过4个),并且可以归属于同一种类型时,应该优先使用第3种做法,或,参数的数量可能发生变化,也应该优先使用第3种做法;

另外,以上第2种和第3种做法可以组合一起使用

5. 转发数据

5.1. 准备工作

在处理登录的过程中,模拟判断登录,暂不考虑登录成功的处理方式,当登录失败时,由专门的错误提示页面来显示错误信息:

// 假设root/1234是正确的用户名/密码
if ("root".equals(username)) {
	// 用户名正确,需要判断密码
	if ("1234".equals(password)) {
		// 密码也正确,则登录成功
		System.out.println("登录成功!");
	} else {
		// 密码错误
		System.out.println("登录失败,密码错误!");
		return "error";
	}
} else {
	// 用户名错误
	System.out.println("登录失败,用户名不存在!");
	return "error";
}

具体的错误描述应该是控制器转发给模版页面。

5.2. 使用HttpServletRequest封装需要转发的数据【不推荐】

在处理请求的方法的参数列表中添加HttpServletRequest参数,然后,当需要封装转发数据时,调用该参数对象的setAttribute(String name, Object value)方法进行封装即可:

// 假设root/1234是正确的用户名/密码
if ("root".equals(username)) {
	// 用户名正确,需要判断密码
	if ("1234".equals(password)) {
		// 密码也正确,则登录成功
		System.out.println("登录成功!");
	} else {
		// 密码错误
		System.out.println("登录失败,密码错误!");
		request.setAttribute("errorMessage", "登录失败,密码错误!");
		return "error";
	}
} else {
	// 用户名错误
	System.out.println("登录失败,用户名不存在!");
	request.setAttribute("errorMessage", "登录失败,用户名不存在!");
	return "error";
}

后续,在页面中需要显示封装的转发数据时,使用Thymeleaf表达式进行处理即可:

<h3 th:text="${errorMessage}"></h3>

这种做法并不推荐,因为它仍存在“不便于执行单元测试”的问题!

5.3. 使用ModelMap封装需要转发的数据【推荐】

关于使用ModelMap封装需要转发的数据,做法与使用HttpServletRequest几乎相同:

@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)) {
			// 密码也正确,则登录成功
			System.out.println("登录成功!");
		} else {
			// 密码错误
			System.out.println("登录失败,密码错误!");
			modelMap.addAttribute("errorMessage", "[ModelMap] 登录失败,密码错误!");
			return "error";
		}
	} else {
		// 用户名错误
		System.out.println("登录失败,用户名不存在!");
		modelMap.addAttribute("errorMessage", "[ModelMap] 登录失败,用户名不存在!");
		return "error";
	}
	
	return null;
}

尽管使用方式几乎是一样的,但是,ModelMap这种类型更加轻量级,并且易于执行单元测试!

5.4. 使用ModelAndView作为处理请求方法的返回值【不推荐】

将处理请求的方法的返回值类型声明为ModelAndView,可以调用ModelAndView(String viewName, Map<String, ?> model)构造方法创建对象,该构造方法的第1个参数String viewName表示视图名,第2个参数Map<String, ?> model表示转发的数据:

@RequestMapping("handle_login.do")
public ModelAndView handleLogin(String username, String password) {
	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)) {
			// 密码也正确,则登录成功
			System.out.println("登录成功!");
		} else {
			// 密码错误
			System.out.println("登录失败,密码错误!");
			Map<String, Object> model = new HashMap<String, Object>();
			model.put("errorMessage", "[ModelAndView] 登录失败,密码错误!");
			ModelAndView mav = new ModelAndView("error", model);
			return mav;
		}
	} else {
		// 用户名错误
		System.out.println("登录失败,用户名不存在!");
		Map<String, Object> model = new HashMap<String, Object>();
		model.put("errorMessage", "[ModelAndView] 登录失败,用户名不存在!");
		ModelAndView mav = new ModelAndView("error", model);
		return mav;
	}
	
	return null;
}

这种做法相比此前使用ModelMap更加麻烦,所以,一般不推荐使用!

当然,使用ModelMap时,SpringMVC的运行原理最终还是会处理为ModelAndView类型的数据,只不过这部分不需要开发者来完成!

6. 重定向

当处理请求的方法的返回值是String类型时,默认情况下,表示的意思是转发,该返回值会与配置的前缀和后缀进行拼接,从而确定最终负责显示的视图组件!如果返回值使用redirect:作为前缀,加上目标路径,就可以实现重定向!例如:

return "redirect:login.do";

注意:当使用重定向时,在redirect:右侧的是目标路径,该路径会直接响应给客户端,并不会参与前缀后缀的拼接,后续客户端会根据这个路径发出第2次请求!

转发也可以使用forward:作为前缀,只不过,默认就表示转发,所以,不需要添加这个前缀。

----------------------------------------------

附1:关于封装

封装就是先“装”了再“封”的过程!

在许多应用场景中,可能需要使用或传递或克隆多项数据,为了便于管理,可以将这些数据“装”在某一个类型中,例如:

public class User {
	public String username;
	public String password;
	public Integer age;
}

后续,关于这个类的使用可以是:

User user = new User();
user.username = "Mike";
user.password = "1223";
user.age = 9527;

以上代码就出现了“合法却不合理”的问题!

为了避免类的属性被随便恶意访问,可以限制属性的访问:

public class User {
	public String username;
	public String password;
	private Integer age;
}

限制了访问权限,其实,就是“封”的过程!

但是,“封”的目的并不是“不允许访问”,而是“不允许随便访问”,为了保证这个属性还是可以被访问的,可以为这个属性添加公有的SET方法:

public class User {
	public String username;
	public String password;
	private Integer age;

	public void setAge(Integer age) {
		this.age = age;
	}
}

后续,在类的外部,就可以通过user.setAge(9527);这类语法来访问Integer age属性!

由于使用了方法,则可以在方法中编写相关规则:

public void setAge(Integer age) {
	if (age >= 0 && age <= 150) {
		this.age = age;
	}
}

最后,为了保证外部还可以获取到该属性的值,通常,还会定义公有的GET方法:

public Integer getAge() {
	return age;
}

另外,除了类的属性可以封装以外,类本身也是可以封装的,例如工厂模式就是一种对类进行封装的表现。

附2:转发与重定向

请添加图片描述

转发与重定向都是控制器可以使得客户端浏览器显示某个页面的做法!

在转发的处理过程中,客户端只向服务器发出过1次请求;在重定向的处理过程中,客户端发出第1次请求后,服务器端会响应302及重定向的目标位置,客户端的浏览器接收到302响应码后,会根据目标位置发出第2次请求!

在转发的过程中,是涉及2个服务器内部的组件,所以,2个组件(控制器与页面)之间是可以传递任何类型的数据的;在重定向的过程中,由于是2次请求,并且Http协议是无状态协议,2次处理过程中的数据并不能直接共享使用!

由于转发是1次请求,所以,在客户端的浏览器的地址栏中,显示的就是最初发出请求的路径;由于重定向是2次请求,所以,最终在客户端的浏览器的地址栏中,显示的是最后一次请求的路径!

如果这篇文章有帮助到您,请简单给个赞吧,谢谢~

热门文章

暂无图片
编程学习 ·

exe4j详细使用教程(附下载安装链接)

一、exe4j介绍 ​ exe4j是一个帮助你集成Java应用程序到Windows操作环境的java可执行文件生成工具&#xff0c;无论这些应用是用于服务器&#xff0c;还是图形用户界面&#xff08;GUI&#xff09;或命令行的应用程序。如果你想在任务管理器中及Windows XP分组的用户友好任务栏…
暂无图片
编程学习 ·

AUTOSAR从入门到精通100讲(126)-浅谈车载充电系统通信方案

01 引言 本文深入研究车载充电系统策略,设计出一套基于电动汽车电池管理系统与车载充电机的CAN通信协议,可供电动汽车设计人员参考借鉴。 02 电动汽车充电系统通讯网络 电动汽车整车控制系统中采用的是CAN总线通信方式,由一个整车内部高速CAN网络、内部低速CAN网络和一个充电…
暂无图片
编程学习 ·

CMake(九):生成器表达式

当运行CMake时&#xff0c;开发人员倾向于认为它是一个简单的步骤&#xff0c;需要读取项目的CMakeLists.txt文件&#xff0c;并生成相关的特定于生成器的项目文件集(例如Visual Studio解决方案和项目文件&#xff0c;Xcode项目&#xff0c;Unix Makefiles或Ninja输入文件)。然…
暂无图片
编程学习 ·

47.第十章 网络协议和管理配置 -- 网络配置(八)

4.3.3 route 命令 路由表管理命令 路由表主要构成: Destination: 目标网络ID,表示可以到达的目标网络ID,0.0.0.0/0 表示所有未知网络,又称为默认路由,优先级最低Genmask:目标网络对应的netmaskIface: 到达对应网络,应该从当前主机哪个网卡发送出来Gateway: 到达非直连的网络,…
暂无图片
编程学习 ·

元宇宙技术基础

请看图&#xff1a; 1、通过AR、VR等交互技术提升游戏的沉浸感 回顾游戏的发展历程&#xff0c;沉浸感的提升一直是技术突破的主要方向。从《愤怒的小鸟》到CSGO,游戏建模方式从2D到3D的提升使游戏中的物体呈现立体感。玩家在游戏中可以只有切换视角&#xff0c;进而提升沉浸…
暂无图片
编程学习 ·

flink的伪分布式搭建

一 flink的伪分布式搭建 1.1 执行架构图 1.Flink程序需要提交给 Job Client2.Job Client将作业提交给 Job Manager3.Job Manager负责协调资源分配和作业执行。 资源分配完成后&#xff0c;任务将提交给相应的 Task Manage。4.Task Manager启动一个线程以开始执行。Task Manage…
暂无图片
编程学习 ·

十进制正整数与二进制字符串的转换(C++)

Function one&#xff1a; //十进制数字转成二进制字符串 string Binary(int x) {string s "";while(x){if(x % 2 0) s 0 s;else s 1 s;x / 2;}return s; } Function two&#xff1a; //二进制字符串变为十进制数字 int Decimal(string s) {int num 0, …
暂无图片
编程学习 ·

[含lw+源码等]微信小程序校园辩论管理平台+后台管理系统[包运行成功]Java毕业设计计算机毕设

项目功能简介: 《微信小程序校园辩论管理平台后台管理系统》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 本系统包含微信小程序做的辩论管理前台和Java做的后台管理系统&#xff1a; 微信小程序——辩论管理前台涉及技术&#xff1a;WXML 和 WXS…
暂无图片
编程学习 ·

树莓派驱动DHT11温湿度传感器

1&#xff0c;直接使用python库 代码如下 import RPi.GPIO as GPIO import dht11 import time import datetimeGPIO.setwarnings(True) GPIO.setmode(GPIO.BCM)instance dht11.DHT11(pin14)try:while True:result instance.read()if result.is_valid():print(ok)print(&quo…
暂无图片
编程学习 ·

ELK简介

ELK简介 ELK是三个开源软件的缩写&#xff0c;Elasticsearch、Logstash、Kibana。它们都是开源软件。不过现在还新增了一个 Beats&#xff0c;它是一个轻量级的日志收集处理工具(Agent)&#xff0c;Beats 占用资源少&#xff0c;适合于在各个服务器上搜集日志后传输给 Logstas…
暂无图片
编程学习 ·

Linux 基础

通常大数据框架都部署在 Linux 服务器上&#xff0c;所以需要具备一定的 Linux 知识。Linux 书籍当中比较著名的是 《鸟哥私房菜》系列&#xff0c;这个系列很全面也很经典。但如果你希望能够快速地入门&#xff0c;这里推荐《Linux 就该这么学》&#xff0c;其网站上有免费的电…
暂无图片
编程学习 ·

Windows2022 无线网卡装不上驱动

想来 Windows2022 和 windows10/11 的驱动应该差不多通用的&#xff0c;但是死活装不上呢&#xff1f; 搜一下&#xff0c;有人提到 “默认安装时‘无线LAN服务’是关闭的&#xff0c;如果需要开启&#xff0c;只需要在“添加角色和功能”中&#xff0c;选择开启“无线LAN服务…
暂无图片
编程学习 ·

【嵌入式面试宝典】版本控制工具Git常用命令总结

目录 创建仓库 查看信息 版本回退 版本检出 远程库 Git 创建仓库 git initgit add <file> 可反复多次使用&#xff0c;添加多个文件git commit -m <message> 查看信息 git status 仓库当前的状态git diff 差异对比git log 历史记录&#xff0c;提交日志--pret…
暂无图片
编程学习 ·

用Postman生成测试报告

newman newman是一款基于nodejs开发的可以运行postman脚本的工具&#xff0c;使用Newman&#xff0c;可以直接从命令运行和测试postman集合。 安装nodejs 下载地址&#xff1a;https://nodejs.org/en/download/ 选择自己系统相对应的版本内容进行下载&#xff0c;然后傻瓜式安…
暂无图片
编程学习 ·

Java面向对象之多态、向上转型和向下转型

文章目录前言一、多态二、引用类型之间的转换Ⅰ.向上转型Ⅱ.向下转型总结前言 今天继续Java面向对象的学习&#xff0c;学习面向对象的第三大特征&#xff1a;多态&#xff0c;了解多态的意义&#xff0c;以及两种引用类型之间的转换&#xff1a;向上转型、向下转型。  希望能…