最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot中的攔截器使用詳解

 更新時間:2026年05月28日 10:56:39   作者:新綠MEHO  
本文詳細解析了Spring框架中的攔截器機制,通過自定義攔截器實現登錄校驗,并介紹了攔截器的執(zhí)行流程、路徑配置及適配器模式的應用,感興趣的朋友一起看看吧

攔截器

之前我們完成的圖書管理系統(tǒng)完成了強制登錄的功能, 實現原理是后端在用戶登錄成功后會把用戶信息存儲到了Session中,后端程序根據Session來判斷用戶是否登錄, 但是這樣是?較麻煩的:

• 需要修改每個接?的處理邏輯
• 需要修改每個接?的返回結果
• 接?定義修改, 前端代碼也需要跟著修改

有沒有更簡單的辦法, 統(tǒng)?攔截所有的請求, 并進?Session校驗呢?

這?我們學習?種新的解決辦法: 攔截器。 

攔截器是Spring框架提供的核?功能之?, 主要?來攔截??的請求, 在指定?法前后, 根據業(yè)務需要執(zhí)行預先設定的代碼。

攔截器的使用

攔截器的使用分為兩步:

1.定義攔截器;

2.注冊配置攔截器。

自定義攔截器

實現HandlerInterceptor接口,并重寫其所有方法

LoginInterceptor

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("LoginInterceptor 目標方法執(zhí)行前執(zhí)行");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("LoginInterceptor 目標方法執(zhí)行后執(zhí)行");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("LoginInterceptor 視圖渲染完畢后執(zhí)行,最后執(zhí)行");
    }
}

• preHandle()?法:?標?法執(zhí)?前執(zhí)?. 返回true表示可以繼續(xù)執(zhí)?后續(xù)操作; 返回false表示中斷后續(xù)操作。
• postHandle()?法:?標?法執(zhí)?后執(zhí)?。
• afterCompletion()?法:視圖渲染完畢后執(zhí)?,最后執(zhí)? 。

注冊配置攔截器

實現WebMvcConfigurer接口,并重寫addInterceptors方法

WebConfig

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
        ;
    }
}

 啟動服務,登陸成功后,發(fā)送根據圖書id查詢圖書請求,觀察后端日志:

可以看到preHandle方法執(zhí)行之后就放行了,開始執(zhí)行目標方法,目標方法執(zhí)行完成之后執(zhí)行postHandle和afterCompletion方法。

我們吧攔截器中preHandle方法的返回值改為false,在觀察后端日志:

后端日志: 

此時看到只有“目標方法執(zhí)行前執(zhí)行”這一條日志。

原因是因為:preHandle方法攔截了所有請求。 

攔截器詳解

攔截器路徑

攔截路徑是指我們定義的這個攔截器, 對哪些請求?效.
我們在注冊配置攔截器的時候, 通過 addPathPatterns() ?法指定要攔截哪些請求. 也可以通過
excludePathPatterns() 指定不攔截哪些請求.
上述代碼中, 我們配置的是 /** , 表?攔截所有的請求.

?如用戶登錄校驗, 我們希望可以對除了登錄之外所有的路徑?效.

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;
import springbook.interceptor.LoginInterceptor;
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login")
        ;
    }
}

在攔截器中除了可以設置 /** 攔截所有資源外,還有一些常見攔截路徑設置:

攔截器執(zhí)行流程

正常的調用順序:

有了攔截器之后,會在調? Controller 之前進?相應的業(yè)務處理,執(zhí)?的流程如下圖:

1. 添加攔截器后, 執(zhí)?Controller的?法之前, 請求會先被攔截器攔截住. 執(zhí)? preHandle() ?法,這個?法需要返回?個布爾類型的值. 如果返回true, 就表?放?本次操作, 繼續(xù)訪問controller中的?法. 如果返回false,則不會放?(controller中的?法也不會執(zhí)?).
2. controller當中的?法執(zhí)?完畢后,再回過來執(zhí)? postHandle() 這個?法以及
afterCompletion() ?法,執(zhí)?完畢之后,最終給瀏覽器響應數據.

修改之前登錄校驗功能

定義攔截器

從session中獲取??信息, 如果session中不存在, 則返回false,并設置http狀態(tài)碼為401, 否則返回true. 

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //true  表示放行   false表示攔截
        log.info("LoginInterceptor preHandle....");
        //獲取session, 并且判斷session中存儲的userinfo信息是否為空
        HttpSession session = request.getSession();
        UserInfo userInfo = (UserInfo) session.getAttribute(Constants.USER_SESSION_KEY);
        if (userInfo==null || userInfo.getId()<=0){
            //用戶未登錄
            response.setStatus(401);
            return false;
        }
        return true;
    }
}

注冊配置攔截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/css/**")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/pic/**")
                .excludePathPatterns("/**/*.html")
        ;
    }
}

使用瀏覽器訪問

http://127.0.0.1:8080/book/queryBookById?bookId=1

訪問結果:

使用Fiddler抓包:

登陸之后再訪問上面的鏈接

DispatcherServlet源碼解析(重要)

觀察服務啟動日志:

當Tomcat啟動之后, 有?個核?的類DispatcherServlet, 它來控制程序的執(zhí)?順序.

所有請求都會先進到DispatcherServlet,執(zhí)?doDispatch 調度?法. 如果有攔截器, 會先執(zhí)?攔截器preHandle() ?法的代碼, 如果 preHandle() 返回true, 繼續(xù)訪問controller中的?法. controller
當中的?法執(zhí)?完畢后,再回過來執(zhí)? postHandle() 和 afterCompletion() ,返回給
DispatcherServlet, 最終給瀏覽器響應數據. 

初始化

DispatcherServlet的初始化?法 init() 在其?類 HttpServletBean 中實現的.
主要作?是加載 web.xml 中 DispatcherServlet 的 配置, 并調??類的初始化.

在 HttpServletBean 的 init() 中調?了 initServletBean() , 它是在
FrameworkServlet 類中實現的, 主要作?是建? WebApplicationContext 容器(有時也稱上下?), 并加載 SpringMVC 配置?件中定義的 Bean到該容器中, 最后將該容器添加到 ServletContext 中. 下?是initServletBean() 的具體代碼:

此處打印的?志, 也正是控制臺打印出來的?志:

初始化web容器的過程中, 會通過onRefresh 來初始化SpringMVC的容器:

在initStrategies()中進?9?組件的初始化, 如果沒有配置相應的組件,就使?默認定義的組件(在
DispatcherServlet.properties中有配置默認的策略)。

關于9大組件的解釋

1. 初始化?件上傳解析器MultipartResolver:從應?上下?中獲取名稱為multipartResolver的Bean,如果沒有名為multipartResolver的Bean,則沒有提供上傳?件的解析器;
2. 初始化區(qū)域解析器LocaleResolver:從應?上下?中獲取名稱為localeResolver的Bean,如果沒有這個Bean,則默認使?AcceptHeaderLocaleResolver作為區(qū)域解析器;
3. 初始化主題解析器ThemeResolver:從應?上下?中獲取名稱為themeResolver的Bean,如果沒有這個Bean,則默認使?FixedThemeResolver作為主題解析器;
4. 初始化處理器映射器HandlerMappings:處理器映射器作?,1)通過處理器映射器找到對應的處理器適配器,將請求交給適配器處理;2)緩存每個請求地址URL對應的位置(Controller.xxx?法);如果在ApplicationContext發(fā)現有HandlerMappings,則從ApplicationContext中獲取到所有的HandlerMappings,并進?排序;如果在ApplicationContext中沒有發(fā)現有處理器映射器,則默認BeanNameUrlHandlerMapping作為處理器映射器;
5. 初始化處理器適配器HandlerAdapter:作?是通過調?具體的?法來處理具體的請求;如果在ApplicationContext發(fā)現有handlerAdapter,則從ApplicationContext中獲取到所有的
HandlerAdapter,并進?排序;如果在ApplicationContext中沒有發(fā)現處理器適配器,則默認
SimpleControllerHandlerAdapter作為處理器適配器;
6. 初始化異常處理器解析器HandlerExceptionResolver:如果在ApplicationContext發(fā)現有
handlerExceptionResolver,則從ApplicationContext中獲取到所有的
HandlerExceptionResolver,并進?排序;如果在ApplicationContext中沒有發(fā)現異常處理器解析器,則不設置異常處理器;
7. 初始化RequestToViewNameTranslator:其作?是從Request中獲取viewName,從
ApplicationContext發(fā)現有viewNameTranslator的Bean,如果沒有,則默認使?
DefaultRequestToViewNameTranslator;
8. 初始化視圖解析器ViewResolvers:先從ApplicationContext中獲取名為viewResolver的Bean,如果沒有,則默認InternalResourceViewResolver作為視圖解析器;
9. 初始化FlashMapManager:其作?是?于檢索和保存FlashMap(保存從?個URL重定向到另?個URL時的參數信息),從ApplicationContext發(fā)現有flashMapManager的Bean,如果沒有,則默認使?DefaultFlashMapManager 。

處理請求(核心)

DispatcherServlet 接收到請求后, 執(zhí)?doDispatch 調度?法, 再將請求轉給Controller.
我們來看doDispatch ?法的具體實現: 

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		try {
			ModelAndView mv = null;
			Exception dispatchException = null;
			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);
				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
				// Process last-modified header, if supported by the handler.
				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;
					}
				}
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new ServletException("Handler dispatch failed: " + err, err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new ServletException("Handler processing failed: " + err, err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

其中有三個方法關注一下:

上面的三個方法對應我們之前使用到的HandlerInterceptor接口:

適配器模式

HandlerAdapter 在 Spring MVC 中使?了適配器模式

適配器模式定義

適配器模式, 也叫包裝器模式. 將?個類的接?,轉換成客?期望的另?個接?, 適配器讓原本接?不兼容的類可以合作?間.

簡單來說就是?標類不能直接使?, 通過?個新類進?包裝?下, 適配調??使?. 把兩個不兼容的接?通過?定的?式使之兼容.

?如下?兩個接?, 本?是不兼容的(參數類型不?樣, 參數個數不?樣等等):

可以通過適配器的?式, 使之兼容:

適配器模式角色

• Target: ?標接? (可以是抽象類或接?), 客?希望直接?的接?
• Adaptee: 適配者, 但是與Target不兼容
• Adapter: 適配器類, 此模式的核?. 通過繼承或者引?適配者的對象, 把適配者轉為?標接?
• client: 需要使?適配器的對象

適配器模式的實現

場景: 前?學習的slf4j 就使?了適配器模式, slf4j提供了?系列打印?志的api, 底層調?的是log4j 或者logback來打?志, 我們作為調?者, 只需要調?slf4j的api就?了.

Log4j

public class Log4j {
    public void log4jPrint(String message){
        System.out.println("我是Log4j, 打印日志內容為: "+message);
    }
}

Log4jAdapter

public class Log4jAdapter implements Slf4jLog{
    private Log4j log4j;
    public Log4jAdapter(Log4j log4j) {
        this.log4j = log4j;
    }
    @Override
    public void log(String message) {
        log4j.log4jPrint("我是是適配器, 打印日志為: "+ message);
    }
}

Slf4jLog

public interface Slf4jLog {
    void log(String message);
}

Main

public class Main {
    public static void main(String[] args) {
        Slf4jLog slf4jLog = new Log4jAdapter(new Log4j());
        slf4jLog.log("我是客戶端");
    }
}

運行結果:

可以看出, 我們不需要改變log4j的api,只需要通過適配器轉換下, 就可以更換?志框架, 保障系統(tǒng)的平穩(wěn)運行.

適配器模式的優(yōu)缺點

優(yōu)點

提高系統(tǒng)的靈活性和可擴展性:通過適配器模式,可以很方便地增加新的適配器來支持新的接口,而不需要修改原有的代碼。
解耦:客戶端通過適配器與需要適配的類進行交互,降低了客戶端與需要適配的類之間的耦合度。

缺點

過多使用適配器會使系統(tǒng)變得復雜:由于引入了適配器,系統(tǒng)的類數量會增加,這可能會使系統(tǒng)的結構變得復雜。
可能隱藏了需要適配的類的真正功能:客戶端通過適配器與需要適配的類進行交互,可能會忽略需要適配的類的某些功能。

適配器模式的應用場景

當系統(tǒng)需要使用現有的類,而這些類的接口不符合系統(tǒng)的需要時。
想要建立一個可以重復使用的類庫,以便與一些彼此之間沒有太大關聯(lián)甚至完全不兼容的類一起工作。
在開發(fā)過程中,由于某些原因(如框架升級、第三方庫變更等)導致原有的接口發(fā)生變化,而客戶端代碼依賴于舊的接口時。

到此這篇關于SpringBoot之攔截器的文章就介紹到這了,更多相關SpringBoot攔截器內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Mybatis日志參數快速替換占位符工具的詳細步驟

    Mybatis日志參數快速替換占位符工具的詳細步驟

    這篇文章主要介紹了Mybatis日志參數快速替換占位符工具的詳細步驟,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • Java性能優(yōu)化之數據結構實例代碼

    Java性能優(yōu)化之數據結構實例代碼

    這篇文章主要介紹了Java性能優(yōu)化之數據結構實例代碼,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • Caffeine本地緩存核心原理與使用方法詳解(含與Spring?Cache集成)

    Caffeine本地緩存核心原理與使用方法詳解(含與Spring?Cache集成)

    本文介紹Caffeine作為高性能本地緩存庫,具備智能淘汰策略和并發(fā)優(yōu)化,集成SpringCache實現緩存管理,探討緩存穿透、擊穿、雪崩防護及多級緩存(Caffeine+Redis)方案,適用于Java生態(tài)的緩存實踐,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • maven打包spring項目縮小體積實現方式

    maven打包spring項目縮小體積實現方式

    文章介紹了如何在Maven的pom.xml文件中配置跳過測試以縮小Spring項目的打包體積,并提供了具體的配置步驟和運行jar包的方法
    2026-03-03
  • Java多線程volatile原理及用法解析

    Java多線程volatile原理及用法解析

    這篇文章主要介紹了Java多線程volatile原理及用法解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • springboot如何讀取配置文件到靜態(tài)工具類

    springboot如何讀取配置文件到靜態(tài)工具類

    這篇文章主要介紹了springboot實現讀取配置文件到靜態(tài)工具類方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • mybatis實現mapper代理模式的方式

    mybatis實現mapper代理模式的方式

    本文向大家講解mybatis的mapper代理模式,以根據ide值查詢單條數據為例編寫xml文件,通過mapper代理的方式進行講解增刪改查,分步驟給大家講解的很詳細,對mybatis mapper代理模式相關知識感興趣的朋友一起看看吧
    2021-06-06
  • Spring 定時任務@Scheduled 注解中的 Cron 表達式詳解

    Spring 定時任務@Scheduled 注解中的 Cron 表達式詳解

    Cron 表達式是一種用于定義定時任務觸發(fā)時間的字符串表示形式,它由七個字段組成,分別表示秒、分鐘、小時、日期、月份、星期和年份,這篇文章主要介紹了Spring 定時任務@Scheduled 注解中的 Cron 表達式,需要的朋友可以參考下
    2023-07-07
  • Java編程使用卡片布局管理器示例【基于swing組件】

    Java編程使用卡片布局管理器示例【基于swing組件】

    這篇文章主要介紹了Java編程使用卡片布局管理器,結合實例形式分析了java基于swing組件的卡片布局管理器具體實現與使用技巧,需要的朋友可以參考下
    2018-01-01
  • BaseJDBC和CRUDDAO的寫法實例代碼

    BaseJDBC和CRUDDAO的寫法實例代碼

    這篇文章主要介紹了BaseJDBC和CRUDDAO的寫法實例代碼,代碼注釋十分詳細,具有一定參考價值,需要的朋友可以了解下。
    2017-09-09

最新評論

利川市| 金寨县| 迁安市| 南丹县| 舒兰市| 图木舒克市| 定襄县| 布拖县| 若羌县| 谢通门县| 吴堡县| 无锡市| 长海县| 武定县| 中牟县| 安图县| 台江县| 吴堡县| 怀远县| 邵阳县| 丰城市| 洪湖市| 韶山市| 星座| 同江市| 阳春市| 杨浦区| 嘉荫县| 沁水县| 九龙坡区| 忻城县| 八宿县| 句容市| 安平县| 喜德县| 台州市| 葵青区| 黄山市| 旅游| 呼图壁县| 张北县|