SpringBoot 統(tǒng)一功能處理方案
在我們進行項目編寫時,有時相同的一段代碼在不同的地方使用多次,對于這種情況,spring 幫我們實現(xiàn)了統(tǒng)一功能處理,下面介紹一些常用的統(tǒng)一功能處理。
一、攔截器
在有些項目中,是需要用戶登錄才能進行后續(xù)訪問的。但如果這時有人拿到了后端接口的 url,這時就能跳過用戶登錄進而訪問我們的網(wǎng)站,這就有可能對網(wǎng)站造成攻擊,對于這種情況,我們可以將用戶的部分信息存到 session 中,在每次訪問接口前,都可以先獲取到 session,再將用戶信息取出,看看這個用戶是否登錄,若登錄,就允許繼續(xù)訪問,若沒有登陸,就強制登錄。
但是,在我們的項目中,接口的數(shù)目可不小,若是在每個接口中都加上這段邏輯,就會顯得我們的代碼過于冗余,于是 Spring 就為我們構(gòu)造了攔截器。
攔截器主要用來攔截用戶的請求,在指定方法前后執(zhí)行攔截器中預(yù)設(shè)的代碼,也就是說,攔截器可以在用戶的請求之前發(fā)揮作用,也可以在用戶的請求之后發(fā)揮作用。
對于上述判斷用戶登錄的業(yè)務(wù),我們就可以在攔截器中寫明判斷用戶是否登錄的代碼,這樣在用戶發(fā)送請求時,就會先執(zhí)行這段代碼,若用戶沒有登錄,就會阻止用戶進行訪問,并跳轉(zhuǎn)到登陸頁面(這是前端干的事)。
下面介紹攔截器的具體用法。
在實現(xiàn)攔截器時,需要實現(xiàn) HandlerInterceptor 類中的 preHandle,postHandle,afterCompletion 方法,preHandle 是在目標方法執(zhí)行前執(zhí)行的,postHandle 是在目標方法執(zhí)行之后執(zhí)行的,afterCompletion 是在試圖渲染完成之后執(zhí)行的,但現(xiàn)在后端開發(fā)基本上都不會涉及到試圖,那么這個方法就不重點介紹了。
代碼如下:
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
//在目標方法執(zhí)行前執(zhí)行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("在目標方法執(zhí)行前執(zhí)行");
return true;
}
//在目標方法之后執(zhí)行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("在目方法執(zhí)行后執(zhí)行");
}
//試圖渲染完成之后執(zhí)行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("視圖渲染完成后執(zhí)行");
}
}preHandle 有返回值,返回 true 表示不攔截請求,返回 false 表示攔截請求。
定義好攔截器的功能后,接下來就需要注冊攔截器。注冊攔截器需要實現(xiàn) WebMvcConfigurer 中的 addInterceptors,代碼如下:
@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/test/**");
}
}使用 addInterceptor 即表示需要注冊的是哪個攔截器,使用 addPathPatterns 即表示需要攔截的路徑,即攔截器對哪些請求生效,這里的 /test/** 表示對 test 路徑下的所有方法均生效,還有其它的路徑表示,如下:
/* : 表示一級路徑,只能匹配 /test,不能匹配 /test/t1等;
/** : 表示任意級路徑,能匹配 /test,/test/t1等;
/test/* : 表示 /test 下的一級路徑,可以匹配 /test/t1,但不能匹配 /test/t1/t2等;
/test/** : 表示 /test 下的任意級路徑,可以匹配 /test/t1,/test/t1/t2,但不能匹配 /user/login 等。
當我們配置完攔截器和注冊完攔截器后,就可以開始運行代碼,我們使用下面的代碼來進行測試:
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/t1")
public void t1() {
log.info("攔截器測試...");
}
}代碼運行結(jié)果如下:

在這里我們可以看到,當訪問被攔截的方法時,首先執(zhí)行的是 preHandle,由于 postHandle 返回值為 true,就不會攔截用戶請求,于是就會先執(zhí)行 t1 方法,之后會順序執(zhí)行 postHandle 和 afterCompletion。
若 preHandle 返回值為 false,代碼的運行結(jié)果如下:

從結(jié)果中可以看出,程序只執(zhí)行了 preHandle,由于 t1 被攔截,于是 t1、postHandle、afterCompletion 就不會執(zhí)行。
在上述的攔截路徑中,我們將 /test 下的所有路徑都給攔截了,但是有的請求我們不需要攔截,那么我們就可以使用 excludPathPatterns 去去除掉不需要攔截的路徑,改動后的代碼如下:
@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/test/**")
.excludePathPatterns("/test/t2");
}
}
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/t1")
public void t1() {
log.info("t1攔截器測試...");
}
@RequestMapping("/t2")
public void t2() {
log.info("不攔截t2測試");
}
}TestController 中新增了 t2 方法,只攔截 t1,不攔截 t2,代碼運行結(jié)果如下:

從圖中可以看出,t1被攔截,但 t2 沒有被攔截。
當我們知道如何使用攔截器之后,就可以在項目中進行運用。
當用戶在進行訪問時,就可以對其訪問進行攔截,在攔截器中判斷用戶是否登錄,若登錄就放行,若沒有登錄就跳轉(zhuǎn)到登錄頁面進行登錄,代碼如下:
package com.gjm.demo.interceptor;
import com.gjm.demo.constant.Constants;
import com.gjm.demo.enums.ResultStatusEnums;
import com.gjm.demo.model.Result;
import com.gjm.demo.model.UserInfo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 登錄攔截器
*/
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
/**
* 目標方法前執(zhí)行, 攔截器攔截到后,就會將response返回給前端,前端的error中就能就受到response
* @param request
* @param response
* @param handler
* @return true: 繼續(xù)執(zhí)行, false: 中斷后續(xù)操作
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("登錄之前執(zhí)行");
//用戶未登錄的處理
if (!checkUserInfo(request.getSession())) {
response.setContentType("text/html;character=utf-8");
response.setStatus(401);
String errorMessage = "用戶未登錄";
response.getOutputStream().write(errorMessage.getBytes("UTF-8"));
return false;
}
return true;
}
/**
* 對檢查用戶信息進行封裝
* @param session
* @return
*/
public boolean checkUserInfo(HttpSession session) {
//用戶未登錄
if (session == null || session.getAttribute(Constants.SESSION_USER_KEY) == null) {
return false;
}
//獲取用戶信息
UserInfo userInfo = (UserInfo) session.getAttribute(Constants.SESSION_USER_KEY);
//用戶信息不對
if (userInfo == null || userInfo.getId() <= 0) {
return false;
}
return true;
}
}其中 check 的用處是檢查用戶是否登錄。
二、統(tǒng)一數(shù)據(jù)返回格式
在進行項目開發(fā)時,我需要向前端返回一個統(tǒng)一的數(shù)據(jù),這個數(shù)據(jù)是經(jīng)過封裝的,但是,在我們的接口中,有的返回的是 String,有的返回的是 boolean等,這就需要我們針對每一個接口都編寫封裝結(jié)果的代碼,這樣的代碼就有點冗余了。于是,Spring 就封裝了能夠統(tǒng)一返回數(shù)據(jù)格式的方法。
現(xiàn)在有一個結(jié)果類 Result,對于接口中的每一種返回值,都需要將其封裝為 Result 對象,實現(xiàn)的代碼如下:
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
//false為不處理,true為處理
return true;
}
@SneakyThrows//異常處理
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof String) {
return objectMapper.writeValueAsString(Result.success(body));//將Result類型轉(zhuǎn)化為String類型
}
//body已經(jīng)是result類型,就不需要封裝
if (body instanceof Result) {
return body;
}
return Result.success(body);
}
}該類需實現(xiàn) ResponseBodyAdvice 接口,實現(xiàn) support 和 beforeBodyWrite 方法,而且需要加上 @ControllerAdvice 注解。
support 的返回值為 boolean,返回 true 表示對各類型的返回值都進行處理,返回 false 表示不處理。
beforeBodyWrite 即具體的實現(xiàn)類。在這里需要強調(diào),若接口中的返回值為 String,那么就需要對其進行單獨處理,不然就會報錯。
對于本來就是 Result 類型的對象,就不要進行處理,直接返回即可。
三、統(tǒng)一異常處理
在我們編寫代碼的時候,有時候可能判斷的不準確,不知道哪里會出現(xiàn)異常,那么如果我們不進行處理,就會影響程序的運行。對于這種情況,Spring 為我們封裝了一個能統(tǒng)一處理異常的方法,代碼如下:
@Slf4j
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
@ExceptionHandler
public Result handler(Exception e) {
log.error("發(fā)生異常, e: ", e);
return Result.fail(ResultStatusEnums.ERROR, "內(nèi)部錯誤,請聯(lián)系管理員");
}
@ExceptionHandler
public Result handler(NullPointerException e) {
log.error("發(fā)生異常, e: ", e);
return Result.fail(ResultStatusEnums.ERROR, "空指針異常,請聯(lián)系管理員");
}
@ExceptionHandler
public Result handler(IndexOutOfBoundsException e) {
log.error("發(fā)生異常, e: ", e);
return Result.fail(ResultStatusEnums.ERROR, "數(shù)組越界異常,請聯(lián)系管理員");
}
}ExceptionAdvice 需要使用 @ControllerAdvice 和 @ResponseBody 注解。
在各個方法上需要使用 @ExceptionHandler 注解。
在這個類中,一共處理了三個異常,也可以繼續(xù)增加異常的種類。若代碼中拋出了異常并且沒有手動捕獲,就會拋給 ExceptionAdvice 類,在這個類中尋找與之距離最近的異常,若沒有,就會直接使用 Exception 代替。
上述的代碼還有另一種寫法,代碼如下:
@Slf4j
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
@ExceptionHandler(Exception.class)
public Result handler1(Exception e) {
log.error("發(fā)生異常, e: ", e);
return Result.fail(ResultStatusEnums.ERROR, "內(nèi)部錯誤,請聯(lián)系管理員");
}
@ExceptionHandler(NullPointerException.class)
public Result handler2(Exception e) {
log.error("發(fā)生異常, e: ", e);
return Result.fail(ResultStatusEnums.ERROR, "空指針異常,請聯(lián)系管理員");
}
@ExceptionHandler(IndexOutOfBoundsException.class)
public Result handler3(Exception e) {
log.error("發(fā)生異常, e: ", e);
return Result.fail(ResultStatusEnums.ERROR, "數(shù)組越界異常,請聯(lián)系管理員");
}
}在每個 @ExceptionHandler 注解中標明捕獲的是什么類型的異常,這樣在每個方法的參數(shù)中就能直接使用 Exception 代替。
到此這篇關(guān)于SpringBoot 統(tǒng)一功能處理方案的文章就介紹到這了,更多相關(guān)SpringBoot 統(tǒng)一功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實現(xiàn)字符串轉(zhuǎn)換為十進制ASCII碼的實戰(zhàn)指南
將Java字符串轉(zhuǎn)換為十進制ASCII碼是文本處理、協(xié)議編碼、數(shù)據(jù)序列化和底層通信中的常見需求,下面我們就來看看具體的實現(xiàn)方法,希望對大家有所幫助2026-05-05
詳解Http請求中Content-Type講解以及在Spring MVC中的應(yīng)用
這篇文章主要介紹了Http請求中Content-Type講解以及在Spring MVC中的應(yīng)用的相關(guān)資料,需要的朋友可以參考下2017-02-02
springcloud 中 zuul 修改請求參數(shù)信息的方法
這篇文章主要介紹了springcloud 中 zuul 修改請求參數(shù)信息的方法,需要的朋友可以參考下2018-02-02
Java中Collection和Collections的區(qū)別
Collection是一個集合接口,集合類的頂級接口,Collections是一個包裝類,本文主要介紹了Java中Collection和Collections的區(qū)別,具有一定的參考價值,感興趣的可以了解一下2024-04-04
SpringMVC 方法四種類型返回值總結(jié)(你用過幾種)
這篇文章主要介紹了SpringMVC 方法四種類型返回值總結(jié)(你用過幾種),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05

