自定义元数据处理器(公共字段自动填充)
在接口开发(新增、编辑)中常常会遇到一些公共的业务逻辑
- 创建时间
- 修改时间
- 修改人
- 创建人
/**
* 新增员工
* @param employee
* @return
*/
@PostMapping
public R<String> save(HttpServletRequest request, @RequestBody Employee employee) {
log.info("新增员工", employee.toString());
// 设置初始密码
employee.setPassword(DigestUtils.md5DigestAsHex("12345".getBytes()));
// 自动填充去处理
// employee.setCreateTime(LocalDateTime.now());
// employee.setUpdateTime(LocalDateTime.now());
//
// // 获取登陆用户id
// Long empId = (Long)request.getSession().getAttribute("employee");
// employee.setCreateUser(empId);
// employee.setUpdateUser(empId);
employeeService.save(employee);
return R.success("新增员工成功");
}
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
// 插入数据时拦截操作
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共字段自动填充【insert】");
log.info(metaObject.toString());
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
// TODO
metaObject.setValue("createUser", new Long(当前登陆用户ID));
metaObject.setValue("updateUser", new Long(当前登陆用户ID));
}
// 编辑数据时拦截操作
@Override
public void updateFill(MetaObject metaObject) {
log.info("公共字段自动填充【update】");
long id = Thread.currentThread().getId();
log.info("当前线程ID {}", id);
log.info(metaObject.toString());
}
}
如何去处理当前登陆用户ID呢
insertFilter无法注入HttpServelet 也就是说无法拿到httpRequest, 所以五法通过session拿到当前登陆的用户ID
基于线程去处理
客户端每次请求服务端,服务端都会创建一个单独的线程处理该请求,而该请求所经过的每一个类都会是相同的线程,比如:过滤器ServletComponentScan、控制器Controll、Service服务器等
ThreadLocal
ThreadLocal并不是一个Thread,而是Thread的局部变量,当使用ThreadLocal去维护变量时,ThreadLocal为每个使用该变量的线程去提供独立的副本,所以每个线程都可以独立改变自己的副本,而不会影响其他线程对应的副本,ThreadLocal为每个线程提供一个独立的存储空间,具备线程隔离的效果,只有在同一个线程内才能获取到对应的值,线程外不能访问
解决方式
可以通过过滤器拦截该请求判断是否登陆时获取到登陆的用户id,然后再ThreadLocal传给元数据处理器
实现步骤
1、编写BaseContext工具类(基于ThreadLocal封装的工具类)
2、在LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登陆的用户ID
3、在MyMetaObjectHandler方法中调用BaseContext获取登陆用户ID
// BaseContext.java
/**
* 基于ThreadLocal封装的工具类,用于保存当前登陆用户的ID
*/
public class BaseContext {
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
}
// LoginCheckFilter.java
//4、判断登录状态,如果已登录,则直接放行
if(request.getSession().getAttribute("employee") != null){
log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee"));
Long empId = (Long) request.getSession().getAttribute("employee");
BaseContext.setCurrentId(empId);
// 每个请求都会开辟一个单独的线程,在这个请求所经过的类都会是同一个线程ID
// long id = Thread.currentThread().getId();
// log.info("当前线程ID {}", id);
filterChain.doFilter(request,response);
return;
}
// MyMetaObjectHandler.java
/**
* 自定义元数据处理器
*/
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共字段自动填充【insert】");
log.info(metaObject.toString());
Long empId = BaseContext.getCurrentId();
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("createUser", new Long(empId));
metaObject.setValue("updateUser", new Long(empId));
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("公共字段自动填充【update】");
long id = Thread.currentThread().getId();
log.info("当前线程ID {}", id);
log.info(metaObject.toString());
}
}