blog

Springbootのインターセプタの使用とその基礎となるソースコードの解析

最近、基本的なフィルタとインターセプタから分析を開始するだけでなく、jettyのフィルタソースコードを分析し、このtomcat内のフィルタの違いを助けるために準備ができて、マイクロサービスを開発したば...

Feb 15, 2020 · 9 min. read
シェア

トピックの冒頭では、名前が示すようにインターセプタは、一連の操作の要求を傍受することです。最初に操作の使用例を示します。

1 @Configuration
2 public class WebConfiguration implements WebMvcConfigurer {
3 
4 @Override
5 public void addInterceptors(InterceptorRegistry registry) {
6 registry.addInterceptor(new TstCfg());
7 }
8 }
 1 /**
 2 * @title: TstCfg
 3 * @Author junyu
 4 * 古い路地に、太陽のように微笑む白いシャツを着たティーンエイジャーがいた。
 5 * 晴れ着を着て子供のように泣いていた女の子を覚えている。
 6 * 彼は、木が曲がるとき、人々は古い、桃の花が落ちる白い髪を参照してください、彼は彼女がまだ笑うだろう冗談を言った、それは良いことだ。
 7 * 国号が変わり、墓に草が生え、大地が内保橋を渡ったときに振り返ったとき、彼がまだ見ていてくれたら、それほど悪いことはないと彼女は言った。
 8 * @Date: 2020/7/29 11:53
 9 * @Version 1.0
10 */
11 public class TstCfg extends HandlerInterceptorAdapter {
12 
13 @Override
14 public boolean preHandle(HttpS request, HttpS response, Object handler) throws Exception {
15 System.out.println("前");
16 return super.preHandle(request, response, handler);
17 }
18 
19 @Override
20 public void postHandle(HttpS request, HttpS response, Object handler, ModelAndView modelAndView) throws Exception {
21 System.out.println("后");
22 }
23 
24 @Override
25 public void afterCompletion(HttpS request, HttpS response, Object handler, Exception ex) throws Exception {
26 System.out.println("それは常にそこにある。");
27 System.out.println(1/0);
28 }
29 }

最初に思いつくのは、インターセプターが

設定クラスWebConfigurationは、WebMvcConfigurerを継承し、addInterceptorsメソッドをオーバーライドするので、インターセプタは、この時点で組み立てられます。今回は、インターセプターの構成例を書いた構成が------WebMvcConfigurerを継承する必要がある理由を知るために、もちろん、あなたはまた、他のクラスのこのクラスで実装されている継承に行くことができますが、彼らはインターセプターを追加するために行くことができるので、ブロガーは個人的に試してみましたので、イメージを貼り付けることはありません!

さて、インターセプターが追加されました。では、いつインターセプターを呼び出すのでしょうか?ブラウザがアドレスをリクエストするとき、いくつかのステップがあります:

ステップ1: tomcatコンテナが最初にリクエストを受信し、ここにDispatcherServletが入ります。

2番目のステップ:もちろん、最初のインターセプタを行くことはありません、インターセプタは、Springbootのフレームワークで管理され、現在もサーブレットで、それは最初のイメージを貼り付けるには、この手順をフィルタフィルタに行くので:公式のコードが長すぎる、画面は、フィルタのチェーンを作成するプロセスの前に、インターセプトすることはできません:など、次回は、tomcatでフィルタのjettyチェーンを与えるためにjettyのフィルタチェーンとtomcatのフィルタチェーンの違い

ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

ステップ3: フィルターが通らなかったら、インターセプターを呼び出すことはありません。コードを貼り付けると、フィルターが通過した後にインターセプターを呼び出す方法です。

1 private void internalDoFilter(ServletRequest request,
 2 ServletResponse response)
 3 throws IOException, ServletException {
 4 //ここでは、フィルターチェーンのすべてのフィルターを呼び出すが、説明に集中せず、次のインターセプターの呼び出しを見てほしい。
 5 // Call the next filter if there is one
 6 if (pos < n) {
 7 ApplicationFilterConfig filterConfig = filters[pos++];
 8 try {
 9 Filter filter = filterConfig.getFilter();
10 
11 if (request.isAsyncSupported() && "false".equalsIgnoreCase(
12 filterConfig.getFilterDef().getAsyncSupported())) {
13 request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
14 }
15 if( Globals.IS_SECURITY_ENABLED ) {
16 final ServletRequest req = request;
17 final ServletResponse res = response;
18 Principal principal =
19 ((HttpSst) req).getUserPrincipal();
20 
21 Object[] args = new Object[]{req, res, this};
22 SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
23 } else {
24 filter.doFilter(request, response, this);
25 }
26 } catch (IOException | ServletException | RuntimeException e) {
27 throw e;
28 } catch (Throwable e) {
29 e = ExceptionUtils.unwrapInvocationTargetException(e);
30 ExceptionUtils.handleThrowable(e);
31 throw new ServletException(sm.getString("filterChain.filter"), e);
32 }
33 return;
34 }
35 
36 // We fell off the end of the chain -- call the servlet instance
37 try {
38 if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
39 lastServicedRequest.set(request);
40 lastServicedResponse.set(response);
41 }
42 
43 if (request.isAsyncSupported() && !servletSupportsAsync) {
44 request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
45 Boolean.FALSE);
46 }
47 // Use potentially wrapped request from this point
48 if ((request instanceof HttpSst) &&
49 (response instanceof HttpSse) &&
50 Globals.IS_SECURITY_ENABLED ) {
51 final ServletRequest req = request;
52 final ServletResponse res = response;
53 Principal principal =
54 ((HttpSst) req).getUserPrincipal();
55 Object[] args = new Object[]{req, res};
56 SecurityUtil.doAsPrivilege("service",
57 servlet,
58 classTypeUsedInService,
59 args,
60 principal);
61 } else {
62 //フィルターがやっと完成し、いよいよ正式にメソッドを呼び出す時が来た!
63 servlet.service(request, response);
64 }
65 } catch (IOException | ServletException | RuntimeException e) {
66 throw e;
67 } catch (Throwable e) {
68 e = ExceptionUtils.unwrapInvocationTargetException(e);
69 ExceptionUtils.handleThrowable(e);
70 throw new ServletException(sm.getString("filterChain.servlet"), e);
71 } finally {
72 if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
73 lastServicedRequest.set(null);
74 lastServicedResponse.set(null);
75 }
76 }
77 }

実際には、DispatcherServletのdoDispatchメソッドを呼び出すことになります。

1 protected void doDispatch(HttpS request, HttpS response) throws Exception {
 2 HttpS processedRequest = request;
 3 HandlerExecutionChain mappedHandler = null;
 4 boolean multipartRequestParsed = false;
 5 
 6 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 7 
 8 try {
 9 ModelAndView mv = null;
10 Exception dispatchException = null;
11 
12 try {
13 processedRequest = checkMultipart(request);
14 multipartRequestParsed = (processedRequest != request);
15 
16 // Determine handler for the current request.
17 mappedHandler = getHandler(processedRequest);
18 if (mappedHandler == null) {
19 noHandlerFound(processedRequest, response);
20 return;
21 }
22 
23 // Determine handler adapter for the current request.
24 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
25 
26 // Process last-modified header, if supported by the handler.
27 String method = request.getMethod();
28 boolean isGet = "GET".equals(method);
29 if (isGet 
 "HEAD".equals(method)) {
30 long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
31 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
32 return;
33 }
34 }
35 //すべてのインターセプターは、メソッドを呼び出す前にインターセプトを開始する。return falseでインターセプターを返すと、直接メソッドを呼び出すことはない!ソースコードを以下に示す
36 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
37 return;
38 }
39 
40 // Actually invoke the handler.
41 //最下層のレイヤーはリフレクションを実行し、リクエストされたメソッドを呼び出す。
42 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
43 
44 if (asyncManager.isConcurrentHandlingStarted()) {
45 return;
46 }
47 
48 applyDefaultViewName(processedRequest, mv);
49 //インターセプターのpostHandleを呼び出す。
50 mappedHandler.applyPostHandle(processedRequest, response, mv);
51 }
52 catch (Exception ex) {
53 dispatchException = ex;
54 }
55 catch (Throwable err) {
56 // As of 4.3, we're processing Errors thrown from handler methods as well,
57 // making them available for @ExceptionHandler methods and other scenarios.
58 dispatchException = new NestedServletException("Handler dispatch failed", err);
59 }
60 //このメソッドではさらにいくつかのロジックが実行され、最終的にtriggerAfterCompletionメソッドが呼び出される。
61 processDispatchResult;
62 }
63 catch (Exception ex) {
64 //そのため、例外が発生してもしなくても、インターセプター・メソッドのafterCompletionメソッドが呼ばれることになる!
65 triggerAfterCompletion;
66 }
67 catch (Throwable err) {
68 //つまり、例外が発生してもしなくても、インターセプター・メソッドのafterCompletionメソッドが呼ばれることになる!
69 triggerAfterCompletion(processedRequest, response, mappedHandler.
70 new NestedServletException("Handler processing failed", err));
71 }
72 finally {
73 if (asyncManager.isConcurrentHandlingStarted()) {
74 // Instead of postHandle and afterCompletion
75 if (mappedHandler != null) {
76 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
77 }
78 }
79 else {
80 // Clean up any resources used by a multipart request.
81 if (multipartRequestParsed) {
82 cleanupMultipart(processedRequest);
83 }
84 }
85 }
86 }

さて、いよいよインターセプター・メソッドをひとつずつ始める番です:

1 boolean applyPreHandle(HttpS request, HttpS response) throws Exception {
 2 HandlerInterceptor[] interceptors = getInterceptors();
 3 if (!ObjectUtils.isEmpty(interceptors)) {
 4 for (int i = 0; i < interceptors.length; i++) {
 5 HandlerInterceptor interceptor = interceptors[i];
 6 //すべてのインターセプターのpreHandleメソッドを呼び出す
 7 if (!interceptor.preHandle(request, response, this.handler)) {
 8 //preHandleメソッドが通らなかった場合でも、このtriggerAfterCompletionメソッドは呼び出される。
 9 triggerAfterCompletion;
10 return false;
11 }
12 this.interceptorIndex = i;
13 }
14 }
15 return true;
16 }
 1 void applyPostHandle(HttpS request, HttpS response, @Nullable ModelAndView mv)
 2 throws Exception {
 3 
 4 HandlerInterceptor[] interceptors = getInterceptors();
 5 if (!ObjectUtils.isEmpty(interceptors)) {
 6 for (int i = interceptors.length - 1; i >= 0; i--) {
 7 HandlerInterceptor interceptor = interceptors[i];
 8 //インターセプターのpostHandleメソッドを呼び出す。
 9 interceptor.postHandle(request, response, this.handler, mv);
10 }
11 }
12 }
 1 void triggerAfterCompletion(HttpS request, HttpS response, @Nullable Exception ex)
 2 throws Exception {
 3 
 4 HandlerInterceptor[] interceptors = getInterceptors();
 5 if (!ObjectUtils.isEmpty(interceptors)) {
 6 for (int i = this.interceptorIndex; i >= 0; i--) {
 7 HandlerInterceptor interceptor = interceptors[i];
 8 try {
 9 //インターセプターのafterCompletionメソッドへの呼び出しは、例外の有無にかかわらず行われるが、メソッドが例外を報告すれば、それはキャッチされる。
10 //プログラムの通常の動作には影響しない。
11 interceptor.afterCompletion(request, response, this.handler, ex);
12 }
13 catch (Throwable ex2) {
14 logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
15 }
16 }
17 }
18 }

次のものが出力されますが、リクエストのレスポンスには影響しません:

その場合でも、クライアントへの応答は正常に返されます:

Read next

NodeJsの基礎

簡単に言うと、Node.jsはサーバーサイドで動作します。 Node.js は Chrome V8 エンジンに基づいた実行環境です。 Node.jsは、GoogleのV8エンジンをベースにしたイベント駆動型のI/Oサーバーサイド環境で、Javas...

Feb 15, 2020 · 11 min read