短链接核心功能。创建短链接分组用gid RandomGenerator.generateRandom(6);创建一个六位数的随机数并用while循环重复创建直到数据库中没有一样的gid。公共参数package com.nageoffer.shortlink.admin.common.biz.user; import com.alibaba.ttl.TransmittableThreadLocal; import java.util.Optional; /** * 用户上下文 */ public final class UserContext { private static final ThreadLocalUserInfoDTO USER_THREAD_LOCAL new TransmittableThreadLocal(); /** * 设置用户至上下文 * * param user 用户详情信息 */ public static void setUser(UserInfoDTO user) { USER_THREAD_LOCAL.set(user); } /** * 获取上下文中用户 ID * * return 用户 ID */ public static String getUserId() { UserInfoDTO userInfoDTO USER_THREAD_LOCAL.get(); return Optional.ofNullable(userInfoDTO).map(UserInfoDTO::getUserId).orElse(null); } /** * 获取上下文中用户名称 * * return 用户名称 */ public static String getUsername() { UserInfoDTO userInfoDTO USER_THREAD_LOCAL.get(); return Optional.ofNullable(userInfoDTO).map(UserInfoDTO::getUsername).orElse(null); } /** * 获取上下文中用户真实姓名 * * return 用户真实姓名 */ public static String getRealName() { UserInfoDTO userInfoDTO USER_THREAD_LOCAL.get(); return Optional.ofNullable(userInfoDTO).map(UserInfoDTO::getRealName).orElse(null); } /** * 清理用户上下文 */ public static void removeUser() { USER_THREAD_LOCAL.remove(); } }总结如果是普通的请求会创建一个线程如果在某个类里想要获取用户的userid或者username或者realname都需要在调用方法的时候传入参数或者对象如果普通的多线程来做虽然可以实现在每一个地方都能获取这些参数而不用传参但是有一个弊端就是当在某一次请求中的某方法里创建了多线程那么这个新建的线程就获取不到了所以才需要用ttlttl可以让一次请求的所有线程里都调用得到公共参数普通的threadlocal中创建子线程无法将主线程中存储的公共参数拷贝而TransmittableThreadLocal可以拷贝。多态创建的对象是不是只能用父类中有的方法而直接创建就可以用子类中所有的方法比如ThreadLocalUserInfoDTO tl1 new TransmittableThreadLocal();只能调用子类中实现父类的方法不能用TransmittableThreadLocal中特有的。过滤器UserConfiguration用于将过滤器UserTransmitFilter交给mvc管理package com.nageoffer.shortlink.admin.common.biz.user; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSON; import com.nageoffer.shortlink.admin.common.convention.exception.ClientException; import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.StringRedisTemplate; import java.io.IOException; import static com.nageoffer.shortlink.admin.common.enums.UserErrorCodeEnum.USER_OUTLOGIN; /** * 用户信息传输过滤器 * * 公众号马丁玩编程回复加群添加马哥微信备注12306获取项目资料 */ RequiredArgsConstructor public class UserTransmitFilter implements Filter {//申明这是个过滤器 private final StringRedisTemplate stringRedisTemplate; Override // 过滤器逻辑,只拦截headers中是否有用户信息 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //第一步从请求头中获取用户信息 HttpServletRequest httpServletRequest (HttpServletRequest) servletRequest; String username httpServletRequest.getHeader(username); if(StrUtil.isNotBlank(username)){ String token httpServletRequest.getHeader(token); //第二步从Redis中获取用户信息 Object userInfoJosnStr stringRedisTemplate.opsForHash().get(login_ username, token); if(userInfoJosnStr ! null){ //第三步将用户信息转为用户信息实体 UserInfoDTO userInfoDTO JSON.parseObject(userInfoJosnStr.toString(), UserInfoDTO.class); //第四步将用户信息设置到上下文中 UserContext.setUser(userInfoDTO); }else{ throw new ClientException(USER_OUTLOGIN); } } try { filterChain.doFilter(servletRequest, servletResponse); } finally { UserContext.removeUser(); } } }Bean // 注册过滤器让spring 管理过滤器 public FilterRegistrationBeanUserTransmitFilter globalUserTransmitFilter(StringRedisTemplate stringRedisTemplate) { FilterRegistrationBeanUserTransmitFilter registration new FilterRegistrationBean(); registration.setFilter(new UserTransmitFilter(stringRedisTemplate)); registration.addUrlPatterns(/*); registration.setOrder(0); return registration; }遇到问题1.为什么拦截器中throw不会被前端接收在 Filter 中抛异常不会被 GlobalExceptionHandler 捕获❌ Tomcat 默认处理为 500 错误页面解决方案✅ 在 Filter 中直接写入 JSON 响应✅ 使用 HttpServletResponse.getWriter().write() 返回数据✅ 立即 return停止过滤器链遇到问题2.在一个用户请求后该线程下的异步处理就无法获取到全局上下文解决方案用阿里的TransmittableThreadLocal()也就是ttl他可以在创建异步的时候将上下午复制过去且线程安全。