自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

SpringBoot攔截器和動(dòng)態(tài)代理有什么區(qū)別?

開(kāi)發(fā) 前端
在 Spring Boot 中,攔截器和動(dòng)態(tài)代理都是用來(lái)實(shí)現(xiàn)功能增強(qiáng)的,但二者沒(méi)有任何關(guān)聯(lián)關(guān)系,它的區(qū)別主要體現(xiàn)在使用范圍、實(shí)現(xiàn)原理、加入時(shí)機(jī)和使用的難易程度都是不同的。

在 Spring Boot 中,攔截器和動(dòng)態(tài)代理都是用來(lái)實(shí)現(xiàn)功能增強(qiáng)的,所以在很多時(shí)候,有人會(huì)認(rèn)為攔截器的底層是通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn)的,所以本文就來(lái)盤(pán)點(diǎn)一下他們兩的區(qū)別,以及攔截器的底層實(shí)現(xiàn)。

1、攔截器

攔截器(Interceptor)準(zhǔn)確來(lái)說(shuō)在 Spring MVC 中的一個(gè)很重要的組件,用于攔截 Controller 的請(qǐng)求。它的主要作用有以下幾個(gè):

  • 權(quán)限驗(yàn)證:驗(yàn)證用戶(hù)是否登錄、是否有權(quán)限訪(fǎng)問(wèn)某個(gè)接口。
  • 日志記錄:記錄請(qǐng)求信息的日志,如請(qǐng)求參數(shù),響應(yīng)信息等。
  • 性能監(jiān)控:監(jiān)控系統(tǒng)的運(yùn)行性能,如慢查詢(xún)接口等。
  • 通用行為:插入一些通用的行為,比如開(kāi)發(fā)環(huán)境忽略某些請(qǐng)求。

典型的使用場(chǎng)景是身份認(rèn)證、授權(quán)檢查、請(qǐng)求日志記錄等。

(1)攔截器實(shí)現(xiàn)

在 Spring Boot 中攔截器的實(shí)現(xiàn)分為兩步:

  • 創(chuàng)建一個(gè)普通的攔截器,實(shí)現(xiàn) HandlerInterceptor 接口,并重寫(xiě)接口中的相關(guān)方法。
  • 將上一步創(chuàng)建的攔截器加入到 Spring Boot 的配置文件中,并配置攔截規(guī)則。

具體實(shí)現(xiàn)如下。

實(shí)現(xiàn)自定義攔截器

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class TestInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("攔截器:執(zhí)行 preHandle 方法。");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("攔截器:執(zhí)行 postHandle 方法。");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("攔截器:執(zhí)行 afterCompletion 方法。");
    }
}

其中:

  • boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handle):在請(qǐng)求方法執(zhí)行前被調(diào)用,也就是調(diào)用目標(biāo)方法之前被調(diào)用。比如我們?cè)诓僮鲾?shù)據(jù)之前先要驗(yàn)證用戶(hù)的登錄信息,就可以在此方法中實(shí)現(xiàn),如果驗(yàn)證成功則返回 true,繼續(xù)執(zhí)行數(shù)據(jù)操作業(yè)務(wù);否則就返回 false,后續(xù)操作數(shù)據(jù)的業(yè)務(wù)就不會(huì)被執(zhí)行了。
  • void postHandle(HttpServletRequest request, HttpServletResponse response, Object handle,ModelAndView modelAndView):調(diào)用請(qǐng)求方法之后執(zhí)行,但它會(huì)在 DispatcherServlet 進(jìn)行渲染視圖之前被執(zhí)行。
  • void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex):會(huì)在整個(gè)請(qǐng)求結(jié)束之后再執(zhí)行,也就是在 DispatcherServlet 渲染了對(duì)應(yīng)的視圖之后再執(zhí)行。

配置攔截規(guī)則

然后,我們?cè)賹⑸厦娴臄r截器注入到項(xiàng)目配置文件中,并設(shè)置相應(yīng)攔截規(guī)則,具體實(shí)現(xiàn)代碼如下:

import org.springframework.beans.factory.annotation.Autowired;
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 AppConfig implements WebMvcConfigurer {

    // 注入攔截器
    @Autowired
    private TestInterceptor testInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(testInterceptor) // 添加攔截器
                .addPathPatterns("/**"); // 攔截所有地址
          .excludePathPatterns("/login"); // 放行接口
    }
}

這樣我們的攔截器就實(shí)現(xiàn)完了。

(2)攔截器實(shí)現(xiàn)原理

Spring Boot 攔截器是基于 Java 的 Servlet 規(guī)范實(shí)現(xiàn)的,通過(guò)實(shí)現(xiàn) HandlerInterceptor 接口來(lái)實(shí)現(xiàn)攔截器功能。

在 Spring Boot 框架的執(zhí)行流程中,攔截器被注冊(cè)在 DispatcherServlet 的 doDispatch() 方法中,該方法是 Spring Boot 框架的核心方法,用于處理請(qǐng)求和響應(yīng)。

程序每次執(zhí)行時(shí)都會(huì)調(diào)用 doDispatch() 方法時(shí),并驗(yàn)證攔截器(鏈),之后再根據(jù)攔截器返回的結(jié)果,進(jìn)行下一步的處理。如果返回的是 true,那么繼續(xù)調(diào)用目標(biāo)方法,反之則會(huì)直接返回驗(yàn)證失敗給前端。

doDispatch  源碼實(shí)現(xiàn)如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        try {
            ModelAndView mv = null;
            Object dispatchException = null;

            try {
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
                mappedHandler = this.getHandler(processedRequest);
                if (mappedHandler == null) {
                    this.noHandlerFound(processedRequest, response);
                    return;
                }

                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                String method = request.getMethod();
                boolean isGet = HttpMethod.GET.matches(method);
                if (isGet || HttpMethod.HEAD.matches(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                // 調(diào)用預(yù)處理【重點(diǎn)】
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 執(zhí)行 Controller 中的業(yè)務(wù)
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                this.applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception var20) {
                dispatchException = var20;
            } catch (Throwable var21) {
                dispatchException = new NestedServletException("Handler dispatch failed", var21);
            }

            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
        } catch (Exception var22) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
        } catch (Throwable var23) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
        }

    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else if (multipartRequestParsed) {
            this.cleanupMultipart(processedRequest);
        }

    }
}

從上述源碼可以看出在開(kāi)始執(zhí)行 Controller 之前,會(huì)先調(diào)用 預(yù)處理方法 applyPreHandle,而 applyPreHandle 方法的實(shí)現(xiàn)源碼如下:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
        // 獲取項(xiàng)目中使用的攔截器 HandlerInterceptor
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        if (!interceptor.preHandle(request, response, this.handler)) {
            this.triggerAfterCompletion(request, response, (Exception)null);
            return false;
        }
    }
    return true;
}

從上述源碼可以看出,在 applyPreHandle 中會(huì)獲取所有的攔截器 HandlerInterceptor 并執(zhí)行攔截器中的 preHandle 方法,這樣就會(huì)咱們前面定義的攔截器對(duì)應(yīng)上了,如下圖所示:

此時(shí)用戶(hù)登錄權(quán)限的驗(yàn)證方法就會(huì)執(zhí)行,這就是攔截器的執(zhí)行過(guò)程。因此,可以得出結(jié)論,攔截器的實(shí)現(xiàn)主要是依賴(lài) Servlet 或 Spring 執(zhí)行流程來(lái)進(jìn)行攔截和功能增強(qiáng)的。

2、動(dòng)態(tài)代理

動(dòng)態(tài)代理是一種設(shè)計(jì)模式,它是指在運(yùn)行時(shí)提供代理對(duì)象,來(lái)擴(kuò)展目標(biāo)對(duì)象的功能。在 Spring 中的,動(dòng)態(tài)代理的實(shí)現(xiàn)手段有以下兩種:

  • JDK 動(dòng)態(tài)代理:通過(guò)反射機(jī)制生成代理對(duì)象,目標(biāo)對(duì)象必須實(shí)現(xiàn)接口。
  • CGLIB 動(dòng)態(tài)代理:通過(guò)生成目標(biāo)類(lèi)的子類(lèi)來(lái)實(shí)現(xiàn)代理,不要求目標(biāo)對(duì)象實(shí)現(xiàn)接口。

動(dòng)態(tài)代理的主要作用包括:

  • 擴(kuò)展目標(biāo)對(duì)象的功能:如添加日志、驗(yàn)證參數(shù)等。
  • 控制目標(biāo)對(duì)象的訪(fǎng)問(wèn):如進(jìn)行權(quán)限控制。
  • 延遲加載目標(biāo)對(duì)象:在需要時(shí)才實(shí)例化目標(biāo)對(duì)象。
  • 遠(yuǎn)程代理:將請(qǐng)求轉(zhuǎn)發(fā)到遠(yuǎn)程的目標(biāo)對(duì)象上。

JDK 動(dòng)態(tài)代理和 CGLIB 的區(qū)別詳見(jiàn):www.javacn.site/interview/spring/jdk_cglib.html

3、攔截器 VS 動(dòng)態(tài)代理

因此,我們可以得出結(jié)論,攔截器和動(dòng)態(tài)代理雖然都是用來(lái)實(shí)現(xiàn)功能增強(qiáng)的,但二者完全不同,他們的主要區(qū)別體現(xiàn)在以下幾點(diǎn):

  • 使用范圍不同:攔截器通常用于 Spring MVC 中,主要用于攔截 Controller 請(qǐng)求。動(dòng)態(tài)代理可以使用在 Bean 中,主要用于提供 bean 的代理對(duì)象,實(shí)現(xiàn)對(duì) bean 方法的攔截。
  • 實(shí)現(xiàn)原理不同:攔截器是通過(guò) HandlerInterceptor 接口來(lái)實(shí)現(xiàn)的,主要是通過(guò) afterCompletion、postHandle、preHandle 這三個(gè)方法在請(qǐng)求前后進(jìn)行攔截處理。動(dòng)態(tài)代理主要有 JDK 動(dòng)態(tài)代理和 CGLIB 動(dòng)態(tài)代理,JDK 通過(guò)反射生成代理類(lèi);CGLIB 通過(guò)生成被代理類(lèi)的子類(lèi)來(lái)實(shí)現(xiàn)代理。
  • 加入時(shí)機(jī)不同:攔截器是在運(yùn)行階段動(dòng)態(tài)加入的;動(dòng)態(tài)代理是在編譯期或運(yùn)行期生成的代理類(lèi)。
  • 使用難易程度不同:攔截器相對(duì)簡(jiǎn)單,通過(guò)實(shí)現(xiàn)接口即可使用。動(dòng)態(tài)代理稍微復(fù)雜,需要了解動(dòng)態(tài)代理的實(shí)現(xiàn)原理,然后通過(guò)相應(yīng)的 api 實(shí)現(xiàn)。

小結(jié)

在 Spring Boot 中,攔截器和動(dòng)態(tài)代理都是用來(lái)實(shí)現(xiàn)功能增強(qiáng)的,但二者沒(méi)有任何關(guān)聯(lián)關(guān)系,它的區(qū)別主要體現(xiàn)在使用范圍、實(shí)現(xiàn)原理、加入時(shí)機(jī)和使用的難易程度都是不同的。

責(zé)任編輯:姜華 來(lái)源: Java中文社群
相關(guān)推薦

2023-02-20 07:19:14

2023-05-29 07:36:04

Java過(guò)濾器攔截器

2020-07-20 09:18:43

存儲(chǔ)數(shù)據(jù)技術(shù)

2009-02-12 15:33:00

代理服務(wù)器HTTPSOCKS

2021-12-17 14:40:02

while(1)for(;;)語(yǔ)言

2024-03-05 18:59:59

前端開(kāi)發(fā)localhost

2022-08-02 08:23:37

SessionCookies

2024-05-27 00:40:00

2022-02-27 15:33:22

安全CASBSASE

2024-09-09 13:10:14

2021-05-16 14:26:08

RPAIPACIO

2020-03-09 20:56:19

LoRaLoRaWAN無(wú)線(xiàn)技術(shù)

2022-09-07 18:32:57

并發(fā)編程線(xiàn)程

2020-11-09 14:07:53

PyQtQt編程

2022-06-06 14:53:02

LoRaLoRaWAN

2022-09-08 18:38:26

LinuxWindowsmacOS

2022-08-31 08:33:54

Bash操作系統(tǒng)Linux

2023-12-15 09:21:17

ObjectJavaString

2022-08-22 07:06:32

MyBatisSQL占位符
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)