找往期文章包括但不限于本期文章中不懂的知识点:
个人主页:我要学编程程(ಥ_ಥ)-优快云博客
所属专栏:JavaEE
初始JavaEE篇 —— Spring Web MVC入门(上)-优快云博客
初始JavaEE篇 —— Spring Web MVC入门(下)-优快云博客
通过上面两篇文章的学习,我们已经可以熟练的编写接收请求和返回响应的代码了。接下来,就是进入实战环节,通过一些综合性的前后端练习来更好的掌握前端知识与后端知识以及两者之间如何进行数据交互。
目录
简易计算器
实现效果:
当我们去输入数字,并选择相应的计算方式,最后点击计算时,会将最终的结果显示出来。
前端实现:
计算器就是一个h1标签的文本。数字输入框是一个Input框,类型是text。操作是下拉选择框。计算是一个按钮。当点击计算时,会将数字输入框内的数字传给后端并进行计算,这就需要一个form表单。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>计算器</title>
</head>
<body>
<!-- action 表示提交的URL,method 表示提交的方式-->
<form action="calc/operation" method="post">
<h1>计算器</h1>
数字1 <input name="num1" type="text"> <br>
数字2 <input name="num2" type="text"> <br>
<select name="operation">
<option value="add">加</option>
<option value="sub">减</option>
<option value="mul">乘</option>
<option value="div">除</option>
</select> <br>
<!-- 点击计算按钮时,会将表单域中的参数按照method的值,提交到目标URL -->
<!-- 参数是key value的形式,key是name的值,value是input框中的值 -->
<input type="submit" value="计算">
</form>
</body>
</html>
后端实现:
后端只需要提供一个接口给前端,就是计算结果数据。
该接口需要接收三个参数:num1、num2以及operation,返回计算的结果即可。
@RestController
@RequestMapping("/calc")
public class CalcController {
@RequestMapping("/operation")
public String add(Integer num1, Integer num2, String operation) {
// 判断num1和num2是否存在null
if (num1 == null || num2 == null) {
return "数据非法,请检查数据的合法性";
}
// 根据operation的值来决定接下来执行什么样的操作
switch (operation) {
case "add":
return num1+"+"+num2+"的值为: "+(num1 + num2);
case "sub":
return num1+"-"+num2+"的值为: "+(num1 - num2);
case "mul":
return num1+"*"+num2+"的值为: "+(num1 * num2);
case "div":
if (num2 == 0) {
return "除数不能为0";
}
return num1+"/"+num2+"的值为: "+(num1 * 1.0 / num2);
default:
return "非法操作,请重试";
}
}
}
注意:
1、我们Java程序员属于纯后端程序员,以后的工作中都是前后端分离开发的模式,因此在学习阶段只需要了解前端代码即可(认识即可)。
2、只要前端使用 <form> 表单传递用户输入的参数,当用户点击 submit 按钮时,表单内所有带有 name 属性的输入框数据 都会按照 <form> 标签的 action 指定的路径,以 method 指定的方式提交给后端。
3、
如果采用get的方式传输的话,参数都是通过query-string进行传输的,会被用户认为"不安全"。如下所示:
简易用户登录
实现效果:
前端实现:
登录界面组成非常简单,一个h1标签、三个input框(文本框、密码框、按钮)。由于登录失败是不需要跳转页面的,因此登录界面不能使用form表单的方式。这里需要使用构造Ajax请求。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录页面</title>
</head>
<body>
<h1>用户登录</h1>
用户名:<input type="text" id="userName"> <br>
密码:<input type="password" id="password"> <br>
<!-- 点击登录之后,就会调用onlick参数的值,即login函数 -->
<input type="button" value="登录" onclick="login()">
<!-- 引入JQuery所需的文件 -->
<script src="./jquery-3.7.1.min.js"></script>
<!-- 登录失败不能跳转页面,也就不能使用form表单的形式了 -->
<script>
function login() {
// 点击登录之后,就需要将参数传递给后端了
$.ajax({
// 请求方式、URL、传递的数据,请求的成功响应处理
type: "post",
url: "/user/login",
data: {
// key: value的形式
userName: $("#userName").val(),
password: $("#password").val()
},
// http请求成功执行回调函数,参数为result(返回的结果)
success: function(result) {
if (result) {
// 跳转到对应的页面
location.href = "userInfo.html";
} else {
alert("账号或密码错误,请重新输入");
}
}
});
}
</script>
</body>
</html>
用户首页:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户登录首页</title>
</head>
<body>
登录人:<span id="span"></span>
<!-- 引入JQuery所需的文件 -->
<script src="./jquery-3.7.1.min.js"></script>
<script>
$.ajax({
// 请求方式、URL、数据、处理成功
type: "get",
url: "user/userInfo",
success: function(userName) {
$("#span").text(userName);
}
});
</script>
</body>
</html>
后端实现:
后端需要提供两个接口给前端,一个是登录接口,另一个是个人界面接口。
登录需要两个参数:userName 和 password,返回是否登录成功即可(true或者false)
个人界面不需要参数,返回的信息就是个人信息的字符串即可。
@RestController
@RequestMapping("/user")
public class UserController {
// 未登录
@RequestMapping("/login")
public boolean login(String userName, String password, HttpServletRequest request) {
// 判断用户名和密码是否为null或者空字符串
if (userName == null || password == null) {
return false;
}
if ("".equals(userName) || "".equals(password)) {
return false;
}
// 验证用户名和密码
if ("我要学编程".equals(userName) && "123456".equals(password)) {
// 将用户的信息存储起来(通过session)
HttpSession session = request.getSession();
session.setAttribute("userName", userName);
return true;
}
return false;
}
// 登录成功
@RequestMapping("/userInfo")
public String userInfo(HttpServletRequest request) {
// 如果未登录,返回一个新的session对象
HttpSession session = request.getSession();
String userName = (String) session.getAttribute("userName");
if (userName == null) { // 新用户(并未登录)
return "新用户,您好!请登录之后再使用!";
}
return userName+"!欢迎回来!感谢您的使用!";
}
}
注意:
1、在验证用户登录时,由于简易版,只是关注前后端的交互过程,因此就是直接验证了,实际开发中,需要去查询数据库中是否存在该用户。
2、在判断userName 和 password时,是手动去判断是否为空字符串和null,除此之外,还可以使用 Spring框架中提供的工具类:StringUtils。其中的hasLength方法和上述手动实现的效果一致。
Lombok的使用
通过前面的学习,我们知道Spring框架在接收JSON数据时,是通过无参的构造方法来构造对象,接着使用setter方法来设置相关属性的。在这个过程中,就需要去创建该对象所对应的类并提供相应的构造方法、getter方法、setter方法等。虽然我们是通过快捷键生成的代码,但是还是比较繁琐(相对于JavaEE的代码来说),那有没有简单点的注解可以代替上述代码呢?有的。Lombok 是一个 Java 库,可以帮助开发者简化和减少样板代码。通过使用 Lombok 的注解,开发者可以自动生成常见的代码,如 getter、setter、构造函数、toString
方法等,从而提高开发效率和代码的可读性。这就避免了去生成大量的样本代码。
1、首先,在pom文件中,导入lombok的依赖(可以去Maven的官网上面查找):
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
2、在对应的类上面,添加注解:@Data。
通过上面两个步骤,就可以将原来的所有代码给注解掉了。
@Data注解会自动生成getter方法、setter方法、equals方法、hashCode方法、toString方法。我们可以观察@Data注解的源码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package lombok;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE}) // 类注解
@Retention(RetentionPolicy.SOURCE) // 生命周期只存在于源码阶段
public @interface Data {
String staticConstructor() default "";
}
由于其生命周期只存在于源码阶段,因此字节码阶段该注解就不会存在了,我们就可以去通过字节码文件来观察其生成的这些方法:
通过上述步骤,找到对应的文件之后,就可以双击来进行观察了(由于IDEA本身会对这些代码进行反编译,因此我们最终看到的代码还是Java语言的源代码)。
在实际的开发中,可能我们并不需要这么多的方法,又或者toString方法的目的就是为了拿到对象在内存中的地址,即不需要去重写,那怎么办呢?别担心,Lombok给我们提供了更加精细的注解:
注解 | 说明 |
@Getter | 自动添加 getter方法 |
@Setter | 自动添加 setter方法 |
@ToString | 自动添加 toString方法 |
@EqualsAndHashCode | 自动添加 equals方法 和 hashCode方法 |
@NoArgsConstructor | 自动添加无参构造方法 |
@AllArgsConstructor | 自动添加全属性的构造方法,顺序按照属性的定义顺序 |
@NonNull | 属性不能为null。如果为null,则抛出空指针异常 |
@RequiredArgsConstructor | 自动添加必需属性的构造方法,final + @NonNull 的属性为必需 |
@Data = @Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor
注意:
1、无参的构造方法是编译器自动生成的,而不是注解生成的。如果我们手动提供一个全属性的构造方法,那么在构造一个空对象时,会构造失败。
2、@Getter 和 @Setter 既是类注解,也是字段注解。当其位于类外时,代表整个类的属性都带有;当其位于某个属性之上时,代表该属性带有,别的属性并不存在。剩下的注解中,除了@NonNull 之外其余都是单一的类类注解,而 @NonNull 既是类注解,又是其他的注解(比较多)
3、@RequiredArgsConstructor 会自动生成一个包含所有 final
字段和带有 @NonNull
注解的字段的构造函数。如果该类中不存在带有 final 字段的属性 并且也不存在 @NonNull 注解的字段,那么即使加了这个注解,最终并不会其作用。
除了上述手动在pom文件中添加依赖之外,我们还可以在创建SpringBoot项目时,去添加Lombok依赖。如果创建完项目之后,忘记添加了,而且也不知道怎么在pom文件中添加依赖的话,可以选择另外一种方式:安装 EditStarters 插件。
1、找到 File -> Settings -> Plugins -> Marketplace,搜索 Editstarters:
2、Install完成之后,重启IDEA,进入pom文件,右键鼠标,找到Generate(Alt + Insert),并点击进入:
找到最上方的 Edit Starters,点击并进入(优先选择 Github),再点击 OK:
在已选择的依赖中,我们已经看到了 Lombok 依赖,这就完成了。
使用该插件,可以随时在项目中配置我们所需的依赖。
有了Lombok依赖之后,后续的所有类的模块代码都可以使用注解来搞定。
留言板
实现效果:
只要服务器正常运行,后续不断刷新也不会丢失数据,留言的数据依旧存在留言板上。
前端实现:
界面的组成元素非常简单,一个h1标签,四个span标签,其余的都是input输入框,但要注意的是整体的实现效果是在中间,因此整体肯定得移动,可以将上述所有标签都放在一个div标签中即可。剩下的就是一些CSS样式的设计了。
上面设计完基础的界面之后,接下来就是具体功能的实现,当点击提交按钮之后,需要对后端发起Ajax请求,请求成功之后,就需要在留言板上显示新的留言,并清空原来留言条上的内容。当每次刷新的时候,都需要向后端发起Ajax请求,拿到留言板的数据。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>留言板</title>
<style>
.container {
width: 350px;
height: 300px;
margin: 0 auto;
/* border: 1px black solid; */
text-align: center;
}
.grey {
color: grey;
}
.container .row {
width: 350px;
height: 40px;
display: flex;
justify-content: space-between;
align-items: center;
}
.container .row input {
width: 260px;
height: 30px;
}
#submit {
width: 350px;
height: 40px;
background-color: orange;
color: white;
border: none;
margin: 10px;
border-radius: 5px;
font-size: 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>留言板</h1>
<p class="grey">输入后点击提交, 会将信息显示下方空白处</p>
<div class="row">
<span>谁:</span> <input type="text" name="" id="from">
</div>
<div class="row">
<span>对谁:</span> <input type="text" name="" id="to">
</div>
<div class="row">
<span>说什么:</span> <input type="text" name="" id="message">
</div>
<input type="button" value="提交" id="submit" onclick="submit()">
<!-- <div>A 对 B 说: hello</div> -->
</div>
<script src="./jquery-3.7.1.min.js"></script>
<script>
// 当访问一个HTML页面时,会去尝试加载其中的script标签中的内容
// 页面一加载,就去拿留言列表的数据
load();
function load() {
let divHtml = "";
$.ajax({
// type, url, success
type: "get",
url: "/message/getList",
success: function (result) {
// 后端传递List时,JS会自动将其转换为JS中的数组
// 使用forEach遍历数组
if (result != null && result.length > 0) {
for (let element of result) {
let from = element.from;
let to = element.to;
let message = element.message;
divHtml += "<div>" + from + "对" + to + "说:" + message + "</div>";
}
$(".container").append(divHtml);
}
}
});
}
function submit() {
// 1、获取留言的内容
// 文本框没有内容,返回空字符串。如果没有找到对应的元素,则返回undefined。
// 不会返回null(手动设置除外)
let from = $('#from').val();
let to = $('#to').val();
let message = $('#message').val();
// 判断是否存在空字符串的情况
if (from == '' || to == '' || message == '') {
return;
}
// 2、发起请求
$.ajax({
// type, url, data, success
type: "post",
url: "/message/publish",
data: {
from: from,
to: to,
message: message
},
success: function (result) {
// 如果后端返回的是JSON,那么我们可以直接去当作对象来使用
// 因为JS会默认转换为对象
if (result.ok == 1) {
// 发布成功
// 2、构造节点
let divElement = "<div>" + from + "对" + to + "说:" + message + "</div>";
// 3、将节点添加到页面
$(".container").append(divElement);
// 4、清空输入框的值
$("#from").val("");
$("#to").val("");
$("#message").val("");
} else {
alert("留言发布失败!");
}
}
});
}
</script>
</body>
</html>
后端实现:
后端需要提供两个接口给前端,一个是发布留言的接口,另一个是获取留言板的接口。
发布留言的接口需要三个参数:from,to,message(可以封装成一个对象,使用JSON数据格式来接收),返回的也是JSON格式的字符串。
获取留言板的接口不需要参数,返回的留言板对象。
@RestController
@RequestMapping("/message")
public class MessageController {
// 创建留言列表对象
private final List<MessageInfo> list = new ArrayList<>();
@RequestMapping("/getList")
public List<MessageInfo> getList() {
return list;
}
@RequestMapping(value = "/publish", produces = "application/json") // 修改响应体为JSON数据格式
public String publish(String from, String to, String message) { // 也可以接收MessageInfo对象
// 判断留言信息是否完整
if (!StringUtils.hasLength(from) || !StringUtils.hasLength(to)
|| !StringUtils.hasLength(message)) {
return "{\"ok\": 0}";
}
// 构造新留言对象
MessageInfo messageInfo = new MessageInfo();
messageInfo.setFrom(from);
messageInfo.setTo(to);
messageInfo.setMessage(message);
// 将留言对象添加到List中
list.add(messageInfo);
return "{\"ok\": 1}";
}
}
注意:我们Java注重的后端开发,而且现在企业大部分都是采用前后端分离开发的模式,因此前端代码我们可以简单编写或者看懂即可。
好啦!本期 初始JavaEE篇 —— Spring Web MVC综合练习 的学习之旅就到此结束啦!我们下一期再一起学习吧!