interceptor 配合Threadlocal全局處理請求基本信息

一. 業務場景java

最近接手一個供app端調用的網關項目,app端迭代更新,接口就要相應的區分版本並作相應的邏輯區分,因此每一次請求必需要獲取版本號。app

二. 實現邏輯ide

期初的作法是在controller中直接定義 @RequestParam("name") String name,但發現調用的方法層數增長時,參數須要逐層傳遞,調用鏈上每一個方法都須要加參數,代碼及不優雅。後改成添加一個攔截器VersionInfoInterceptor,在攔截器中操做Threadlocal中的變量,代碼以下:工具

/**
 * @author:xiantao.wu
 * @createDate:2018/7/1011:25
 * 先執行preHandle方法,將須要的變量放入ThreadLocal,方法執行完執行afterCompletion,將變量remove掉,攔截器原理後續文章將會介紹
 * 
 **/
public class VersionInfoInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String clientVersion = request.getHeader("x-client-version");
        if (clientVersion != null) {
            RequestHeaderBaseInfo requestHeaderBaseInfo=new RequestHeaderBaseInfo();
            requestHeaderBaseInfo.setVersion(clientVersion);
            ThreadLocalUtil.set(requestHeaderBaseInfo);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        ThreadLocalUtil.remove();
    }
複製代碼

直接TreadlocalUtil.get()便可獲取version放入的變量測試

/**
 * @author:xiantao.wu
 * @createDate:2018/7/1011:25
 * 提供工具類你,操做Threadlocal
 **/
public class ThreadLocalUtil {
    private ThreadLocalUtil() {
    }

    private static ThreadLocal<RequestHeaderBaseInfo> LOCAL = new  TransmittableThreadLocal<>();

    public static void set(RequestHeaderBaseInfo requestHeaderBaseInfo) {
        LOCAL.set(requestHeaderBaseInfo);
    }

    public static RequestHeaderBaseInfo get() {
        return LOCAL.get();
    }

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

}
複製代碼

三. 敲黑板(須要避免的坑) 如今ThreadLocal除原生實現外,還有InheritableThreadLocal,TransmittableThreadLocal(眼尖的同窗可能已經注意到了)這兩種實現,上面選用的就是TransmittableThreadLocal,如下是三者的比較:spa

實現 可否在傳遞到子線程 使用注意點
ThreadLocal 只能在當前線程中獲取本地變量
InheritableThreadLocal 可獲取父線程的中的本地變量,線程池場景不適用,存在線程複用,會致使混
TransmittableThreadLocal 可獲取父線程中的本地變量,適用線程池場景

四. 測試結果線程

/** * Created by wuxiantao on 2018/7/14. */ public class TransmittableThreadlocalTest {code

private static ThreadLocal<String> transmittableThreadLocal=new TransmittableThreadLocal<>();

public static void main(String[] args) throws InterruptedException {
    ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(3,3,10, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>());
    transmittableThreadLocal.set("父線程設定的值");
    System.out.println(Thread.currentThread().getName()+"===="+transmittableThreadLocal.get());

    for (int i = 0; i < 5; i++) {
        threadPoolExecutor.submit(TtlCallable.get(new Callable<String>() {
            public String call() throws Exception {
                System.out.println(Thread.currentThread().getName()+"------"+transmittableThreadLocal.get());
                transmittableThreadLocal.set("我被修改了");
                return transmittableThreadLocal.get();
            }
        }));
    }
}
複製代碼

}接口

<br />使用transmimttable-threadlocal中TtlCallable.get()提交任務,在子線程中修改Threadlocal,子線程仍是會取主線程中的threadlocal
複製代碼
main====父線程設定的值
pool-1-thread-2------父線程設定的值
pool-1-thread-1------父線程設定的值
pool-1-thread-1------父線程設定的值
pool-1-thread-1------父線程設定的值
pool-1-thread-3------父線程設定的值
複製代碼

`\rem

相關文章
相關標籤/搜索