最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時間:2020年07月08日 09:49:02   作者:清茶豆奶  
這篇文章主要介紹了SpringCloud通用請求字段攔截處理,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

背景

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

這個問題該如何優(yōu)雅地解決呢?

最佳實踐

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

HandlerInterceptorAdapter的源碼實現(xiàn)及注釋

public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
    // 在業(yè)務(wù)接口方法處理之前被調(diào)用,可以在這里對通用的header信息進行提取
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
		// 這個方法在業(yè)務(wù)接口方法執(zhí)行完成后,生成SpringMVC ModelAndView之前被調(diào)用
		// 今天這個案例我們不用此方法,故可以不實現(xiàn)。
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
		// 這個方法在DispatcherServlet完全處理完成后被調(diào)用,可以在這里對ThreadLocal的內(nèi)容進行釋放
	}

	@Override
	public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response,
			Object handler) throws Exception {
		// 這個方法用來處理異步主動,但也會先行調(diào)用preHandle,然后執(zhí)行此方法,異步線程完成后會執(zhí)行postHandle和afterCompletion兩方法,這里暫時用不上。
	}
}

ThreadLocal的源碼主要實現(xiàn)及注釋

public class ThreadLocal<T> {
  
  protected T initialValue() {
    return null;
  }

  public T get() {
		// 獲取當(dāng)前的線程
    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) {
		// 獲取當(dāng)前的線程
    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最關(guān)鍵的get()和set()方法,都是針對當(dāng)前線程來操作的,調(diào)用set()方法時把值放到ThreadMap(Map的一種實現(xiàn))中,以當(dāng)前線程的hash值為key,get()方法則對應(yīng)以當(dāng)前線程作為key來取值,從而實現(xiàn)每個線程的數(shù)據(jù)是隔離的效果。

另附上ThreadLocal類源碼解讀的導(dǎo)圖,僅供參考

案例實戰(zhàn)

我們對實際業(yè)務(wù)系統(tǒng)進行簡化處理,假定header信息固定有ip,uid,deviceId三個信息,按照上文的實現(xiàn)思路,開始案例演示。

DTO定義

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

@Data
public class CommonHeader implements Serializable {

	private static final long serialVersionUID = -3949488282201167943L;
	
	/**
	 * 真實ip
 	 */
	private String ip;

	/**
	 * 設(shè)備id
 	 */
	private String deviceId;

	/**
	 * 用戶uid
 	 */
	private Long uid;
	
	// 省略getter/setter/構(gòu)造器
}

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

/**
 * 將公共請求頭信息放在ThreadLocal中去
 */
public class RequestWrap {

	private static ThreadLocal<CommonHeader> current = new ThreadLocal<>();

  /**
	 * 獲取靜態(tài)的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類:

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();
	}
}

攔截器類實現(xiàn)

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

/**
 * 請求頭處理
 *
 * @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 {
	}
}

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

業(yè)務(wù)接口方法的使用

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

/**
 * 獲取用戶基礎(chǔ)信息
 */
@PostMapping(value = "/user/info")
public Response<UserInfo> getUserInfo() {
	return userManager.getUserInfo(RequestWrap.getUid());
}

總結(jié)

這個實戰(zhàn)的目標(biāo)是解決通用header信息的在接口的重復(fù)定義問題,基于HandlerInterceptorAdapter攔截器的實現(xiàn),ThreadLocal對線程訪問數(shù)據(jù)的隔離來實現(xiàn)的,在實際生產(chǎn)項目應(yīng)用中有很好的借鑒意義,希望對你有幫助。

到此這篇關(guān)于SpringCloud通用請求字段攔截處理方法的文章就介紹到這了,更多相關(guān)SpringCloud請求字段攔截內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mybatis中updateBatch實現(xiàn)批量更新

    Mybatis中updateBatch實現(xiàn)批量更新

    本文主要介紹了Mybatis中updateBatch實現(xiàn)批量更新,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 實例分析Try {} Catch{} 作用

    實例分析Try {} Catch{} 作用

    本文是通過一個簡單的實例,向大家介紹了Try {} Catch{}的意義和作用,非常的實用,有需要的小伙伴可以參考下。
    2015-10-10
  • Java創(chuàng)建型設(shè)計模式之抽象工廠模式(Abstract?Factory)

    Java創(chuàng)建型設(shè)計模式之抽象工廠模式(Abstract?Factory)

    當(dāng)系統(tǒng)所提供的工廠所需生產(chǎn)的具體產(chǎn)品并不是一個簡單的對象,而是多個位于不同產(chǎn)品等級結(jié)構(gòu)中屬于不同類型的具體產(chǎn)品時需要使用抽象工廠模式,抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態(tài)
    2022-09-09
  • Spring Boot詳解各類請求和響應(yīng)的處理方法

    Spring Boot詳解各類請求和響應(yīng)的處理方法

    平時只是在用SpringBoot框架,但并沒有詳細研究過請求和響應(yīng)執(zhí)行的一個具體過程,所以本文主要來梳理一下SpringBoot請求和響應(yīng)的處理過程
    2022-07-07
  • SpringBoot中@ConditionalOnBean實現(xiàn)原理解讀

    SpringBoot中@ConditionalOnBean實現(xiàn)原理解讀

    這篇文章主要介紹了SpringBoot中@ConditionalOnBean實現(xiàn)原理,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Java簡易學(xué)生成績系統(tǒng)寫法實例

    Java簡易學(xué)生成績系統(tǒng)寫法實例

    在本篇文章里小編給大家分享的是關(guān)于Java簡易學(xué)生成績系統(tǒng)寫法實例以及相關(guān)知識點,有需要的朋友們可以學(xué)習(xí)下。
    2019-09-09
  • MyBatis動態(tài)SQL foreach標(biāo)簽實現(xiàn)批量插入的方法示例

    MyBatis動態(tài)SQL foreach標(biāo)簽實現(xiàn)批量插入的方法示例

    這篇文章主要介紹了MyBatis動態(tài)SQL foreach標(biāo)簽實現(xiàn)批量插入的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • SpringBoot實現(xiàn)單文件與多文件上傳功能

    SpringBoot實現(xiàn)單文件與多文件上傳功能

    這篇文章主要介紹了SpringBoot實現(xiàn)單文件與多文件上傳功能,Spring?MVC對文件上傳做了簡化,而在Spring?Boot中對此做了更進一步的簡化,文件上傳變得更為方便,下面開始演示,需要的小伙伴可以參考一下,希望對你有所幫助
    2022-01-01
  • SpringBoot使用minio及配置代碼

    SpringBoot使用minio及配置代碼

    MinIO是一個非常輕量的服務(wù),可以很簡單的和其他應(yīng)用的結(jié)合,類似?NodeJS,?Redis?或者?MySQL。本文重點給大家介紹SpringBoot使用minio及配置代碼,感興趣的朋友一起看看吧
    2022-02-02
  • Java中的Apache?Commons?Math使用詳解

    Java中的Apache?Commons?Math使用詳解

    Java中的Apache?Commons?Math是一個開源的數(shù)學(xué)庫,它提供了許多常用的數(shù)學(xué)函數(shù)和算法,這個庫對于需要處理大量數(shù)據(jù)的開發(fā)者來說非常有用,因為它可以大大簡化代碼并提高效率,本文給大家詳解講解Java中的Apache?Commons?Math知識,感興趣的朋友跟隨小編一起看看吧
    2023-08-08

最新評論

兰西县| 大田县| 塔河县| 威远县| 清徐县| 吉林省| 专栏| 英吉沙县| 安平县| 兴化市| 库车县| 武汉市| 赫章县| 龙泉市| 宁德市| 石屏县| 万安县| 景泰县| 卓尼县| 乐山市| 万全县| 沾益县| 达拉特旗| 北流市| 子洲县| 合肥市| 沅陵县| 墨竹工卡县| 阜南县| 罗平县| 温泉县| 尉氏县| 离岛区| 安阳市| 万全县| 巴林右旗| 南华县| 望谟县| 清水县| 天镇县| 吕梁市|