spring boot 使用ThreadLocal实例

本文参考慕课教程给出一个在spring boot中使用ThreadLocal实现线程封闭的实例。

首先创建一个包含ThreadLocal成员变量的实例:

public class RequestHolder {

    private final static ThreadLocal<Long> requestHolder = new ThreadLocal<>();
    public static void add(Long id) {
        requestHolder.set(id);
    }

    public static Long getId() {
        return requestHolder.get();
    }

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

编写一个Controller类,请求该类的test()方法获取ThreadLocal中存储的id:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/threadLocal")
public class ThreadLocalController {

    @RequestMapping("/test")
    @ResponseBody
    public Long test() {
        return RequestHolder.getId();
    }
}

编写过滤器,在请求到达Servlet之前(请求->tomcat容器->filter->servlet->inteceptor->controller),将当前线程的id添加到ThreadLocal中:

import com.mmall.concurrency.example.threadLocal.RequestHolder;
import lombok.extern.slf4j.Slf4j;

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 java.io.IOException;

@Slf4j
public class HttpFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        log.info("do filter, {}, {}", Thread.currentThread().getId(), request.getServletPath());
        //在ThreadLocal中添加当前线程的id
        RequestHolder.add(Thread.currentThread().getId());
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

编写拦截器,当请求处理完成后(从Controller返回后),清除ThreadLocal中的id,避免内存泄漏。

import com.mmall.concurrency.example.threadLocal.RequestHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

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

@Slf4j
public class HttpInterceptor extends HandlerInterceptorAdapter {

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

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("ThreadId:"+RequestHolder.getId());
        RequestHolder.remove();
        log.info("afterCompletion");
        return;
    }
}

最后,我们需要在spring boot启动类上注册我们定义的Filer及Inteceptor,并设置拦截路径。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@SpringBootApplication
public class ConcurrencyApplication extends WebMvcConfigurerAdapter{

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

	@Bean
	public FilterRegistrationBean httpFilter() {
		FilterRegistrationBean registrationBean = new FilterRegistrationBean();
		registrationBean.setFilter(new HttpFilter());
		registrationBean.addUrlPatterns("/threadLocal/*");
		return registrationBean;
	}

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

在浏览器或者postman中输入http://localhost:8080/threadLocal/test

观察输出结果:

2018-11-09 11:16:51.287  INFO 34076 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-11-09 11:16:51.290  INFO 34076 --- [           main] c.m.concurrency.ConcurrencyApplication   : Started ConcurrencyApplication in 1.718 seconds (JVM running for 2.132)
2018-11-09 11:17:03.060  INFO 34076 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-11-09 11:17:03.060  INFO 34076 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2018-11-09 11:17:03.072  INFO 34076 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 12 ms
2018-11-09 11:17:03.078  INFO 34076 --- [nio-8080-exec-2] com.mmall.concurrency.HttpFilter         : do filter, 29, /threadLocal/test
2018-11-09 11:17:03.090  INFO 34076 --- [nio-8080-exec-2] com.mmall.concurrency.HttpInterceptor    : preHandle
2018-11-09 11:17:03.124  INFO 34076 --- [nio-8080-exec-2] com.mmall.concurrency.HttpInterceptor    : ThreadId:29
2018-11-09 11:17:03.124  INFO 34076 --- [nio-8080-exec-2] com.mmall.concurrency.HttpInterceptor    : afterCompletion

从打印的日志结果中,我们看到在Filter中我们将当前线程的id 29添加到了ThreadLocal中,随后在Inteceptor中打印并删除了id。

猜你喜欢

转载自blog.csdn.net/qq_27127145/article/details/83894400