实体类

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方法执行完毕后,把当前线程删除掉

Logo

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

更多推荐