Java笔记-28、功能实现-属性拷贝、ThreadLocal动态获取id、全局异常处理
新增员工
对象间的属性拷贝
在实现新增员工的过程中,由前端传来的数据与数据库存储数据差别较大,建立了EmployeeDTO类和Employee类。为方便处理对象间的属性拷贝,spring framework提供了:
BeanUtils.copyProperties(Object source, Object target) throws BeansException
该方法通过反射机制实现属性的复制。它遵循以下步骤:
- 获取源对象和目标对象的属性:通过反射获取两个对象的属性名。
- 匹配属性:将源对象的属性与目标对象的属性进行匹配,通常基于属性名。
- 复制值:将源对象中属性的值赋值给目标对象相应的属性。
注意事项:
- 属性名匹配:源对象和目标对象的属性名必须一致。如果属性名不匹配,则该属性不会被复制。
- 类型匹配:属性类型必须兼容。例如,如果源对象的属性是 int 类型,目标对象的属性应该是 Integer 类型,否则可能会导致转换错误。
- 异常处理:该方法会抛出 BeansException 异常,因此需要进行适当的异常处理。
@Override
public void save(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
// 1. 属性拷贝
BeanUtils.copyProperties(employeeDTO, employee);
// 2. 设置employee中有而employeeDTO没有的属性的值
// 2.1 设置账号状态
employee.setStatus(StatusConstant.ENABLE);
// 2.2 设置密码,默认为123456
employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
// 2.3 设置创建时间和修改时间
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());
// 2.4 设置创建和更新人员id TODO 先写假数据,获取id后再补全
employee.setCreateUser(10L);
employee.setUpdateUser(10L);
// 3. 调用mapper的插入方法
employeeMapper.insert(employee);
}
调试的注意事项
在后端调试时,由于加入了jwt令牌机制,测试单个请求时,会由于JWT令牌校验失败,导致save方法没被调用。
解决办法:使用员工登录接口获取一个合法的jwt令牌,并在swagger文档管理-->全局参数设置-->添加参数。
代码优化
全局异常处理器处理重复用户名异常
数据库建表时username已经设为unique,如果前端请求带来的数据中username在数据库中不唯一,后端会报错,exception类型为SQLIntegrityConstraintViolationException。
但是前端没有任何提示,用户不知道哪里错了,导致添加失败。
需要在全局异常处理器中捕捉异常并返回提示。
在sky-server模块,com.sky.hander包的GlobalExceptionHandler.java中添加方法。
动态获取当前创建人和修改人的id
当前用户登录时,前端提交用户名和密码后,后端会生成JWT Token返回给前端。
之后前端每次请求后端接口,都会在请求头中携带token。
后端在每次请求前都会用注册的自定义拦截器拦截请求,验证jwt token,通过了就会执行业务逻辑,返回数据。不通过就会返回错误信息。
因此,在自定义拦截器中解析出员工id后,如何传递给service的save方法,以达到动态获取当前登录用户的id呢?
通过ThreadLocal进行传递。
ThreadLocal
ThreadLocal 并不是一个Thread,而是Thread的局部变量。
ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
常用方法:
public void set(T value) |
设置当前线程的线程局部变量的值 |
|---|---|
public T get() |
返回当前线程所对应的线程局部变量的值 |
public void remove() |
移除当前线程的线程局部变量 |
应用

在sky-common模块封装工具类,用于操作ThreadLocal存储id:
public class BaseContext {
public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
public static void removeCurrentId() {
threadLocal.remove();
}
}
在拦截器中解析出当前登录员工id,并放入线程局部变量中:
Long employeeId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
BaseContext.setCurrentId(employeeId);
在Service中获取线程局部变量中的值:
// 2.4 设置创建和更新人员id
employee.setCreateUser(BaseContext.getCurrentId());
employee.setUpdateUser(BaseContext.getCurrentId());

浙公网安备 33010602011771号