中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringMVC中HandlerInterceptor詭異問題排查的示例分析

發布時間:2021-08-10 14:31:11 來源:億速云 閱讀:169 作者:小新 欄目:編程語言

這篇文章給大家分享的是有關SpringMVC中HandlerInterceptor詭異問題排查的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

發現問題

最近在進行壓測發現,有一些接口時好時壞,通過sentry日志平臺及sky walking平臺跟蹤發現,用戶張三獲取到的用戶上下文確是李四。

代碼走讀

用戶登錄下上文

/**
 * 用戶登錄下上文
 *
 * @author : jamesfu
 * @date : 22/5/2019
 * @time : 9:18 AM
 */
@Data
public class UserContext {
  private final static ThreadLocal<UserContext> threadLocal = new ThreadLocal<>();

  private Long id;

  private String loginName;

  public static UserContext get() {
    UserContext context = threadLocal.get();
    if (context == null) {
      // TODO(james.h.fu):根據請求上下文獲取token, 然后恢復用戶登錄下上文
      context = new UserContext() {{
        setId(1L);
        setLoginName("james.h.fu1");
      }};
      threadLocal.set(context);
    }

    return context;
  }

  public static void clear() {
    threadLocal.remove();
  }

  public static void set(UserContext context) {
    if (context != null) {
      threadLocal.set(context);
    }
  }
}

在攔截器中有調用UserContext.set恢復用戶登錄上下文,并在請求結束時調用UserContext.clear清理用戶登錄上下文。

SpringMVC中HandlerInterceptor詭異問題排查的示例分析

攔截器注冊配置

/**
 * 攔截器注冊配置
 *
 * @author : jamesfu
 * @date : 22/5/2019
 * @time : 9:15 AM
 */
@Configuration
public class FilterConfig implements WebMvcConfigurer {
  @Autowired
  private JsonRpcInterceptor jsonRpcInterceptor;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(jsonRpcInterceptor)
        .addPathPatterns("/json.rpc");
  }
}

SpringMVC中HandlerInterceptor詭異問題排查的示例分析 

SpringMVC中HandlerInterceptor詭異問題排查的示例分析 

SpringMVC中HandlerInterceptor詭異問題排查的示例分析

通過debug可以發現UserContext中的ThreadLocal的清理工作沒有得到執行。導致請求進來時,有可能ThreadLocal已存在了,就不會再根據請求上下文恢復了。

springmvc 源碼走讀

tomcat 在收到http請求后,最終會交由spring mvc的 DispatcherServlet 處理。 這里可以從doDispatch按圖索驥,順藤摸瓜地往下看起走。

源碼走讀:DispatcherServlet

/**
	 * Process the actual dispatching to the handler.
	 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
	 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
	 * to find the first that supports the handler class.
	 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
	 * themselves to decide which methods are acceptable.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws Exception in case of any kind of processing failure
	 */
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception

請求會得到分發,然后執行各個已注冊Handler的preHandle-->postHandle-->afterCompletion。

源碼走讀:HandlerExecutionChain applyPreHandle

/**
	 * Apply preHandle methods of registered interceptors.
	 * @return {@code true} if the execution chain should proceed with the
	 * next interceptor or the handler itself. Else, DispatcherServlet assumes
	 * that this interceptor has already dealt with the response itself.
	 */
	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}

當執行到preHandle返回false時,它就會從上一個返回true的handler依次往前執行afterCompletion,它自己的afterCompletion得不到執行。

triggerAfterCompletion

/**
	 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
	 * Will just invoke afterCompletion for all interceptors whose preHandle invocation
	 * has successfully completed and returned true.
	 */
	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = this.interceptorIndex; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				try {
					interceptor.afterCompletion(request, response, this.handler, ex);
				}
				catch (Throwable ex2) {
					logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
				}
			}
		}
	}

triggerAfterCompletion只會在(1)出現異常,(2)preHandle返回false 或(3)正常執行結束才會從索引interceptorIndex依次往前執行。

所以基于以上源碼可以得知,在寫攔截器時preHandle返回false時,afterCompletion是不會執行的。所以一些必要的清理工作得不到執行,會出現類似我們遇到的帳號串的問題。

感謝各位的閱讀!關于“SpringMVC中HandlerInterceptor詭異問題排查的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

桦川县| 安国市| 无极县| 朔州市| 无棣县| 江油市| 淳安县| 鲁山县| 澎湖县| 武夷山市| 柘荣县| 台南市| 兴宁市| 门头沟区| 怀集县| 荔波县| 左云县| 盱眙县| 宜春市| 重庆市| 密云县| 托克逊县| 肇源县| 仙居县| 大新县| 武穴市| 平顶山市| 固始县| 政和县| 东丰县| 景谷| 雷山县| 济阳县| 海林市| 阿瓦提县| 蓝山县| 杭锦后旗| 离岛区| 兰考县| 长武县| 汾阳市|