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 를 호출 하여 사용자 로그 인 컨 텍스트 를 청소 합 니 다.

차단기 등록 설정

/**
 *        
 *
 * @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");
  }
}
 
 

debug 를 통 해 UserContext 의 ThreadLocal 청소 가 실행 되 지 않 았 음 을 알 수 있 습 니 다.요청 이 들 어 왔 을 때 ThreadLocal 이 존재 할 수도 있 습 니 다.요청 한 컨 텍스트 에 따라 복구 되 지 않 습 니 다.
springmvc 소스 코드 통독
tomcat 는 http 요청 을 받 은 후 최종 적 으로 spring mvcDispatcherServlet에 의 해 처 리 됩 니 다.여기 서 doDispatch 에서 그림 에 따라 준 마 를 찾 아 덩굴 을 더 듬 으 며 아래 를 내 려 다 볼 수 있다.
원본 통학:Dispatcher Servlet

/**
	 * 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->post Handle->after Complete 를 실행 합 니 다.
소스 코드 통독: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 로 돌아 갈 때 이전 트 루 로 돌아 온 handler 에서 차례대로 after Complete 를 실행 합 니 다.자신의 after Complete 는 실행 되 지 않 습 니 다.
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);
				}
			}
		}
	}
triggerAfterComplete 는(1)에 만 이상 이 발생 합 니 다.(2)preHandle 은 false 로 되 돌아 가 거나(3)정상 적 인 실행 이 끝나 야 색인 interceptorIndex 에서 순서대로 실 행 됩 니 다.
따라서 상기 소스 코드 를 바탕 으로 차단 기 를 쓸 때 preHandle 이 false 로 돌아 갈 때 after Complete 는 실행 되 지 않 는 다 는 것 을 알 수 있 습 니 다.그래서 일부 필요 한 정리 작업 이 실행 되 지 않 으 면 우리 가 만난 계 정 문자열 과 비슷 한 문제 가 발생 할 수 있 습 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기