SpringBoot インターセプターのソースコード分析

PHPz
リリース: 2023-05-15 12:28:23
転載
1538 人が閲覧しました

1. インターセプターとは何ですか?

Java のインターセプター (インターセプター) は、アクションの呼び出しを動的にインターセプトするオブジェクトであり、開発者がアクションの前後にコードを実行できるようにするメカニズムを提供します。は、アクションが実行される前にその実行を防ぐこともでき、アクション内のコードの再利用可能な部分を抽出する方法も提供します。 AOP では、インターセプターを使用してメソッドまたはフィールドがアクセスされる前にインターセプトし、その前後に特定の操作を追加します。

上記のアクションは通常、コントローラー層のインターフェイスを指します。

2. カスタマイズされたインターセプター

一般に、インターセプターのカスタマイズは 3 つのステップに分かれます

(1) HandlerInterceptor インターフェイスを実装するインターセプターを作成します。

(2) インターセプタをコンテナに登録します。

(3) インターセプト ルールを設定します。

2.1 インターセプターの作成

新しい SpringBoot プロジェクトを作成し、インターセプター LoginInterceptor をカスタマイズして、非ログイン状態で特定のリクエストをインターセプトします。 JDK 1.8 以降、default キーワードを含むインターフェース メソッドはデフォルトの実装を持つことができるため、インターフェースを実装するには、このキーワードを持たないメソッドを実装するだけで済みます。

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 登录拦截器
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 目标方法执行之前执行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取请求路径
        String requestUrl = request.getRequestURI();
        log.info("请求的路径是: {}", requestUrl);

        String username = request.getParameter("username");
        if (username != null) {
            // 放行
            return true;
        }

        request.setAttribute("msg", "请先登录");
        // 携带msg跳转到登录页
        request.getRequestDispatcher("/").forward(request, response);
        return false;
    }

    /**
     * 目标方法完成以后执行
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle执行");
    }

    /**
     * 页面渲染以后执行
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion执行");
    }
}
ログイン後にコピー

2.2 インターセプターの登録と構成

SpringBoot では、構成をカスタマイズする必要がある場合、WebMvcConfigurer クラスを実装し、対応するメソッドを書き直すだけで済みます。ここではインターセプターを構成する必要があるため、その addInterceptors メソッドを書き直すだけです。

import com.codeliu.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

// 表示这是一个配置类
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  // 拦截所有路径
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**");  // 不拦截这些路径
    }
}
ログイン後にコピー

すべてのパスをインターセプトするように設定する場合は、静的リソースを除外する必要があることに注意してください。除外しないと、画像スタイルがインターセプトされます。

上記の手順により、システムに追加されたインターセプターを実装しました。あとは検証を開始するだけです。

3. インターセプターの原則

ブレークポイント デバッグ方法を使用して、ブラウザーのリクエストが最初からバックエンドまでどのように処理されるかを確認します。 DispatcherServlet の doDispatch メソッドにブレークポイントを設定します。これはリクエストのエントリ ポイントです。ブラウザがリクエストを送信した後、このメソッドはリクエストを転送して処理します。

SpringBoot インターセプターのソースコード分析

アプリケーションをデバッグ モードで起動し、任意のインターフェイスにアクセスし、コード フローを追跡します。

3.1 リクエストとすべてのインターセプターを処理できるハンドラーを見つけます。ハンドラーの

SpringBoot インターセプターのソースコード分析

ここで、HandlerExecutionChain とインターセプター チェーンが見つかりました。その中には、カスタム LoginInterceptor とシステムのデフォルトの 2 つのインターセプターの 3 つのインターセプターがあります。 。

3.2 インターセプタの preHandle メソッドを実行します。

doDispatch メソッドには、次の 2 行のコードがあります。

// 执行拦截器的preHandle方法,如果返回为fasle,则直接return,不执行目标方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}

// 反射执行目标方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
ログイン後にコピー

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 {
    // 遍历拦截器
    for (int i = 0; i < this.interceptorList.size(); i++) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        // 执行当前拦截器的preHandle方法
        if (!interceptor.preHandle(request, response, this.handler)) {
            // 如果preHandle方法返回为false,则执行当前拦截器的afterCompletion方法
            triggerAfterCompletion(request, response, null);
            return false;
        }
        // 记录当前拦截器的下标
        this.interceptorIndex = i;
    }
    return true;
}
ログイン後にコピー

上記のコードにより、現在のインターセプターの preHandle メソッドが true を返した場合、次のインターセプターの preHandle メソッドが引き続き実行され、それ以外の場合はインターセプターの 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) {
    // 反向遍历拦截器
    for (int i = this.interceptorIndex; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        try {
            // 执行当前拦截器的afterCompletion方法
            interceptor.afterCompletion(request, response, this.handler, ex);
        }
        catch (Throwable ex2) {
            logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
        }
    }
}
ログイン後にコピー

上記のコードから、インターセプターの afterCompletion メソッドが逆に実行されることがわかります。

3.3 ターゲット メソッドの実行

上記のインターセプターのすべての preHandle メソッドが true を返した場合、doDispatch メソッドには直接戻りはありませんが、ターゲット メソッドは引き続き実行されます。 。いずれかのインターセプタの preHandle メソッドが false を返した場合、インターセプタ (preHandle メソッドを実行したインターセプタ) の afterCompletion メソッドを実行した後、doDispatch メソッドが直接戻り、対象のメソッドは実行されません。

次のコードを通じてターゲット メソッドを実行します

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
ログイン後にコピー

具体的な内部実行については説明しませんが、実行後のロジックを確認します。

3.4 インターセプターの postHandle メソッドを実行します

ターゲット メソッドが実行された後、コードがダウンします

mappedHandler.applyPostHandle(processedRequest, response, mv);
ログイン後にコピー

applyPostHandle のロジックを表示します

/**
 * Apply postHandle methods of registered interceptors.
 */
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
    throws Exception {
	// 反向遍历
    for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        // 执行当前拦截器的postHandle方法
        interceptor.postHandle(request, response, this.handler, mv);
    }
}
ログイン後にコピー

インターセプトを逆の順序で実行します。 インターセプターの postHandle メソッドです。

3.5 インターセプターの afterCompletion メソッドを実行します。

継続して下降します。

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
ログイン後にコピー

このメソッドを入力すると、このメソッドは最後に以下のコードを実行します。

SpringBoot インターセプターのソースコード分析

3.6 例外処理

実行中に例外がスローされた場合doDispatch メソッドの場合、catch モジュールでトリガーされます。 afterCompletion メソッドを実行します。

SpringBoot インターセプターのソースコード分析

以上がSpringBoot インターセプターのソースコード分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:yisu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート