springboot中使用ThreadLocal
我们在来看下remove源码是怎么解决内存泄漏的。
·
实体类
package com.example.demo.entity;
import lombok.Data;
@Data
public class User {
//主键
private Long id;
//姓名
private String name;
}
用户全局上下文,在接口中获取用户数据
package com.example.demo.entity;
import lombok.Data;
@Data
public class SysUserContext {
/**
*
* 私有无参构造 防止外部创建对象
* @param
* @return
* @throws Exception
*/
private SysUserContext(){
}
/**
*
* 采用线程局部变量 保存每一个用户信息 每个用户都是单独的线程
* key 是线程 value是user对象
* @param
* @return
* @throws Exception
*/
private static final ThreadLocal<User>local=new ThreadLocal<User>();
/**
*
* 添加数据
* @param
* @return
* @throws Exception
*/
public static void set(User user){
local.set(user);
}
/**
*
* 删除数据
* @param
* @return
* @throws Exception
*/
public static void remove(){
local.remove();
}
/**
*
* 获取用户信息
* @param
* @return
* @throws Exception
*/
public static User get(){
return local.get();
}
}
登陆拦截器,登录成功后,把用户信息放入全局上下文中
package com.example.demo.config;
import com.example.demo.entity.SysUserContext;
import com.example.demo.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* 登陆拦截器
* @param
* @return
* @throws Exception
*/
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("方法执行之前");
//获取token
String token=request.getHeader("Authorization");
log.info("token:{}",token);
if(!StringUtils.hasText(token)){
//如果为空 拦截
//设置json类型
response.setContentType("application/json;charset=utf-8");
//返回错误信息
response.getWriter().print("token不能为空");
return false;
}
//从token中获取用户信息 我这里就写死了
User user=new User();
user.setId(1L);
user.setName("张三");
//放入本地局部线程用户上下文中
SysUserContext.set(user);
//放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("方法执行完毕之后");
//释放本地线程 防止内存泄漏
SysUserContext.remove();
}
}
注册拦截器,使得登录拦截器生效
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
*
* 注册拦截器配置
* @param
* @return
* @throws Exception
*/
@Component
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册登陆拦截器
registry.addInterceptor(loginInterceptor).
//拦截所有路径
addPathPatterns("/**")
//放行登陆
.excludePathPatterns("/login")
;
}
}
请假控制层 获取登录用户的信息
package com.example.demo.controller;
import com.example.demo.entity.SysUserContext;
import com.example.demo.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
public class QingJiaController {
@GetMapping("add")
@ResponseBody
public String add() {
log.info("请假方法");
User user= SysUserContext.get();
log.info("获取到用户信息:{}",user);
log.info("=========={}",user.hashCode());
return "success";
}
}
内存泄漏的产生
在这里我们要注意,在方法执行完毕后,一定要释放本地线程,否则会造成内存泄漏
当内存满了之后,就会内存溢出,项目就会挂掉
假如我们把这一行注释掉
SysUserContext.remove();
可以看到在内存这里,有些内存并没有被释放,一直在占用着,为什么呢?我们来看下源码
可以看到WeakReference他是弱引用,那么什么是弱引用呢?
弱引用就是在任何时候都会被垃圾回收掉
在这里他的key是弱引用,他的value是强引用,那么什么是强引用呢?
强引用就是用于不会被回收,只有强引用=null的时候,才会被回收
new了一个对象就是强引用,这个Object 就是强引用
所以如果你没有把强引用释放掉,那么他的key 虽然释放了,但是他的value一直在这里占用这内存空间,所以造成了内存泄漏
我们在来看下remove源码是怎么解决内存泄漏的
可以看到在这里把引用给断开了,也就是把强引用设置为null,所以这时候才能把强引用回收掉
解决内存泄漏
就是在afterCompletion方法执行完毕后,把当前线程删除掉

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)