一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|

服務器之家 - 編程語言 - JAVA教程 - SpringCloud通用請求字段攔截處理方法

SpringCloud通用請求字段攔截處理方法

2020-07-08 14:25清茶豆奶 JAVA教程

這篇文章主要介紹了SpringCloud通用請求字段攔截處理,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

背景

SpringCloud構建的微服務系統為例,使用前后端分離的架構,每個系統都會提供一些通用的請求參數,例如移動端的系統版本信息、IMEI信息,Web端的IP信息,瀏覽器版本信息等,這些參數可能放在header里,也可以放在參數里,如果這些參數需要在每個方法內聲明定義,一來工作量太大,二是這些通用參數與業務接口方法耦合過緊,本身就是一個不好的設計。

這個問題該如何優雅地解決呢?

最佳實踐

  • 利用SpringMVC提供攔截器,對匹配的請求,抽取通用的header信息(假設通用字段全部放在header里)
  • 將每個請求的信息單獨隔離開,互不干擾。
  • Controller層使用時,可以將在該請求線程(http線程)里將通用的header信息提取出來使用。
  • 請求線程完成時,相應的header頭信息對象需要回收銷毀。
  • 實現方式SpringMVA提供的HandlerInterceptorAdapter可以拿來使用,繼承實現即可。
  • 使用ThreadLocal記錄每個請求的信息,ThreadLocal有隔離線程變量的作用。

HandlerInterceptorAdapter的源碼實現及注釋

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
    // 在業務接口方法處理之前被調用,可以在這里對通用的header信息進行提取
        return true;
    }
 
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable ModelAndView modelAndView) throws Exception {
        // 這個方法在業務接口方法執行完成后,生成SpringMVC ModelAndView之前被調用
        // 今天這個案例我們不用此方法,故可以不實現。
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable Exception ex) throws Exception {
        // 這個方法在DispatcherServlet完全處理完成后被調用,可以在這里對ThreadLocal的內容進行釋放
    }
 
    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response,
            Object handler) throws Exception {
        // 這個方法用來處理異步主動,但也會先行調用preHandle,然后執行此方法,異步線程完成后會執行postHandle和afterCompletion兩方法,這里暫時用不上。
    }
}

ThreadLocal的源碼主要實現及注釋

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class ThreadLocal<T> {
  
  protected T initialValue() {
    return null;
  }
 
  public T get() {
        // 獲取當前的線程
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
      ThreadLocalMap.Entry e = map.getEntry(this);
      if (e != null) {
        @SuppressWarnings("unchecked")
        T result = (T)e.value;
        return result;
      }
    }
    return setInitialValue();
  }
 
  private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
      map.set(this, value);
    else
      createMap(t, value);
    return value;
  }
 
  public void set(T value) {
        // 獲取當前的線程
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
      map.set(this, value);
    else
      createMap(t, value);
  }
 
   public void remove() {
     ThreadLocalMap m = getMap(Thread.currentThread());
     if (m != null)
       m.remove(this);
   }
 
  ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
  }
 
  void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
  }
}

簡單來說,ThreadLocal最關鍵的get()和set()方法,都是針對當前線程來操作的,調用set()方法時把值放到ThreadMap(Map的一種實現)中,以當前線程的hash值為key,get()方法則對應以當前線程作為key來取值,從而實現每個線程的數據是隔離的效果。

另附上ThreadLocal類源碼解讀的導圖,僅供參考

SpringCloud通用請求字段攔截處理方法

案例實戰

我們對實際業務系統進行簡化處理,假定header信息固定有ip,uid,deviceId三個信息,按照上文的實現思路,開始案例演示。

DTO定義

通用的header信息,使用Dto對象進行封裝:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Data
public class CommonHeader implements Serializable {
 
    private static final long serialVersionUID = -3949488282201167943L;
    
    /**
     * 真實ip
     */
    private String ip;
 
    /**
     * 設備id
     */
    private String deviceId;
 
    /**
     * 用戶uid
     */
    private Long uid;
    
    // 省略getter/setter/構造器
}

定義Request請求的封裝類Dto,并引入ThreadLocal:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
 * 將公共請求頭信息放在ThreadLocal中去
 */
public class RequestWrap {
 
    private static ThreadLocal<CommonHeader> current = new ThreadLocal<>();
 
  /**
     * 獲取靜態的ThreadLocal對象
     * @return
     */
    public static ThreadLocal<CommonHeader> getCurrent() {
        return current;
    }
    
    /**
     * 獲取ip
     * @return
     */
    public static String getIp() {
        CommonHeader request = current.get();
        if (request == null) {
            return StringUtils.EMPTY;
        }
        return request.getIp();
    }
 
    /**
     * 獲取uid
     * @return
     */
    public static Long getUid() {
        CommonHeader request = current.get();
        if (request == null) {
            return null;
        }
        return request.getUid();
    }
 
    /**
     * 獲取封裝對象
     * @return
     */
    public static CommonHeader getCommonReq() {
        CommonHeader request = current.get();
        if (request == null) {
            return new CommonHeader(StringUtils.EMPTY, StringUtils.EMPTY,0L);
        }
        return request;
    }
}

工具類

這里添加一個簡單的工具類,將HttpServletRequest通過getHeader方法,生成CommonHeader類:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class HttpUtil {
    /**
     * 獲取請求頭信息
     *
     * @param request
     * @return
     */
    public static CommonHeader getCommonHeader(HttpServletRequest request) {
        String UID = request.getHeader("uid");
        Long uid = null;
        if (StringUtils.isNotBlank(UID)) {
            uid = Long.parseLong(UID);
        }
        return new CommonHeader(HttpUtil.getIp(request), request.getHeader("deviceId"), uid);
    }
 
    /**
     * 獲取IP
     *
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
 
        if (null != ip && !"".equals(ip.trim()) && !"unknown".equalsIgnoreCase(ip)) {
            int index = ip.indexOf(',');
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (null != ip && !"".equals(ip.trim()) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }
}

攔截器類實現

最核心的實現終于出場了,這里繼承HandlerInterceptorAdapter,這里作了簡化處理:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
 * 請求頭處理
 *
 * @author yangfei
 */
@Component
public class BaseInterceptor extends HandlerInterceptorAdapter {
 
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BaseInterceptor.class);
 
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        RequestWrap.getThreadLocal().set(HttpUtil.getCommonHeader(request));
        return true;
    }
 
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
    }
 
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        RequestWrap.getThreadLocal().remove();
    }
 
    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
    }
}

如上一章節描述的邏輯,在preHandle方法內將request中的ip,uid,deviceId封裝到RequestWrap對象里,在afterCompletion中對該線程的ThreadLocal值進行釋放。

業務接口方法的使用

在Controller類的接口方法中,如要獲取uid信息,只需要調用RequestWrap.getUid()方法即可,再也不需要在每個接口上聲明uid參數了,如下示例:

?
1
2
3
4
5
6
7
/**
 * 獲取用戶基礎信息
 */
@PostMapping(value = "/user/info")
public Response<UserInfo> getUserInfo() {
    return userManager.getUserInfo(RequestWrap.getUid());
}

總結

這個實戰的目標是解決通用header信息的在接口的重復定義問題,基于HandlerInterceptorAdapter攔截器的實現,ThreadLocal對線程訪問數據的隔離來實現的,在實際生產項目應用中有很好的借鑒意義,希望對你有幫助。

到此這篇關于SpringCloud通用請求字段攔截處理方法的文章就介紹到這了,更多相關SpringCloud請求字段攔截內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/huangying2124/p/13264753.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 99久久6er热免费精品 | 好男人在线观看hd中字 | 2020韩国r级理论片在线观看 | 日本在线一区二区 | 久久免费特黄毛片 | 超级毛片 | 男女精品视频 | 精品一区二区三区高清免费观看 | 高清免费毛片 | 国产人成77777视频网站 | 天堂资源8中文最新版 | 视频在线观看高清免费 | 国产在线欧美精品 | 欧美专区综合 | 天天做天天爱天天爽综合网 | 手机看片一区二区 | 五月最新商场女厕所高跟嘘嘘 | 99精品全国免费7观看视频 | 国产成人精品免费久久久久 | 暖暖在线日本 | 91av俱乐部 | 高清一区| 午夜神器18以下不能进免费 | 国产精品原创巨作无遮挡 | 农村脱精光一级 | 精品久久香蕉国产线看观看亚洲 | 91国内精品 | 成人福利在线 | 免费国产成人 | 亚洲高清视频免费 | 免费一级黄 | 欧美免赞性视频 | 国产午夜免费 | 无限观看社区在线视频 | china中国xxxxfree china国产bbw | 国产 日韩 欧美 综合 | 日本免费观看95视频网站 | 9999视频| 九九九久久久 | 久久se视频精品视频在线 | 欧美日韩国产中文字幕 |