公共字段自动填充(ThreadLocal)
数据库中人员信息表
或其他表中经常会有一些工共字段用于记录操作,追踪操作人
如我们在对人员信息进行增删改查的时候需要记录修改人,修改时间等等工共字段
编写一个公共类MyMetaObjectHandler用于实现工共字段的自动填充
我们一般都会将用户的id存入HttpSession中,通过
HttpServletRequest的request.getSession().getAttribute(“XXX”);来获取相应信息,如id;
但在这个MyMetaObjectHandler工共类中,我们无法获得HttpSession对象的,所以我们需要通过其他方式获取登录的当前用户id。
解决方法:Threadlocal,他是JDK中的一个类
什么是ThreadLocal?
ThreadLocal并不是-个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时, ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal为每个线程提供单独-份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
ThreadLocal常用方法:
●publicvoidset(Tvalue)设置当前线程的线程局部变量的值
●public T get() 返回当前线程所对应的线程局部变量的值
首先确认一个事情
客户端发送的每次http请求,对应的在服务端都会分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都属于相同的一个线程:
1、LoginCheckFilter的doFilter方法
2、EmployeeController中的update方法
3、MyMetaObjecthandler的updateFill方法
能不能理解成一个请求对应一个线程:
发送一次请求,这个请求会涉及到很多类,那么对这些类中的方法处理都来自于同一个线程,所以我们可以将在这些类中涉及到的公共信息写入当前线程的数据中,这样,在这个线程中的类都可以访问这个公共数据区。
我们可以在LoginCheckFilter的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方 法来设置当前线程的线程局部变量的值(用户id),然后在MyMetaObjectHandler的updateFill方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值(用户id) 。
doFilter方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// log.info("拦截到请求:{}",request.getRequestURI());
// filterChain.doFilter(request,response);
// 1.获取本期请求的URI
String requestURI = request.getRequestURI();
log.info("拦截到请求:{}",requestURI);
// 定义不需要处理的请求路径
String[] urls = new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**"
};
// 2.判断本次请求是否需要处理
boolean check = check(urls, requestURI);
// 3.如果不需要处理,则直接放行
if (check){
log.info("拦截到请求:{}本次请求不需要处理",requestURI);
filterChain.doFilter(request,response);
return;
}
// 4.判断登录状态,如已登录,则直接放行
if (request.getSession().getAttribute("employee") !=null){
log.info("用户已登录,其登录id为{}",request.getSession().getAttribute("employee"));
Long empId =(Long) request.getSession().getAttribute("employee");
//此处用到线程,调用BaseContext中的ThreadLocal对象,将empid放入其中
BaseContext.setCurrentId(empId);
filterChain.doFilter(request,response);
return;
}
log.info("用户未登录");
// 5.如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));//将R对象转换为JSON,通过输出流返回去
return;
}
公共类
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 自定义元数据对象处理器
* @author pengdada
* @create 2022-04-22-20:43
*/
@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
/***
* 插入操作,自动填充
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共字段自动填充[insert]...");
log.info(metaObject.toString());
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime",LocalDateTime.now());
metaObject.setValue("createUser",BaseContext.getCurrentId());
metaObject.setValue("updateUser",BaseContext.getCurrentId());
}
/**
* 更新操作,自动填充
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("公共字段自动填充[update]....");
log.info(metaObject.toString());
// long id = Thread.currentThread().getId();
// log.info("线程id为:{}",id);
metaObject.setValue("updateTime",LocalDateTime.now());
metaObject.setValue("updateUser",BaseContext.getCurrentId());
}
}