ThreadLocal+Filter+SpringBoot

1、线程封闭

1.1 线程封闭

 
       当访问共享的可变数据时,通常需要同步。一种避免同步的方式就是不共享数据。如果仅在单线程内访问数据,就不需要同步,这种技术称为线程封闭(thread confinement).

       线程封闭技术一个常见的应用就是JDBC的Connection对象,JDBC规范并没有要求Connection对象必须是线程安全的,在服务器应用程序中,线程从连接池获取一个Connection对象,使用完之后将对象返还给连接池。下面介绍几种线程封闭技术:
 

1.2 ThreadLocal 类

 
       维持线程封闭性的一种更加规范方法是使用ThreadLocal类,这个类能使线程中某个值与保存值的对象关联起来。ThreadLocal类提供了 get 和 set 等访问接口或者方法,这些方法为每个使用该变量的线程都存在一份独立的副本,因此 get 总是放回当前执行线程在调用set设置的最新值。
 

扩展知识:

       ThreadLocalMap对象是以this指向的ThreadLocal对象为键进行查找的,这当然和前面set()方法的代码是相呼应的。
 
       进一步地,我们可以创建不同的ThreadLocal实例来实现多个变量在不同线程间的访问隔离,为什么可以这么做?因为不同的ThreadLocal对象作为不同键,当然也可以在线程的ThreadLocalMap对象中设置不同的值了。通过ThreadLocal对象,在多线程中共享一个值和多个值的区别,就像你在一个HashMap对象中存储一个键值对和多个键值对一样,仅此而已。
 
       也就说,每个线程都有一个ThreadLocalMap,该线程访问到某个局部变量,且该局部变量是用ThreadLocal类进行声明时,该线程就会new ThreadLocal(),然后将该ThreadLocal类的对象作为key值,所对应的局部变量作为value值保存到ThreadLocalMap中。当线程访问多个ThreadLocal类进行声明局部变量时,在ThreadLocalMap中就有多个键值对。而每个线程都有自己的ThreadLocalMap,从而达到隔离的目的了。
 
       当某个线程终止后,该线程里的ThreadLocalMap也被回收了,所以完全不用担心内存泄漏的问题。
 
       假如多线程访问的对象实例是单例的,或者说只能创建一个,那就老老实实的使用同步机制(synchronized)了.

2、ThreadLocal 使用实例

 
ThreadLocal+Filter+SpringBoot 保存当前登录用户信息
 

package com.icmc.icao.util;

import com.icmc.icao.entity.sys.IcaoUser;
import javax.servlet.http.HttpServletRequest;

/**
 * @author 
 * @title: RequestHolder
 * @description: TODO
 * @date 2020/4/13 10:35
 */
public class RequestHolder {
    
    

     private final static ThreadLocal<IcaoUser> userHoldert =
                                         new ThreadLocal<IcaoUser>();
     private static final ThreadLocal<HttpServletRequest> requestHolder = 
                                         new ThreadLocal<HttpServletRequest>();

     public static void add (IcaoUseruser) {
    
    
         userHoldert.set(user);
     }
     public static void add(HttpServletRequest request) {
    
    
        requestHolder.set(request);
     }

     public static IcaoUseruser getIcaoUser() {
    
    
        return userHoldert.get();
     }
     
     public static HttpServletRequest getCurrentRequest() {
    
    
        return requestHolder.get();
     }

     public static void remove() {
    
    
         userHoldert.remove();
         requestHolder.remove();
     }


}

Interceptor 拦截器代码

package com.icmc.icao.handler;

import com.icmc.icao.util.Log4JUtil;
import com.icmc.icao.util.RequestHolder;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 
 * @title: HttpInterceptor
 * @description: TODO
 * @date 2020/4/13 10:50
 */
public class HttpInterceptor extends HandlerInterceptorAdapter {
    
    

    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response,
                             Object handler) throws Exception {
    
    
        Log4JUtil.getLogger().info("preHandle");
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, 
                                HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
    
    
        RequestHolder.remove();
        Log4JUtil.getLogger().info("afterCompletion");
        return;
    }
}

启动类配置

package com.icmc.icao;

import com.icmc.icao.handler.HttpInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@SpringBootApplication
public class LcaoApplication implements WebMvcConfigurer {
    
    


    public static void main(String[] args) {
    
    
        SpringApplication.run(LcaoApplication.class, args);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/**");
    }
}

Filter 过滤器代码

package com.icmc.icao;
 
import com.icmc.icao.util.RequestHolder;
import com.icmc.icao.entity.sys.IcaoUser;

import org.springframework.stereotype.Component;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
 
@Component
@WebFilter(urlPatterns = "/*", filterName = "authFilter")
public class AuthFilter implements Filter {
    
    
 
    public void init(FilterConfig filterConfig) throws ServletException {
    
    
 
    }
 
    public void doFilter(ServletRequest servletRequest, 
                         ServletResponse servletResponse, 
                         FilterChain filterChain) throws IOException, ServletException {
    
    
                         
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        // 这里的实现思路是,先获取 session 中的 IcaoUser,然后放到 ThreadLocal中。
        // 所以在登录接口中,登录成功后,必须将 IcaoUser 用户对象放入session中 
        if (req.getSession() != null 
                  && req.getSession().getAttribute("user") != null)
           IcaoUser icaoUser = (IcaoUser)req.getSession().getAttribute("user");
        if (icaoUser== null) {
    
    
            String path = "/login.jsp";
            resp.sendRedirect(path);
            return;
        }
        RequestHolder.add(icaoUser);
        RequestHolder.add(req);
        filterChain.doFilter(servletRequest, servletResponse);
        return;
    }
 
    public void destroy() {
    
    
 
    }
}

用户登录控制层代码


    @RequestMapping(value = {
    
     "login" }, method = RequestMethod.POST)
    @ResponseBody
	public String login(HttpServletRequest req,
	                          String username, 
	                          String password,
	                          String checkcode) {
    
    
		// 业务层实现 参数非空验证,以及验证验证码等操作 todo……
        // 验证账号密码等,如果用户名和密码正确,将用户信息放入session
        IcaoUser icaoUser = 业务层的相关查询……
		req.getSession().setAttribute("user", icaoUser);
		// 根据业务需求返回相关信息
		return "success";

	}

业务层需要获取当前用户信息实例

 
在其他业务层需要获取到用户信息

@Service
public class IcaoOrgQualiServiceImpl implements IcaoOrgQualiService {
    
    

    @Autowired
    private IcaoOrgQualiMapper icaoOrgQualiMapper;
    
    @Override
    public void insertSelective(IcaoOrgQuali icaoOrgQualin) {
    
    
        // 获取当前用户信息
        IcaoUser icaoUser = RequestHolder.getIcaoUser();
        icaoOrgQualin.setCreateUser(icaoUser.getUserName());
        icaoOrgQualiMapper.insertSelective(icaoOrgQualin);
    }

   
}

 
 
 
 
 
 
.

猜你喜欢

转载自blog.csdn.net/weixin_41922349/article/details/105489462