文章目录
- 前言
- 一、Lombok库
- 二、spring-boot-starter-validation库
- 三、ThreadLocalUtil
- 四、全局异常处理
- 总结
前言
前面我们已经把vue+springboot前后端分离开发和打包部署过程全部打通了,通过一个简单的demo来演示整个过程,主要关注在开发工具使用、框架目录结构、调试方法、打包部署上,虽然也有少量代码理解,但是并没有过多关注代码的实现细节。后面我们将通过代码解读优化来逐步熟悉vue和springboot的代码细节实现,本篇先解读一下后端springboot代码并做优化
一、Lombok库
我们先来看看《手把手教你入门vue+springboot开发(一)》中的User.java代码。
package com.example.demo.bean;
public class User {
private String id;
private String userName;
private String password;
private String sex;
private String telephone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
}
同C++开发一样,我们需要为类的每个成员变量编写get和set方法,这种繁琐的工作占用了我们大量的时间,所以springboot提供了很多好的库,可以让我们通过注解的方式省掉这些繁琐的工作,更专注于业务代码的实现。比如,我们可以使用Lombok库的Data注解,如下图所示修改User.java代码,简洁很多。
package com.example.demo.bean;
import lombok.Data;
@Data
public class User {
private String id;
private String userName;
private String password;
private String sex;
private String telephone;
}
要使用lombok库,需要在pom.xml中增加lombok依赖
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
我理解,注解实际会在编译的时候生成代码,这部分生成的代码就不需要我们手写了。那么Data注解会生成什么代码呢?user.java编译后会在target目录生成User.class代码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.example.demo.bean;
public class User {
private String id;
private String userName;
private String password;
private String sex;
private String telephone;
public User() {
}
public String getId() {
return this.id;
}
public String getUserName() {
return this.userName;
}
public String getPassword() {
return this.password;
}
public String getSex() {
return this.sex;
}
public String getTelephone() {
return this.telephone;
}
public void setId(final String id) {
this.id = id;
}
public void setUserName(final String userName) {
this.userName = userName;
}
public void setPassword(final String password) {
this.password = password;
}
public void setSex(final String sex) {
this.sex = sex;
}
public void setTelephone(final String telephone) {
this.telephone = telephone;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof User)) {
return false;
} else {
User other = (User)o;
if (!other.canEqual(this)) {
return false;
} else {
label71: {
Object this$id = this.getId();
Object other$id = other.getId();
if (this$id == null) {
if (other$id == null) {
break label71;
}
} else if (this$id.equals(other$id)) {
break label71;
}
return false;
}
Object this$userName = this.getUserName();
Object other$userName = other.getUserName();
if (this$userName == null) {
if (other$userName != null) {
return false;
}
} else if (!this$userName.equals(other$userName)) {
return false;
}
label57: {
Object this$password = this.getPassword();
Object other$password = other.getPassword();
if (this$password == null) {
if (other$password == null) {
break label57;
}
} else if (this$password.equals(other$password)) {
break label57;
}
return false;
}
Object this$sex = this.getSex();
Object other$sex = other.getSex();
if (this$sex == null) {
if (other$sex != null) {
return false;
}
} else if (!this$sex.equals(other$sex)) {
return false;
}
Object this$telephone = this.getTelephone();
Object other$telephone = other.getTelephone();
if (this$telephone == null) {
if (other$telephone == null) {
return true;
}
} else if (this$telephone.equals(other$telephone)) {
return true;
}
return false;
}
}
}
protected boolean canEqual(final Object other) {
return other instanceof User;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
result = result * 59 + ($id == null ? 43 : $id.hashCode());
Object $userName = this.getUserName();
result = result * 59 + ($userName == null ? 43 : $userName.hashCode());
Object $password = this.getPassword();
result = result * 59 + ($password == null ? 43 : $password.hashCode());
Object $sex = this.getSex();
result = result * 59 + ($sex == null ? 43 : $sex.hashCode());
Object $telephone = this.getTelephone();
result = result * 59 + ($telephone == null ? 43 : $telephone.hashCode());
return result;
}
public String toString() {
String var10000 = this.getId();
return "User(id=" + var10000 + ", userName=" + this.getUserName() + ", password=" + this.getPassword() + ", sex=" + this.getSex() + ", telephone=" + this.getTelephone() + ")";
}
}
可以看到,Data注解为我们自动生成了构造函数、get/set方法、equals方法、canEqual方法、hashCode方法、toString方法。
我们再来看看《手把手教你入门vue+springboot开发(三)》中的Result.java代码。
package com.example.demo.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//统一响应结果
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {
private Integer code;//业务状态码 0-成功 1-失败
private String message;//提示信息
private T data;//响应数据
//快速返回操作成功响应结果(带响应数据)
public static <E> Result<E> success(E data) {
return new Result<>(0, "操作成功", data);
}
//快速返回操作成功响应结果
public static Result success() {
return new Result(0, "操作成功", null);
}
public static Result error(String message) {
return new Result(1, message, null);
}
}
这里除了使用了Data注解,还使用了NoArgsConstructor和AllArgsConstructor注解,NoArgsConstructor注解表示自动生成无参数构造函数,AllArgsConstructor注解表示自动生成全参数构造函数。
以下是target目录编译生成的Result.class文件。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.example.demo.bean;
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <E> Result<E> success(E data) {
return new Result(0, "操作成功", data);
}
public static Result success() {
return new Result(0, "操作成功", (Object)null);
}
public static Result error(String message) {
return new Result(1, message, (Object)null);
}
public Result() {
}
public Result(final Integer code, final String message, final T data) {
this.code = code;
this.message = message;
this.data = data;
}
public Integer getCode() {
return this.code;
}
public String getMessage() {
return this.message;
}
public T getData() {
return this.data;
}
public void setCode(final Integer code) {
this.code = code;
}
public void setMessage(final String message) {
this.message = message;
}
public void setData(final T data) {
this.data = data;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Result)) {
return false;
} else {
Result<?> other = (Result)o;
if (!other.canEqual(this)) {
return false;
} else {
label47: {
Object this$code = this.getCode();
Object other$code = other.getCode();
if (this$code == null) {
if (other$code == null) {
break label47;
}
} else if (this$code.equals(other$code)) {
break label47;
}
return false;
}
Object this$message = this.getMessage();
Object other$message = other.getMessage();
if (this$message == null) {
if (other$message != null) {
return false;
}
} else if (!this$message.equals(other$message)) {
return false;
}
Object this$data = this.getData();
Object other$data = other.getData();
if (this$data == null) {
if (other$data != null) {
return false;
}
} else if (!this$data.equals(other$data)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(final Object other) {
return other instanceof Result;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $code = this.getCode();
result = result * 59 + ($code == null ? 43 : $code.hashCode());
Object $message = this.getMessage();
result = result * 59 + ($message == null ? 43 : $message.hashCode());
Object $data = this.getData();
result = result * 59 + ($data == null ? 43 : $data.hashCode());
return result;
}
public String toString() {
Integer var10000 = this.getCode();
return "Result(code=" + var10000 + ", message=" + this.getMessage() + ", data=" + String.valueOf(this.getData()) + ")";
}
}
Lombok库除了提供Data注解、NoArgsConstructor注解、AllArgsConstructor注解,还提供了很多其它注解,大家有兴趣可以自行百度一下,后面我们用到了再给大家介绍。
二、spring-boot-starter-validation库
spring-boot-starter-validation库主要用于校验,我们需要先在pom.xml增加依赖
<!--validation依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
它主要提供以下常用注解。
UserController.java代码中使用了Pattern注解对参数username和password做校验。
public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password)
我们再在User.java中增加NotNull、NotEmpty、JsonIgnore三个注解,代码如下:
package com.example.demo.bean;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class User{
@NotNull
private String id;
@NotEmpty
private String userName;
@JsonIgnore
private String password;
private String sex;
private String telephone;
}
@JsonIgnore/注解让springboot把当前对象转换成json字符串的时候,忽略password,最终的json字符串中就没有password这个属性。
三、ThreadLocalUtil
在LoginInterceptor.java代码中我们使用了线程局部变量ThreadLocalUtil,如下图所示:
前面我们讲过,LoginInterceptor实现的是拦截器功能,preHandle方法实现请求进入controller处理前要做的事,这里我们主要是校验token,并把它保存起来,因为token信息中带了用户ID,后面的代码就不需要再从数据库中去拿用户ID。那么这里我们为什么要用线程局部变量ThreadLocalUtil呢?
因为在B/S架构中,每个用户登录服务器,tomcat会为它创建一个线程,如果有3个用户同时登录,那tomcat就会创建3个线程,每个用户的请求都进入它自己的线程处理,显然在这种情况下只能使用ThreadLocalUtil来保存token了。
afterCompletion方法实现请求处理完后要做的事情,那么为了防止内存泄漏,在这里我们需要清空ThreadLocalUtil的数据。
四、全局异常处理
增加exception包,里面增加GlobalExceptionHandler类来处理全局异常,主要目的是为了把各类异常统一返回成Result的格式。
GlobalExceptionHandler.java代码如下:
package com.example.demo.exception;
import com.example.demo.bean.Result;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result handleException(Exception e){
e.printStackTrace();
return Result.error(StringUtils.hasLength(e.getMessage())? e.getMessage() : "操作失败");
}
}
这里使用了RestControllerAdvice和ExceptionHandler注解。RestControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上。ExceptionHandler注解用于指定异常处理方法,当与@RestControllerAdvice注解配合使用时,用于全局处理控制器里的异常。
总结
本篇主要通过对前面实现的登录demo实例的后端代码解读并进行一些优化,来熟悉java或者springboot编程的基础细节,一些库和注解的使用。下一篇我们将解读登录demo实例的vue前端代码。