SpringBoot統(tǒng)一處理功能實(shí)現(xiàn)的全過(guò)程
在處理網(wǎng)絡(luò)請(qǐng)求時(shí),有一部分功能是需要抽出來(lái)統(tǒng)一處理的,與業(yè)務(wù)隔開(kāi)。
登錄校驗(yàn)
可以利用spring mvc的攔截器Interceptor,實(shí)現(xiàn)HandlerInterceptor接口即可。實(shí)現(xiàn)該接口后,會(huì)在把請(qǐng)求發(fā)給Controller之前進(jìn)行攔截處理。
攔截器的實(shí)現(xiàn)分為以下兩個(gè)步驟:
- 創(chuàng)建?定義攔截器,實(shí)現(xiàn) HandlerInterceptor 接?的 preHandle(執(zhí)?具體?法之前的預(yù)處理)?法。
- 將?定義攔截器加? WebMvcConfigurer 的 addInterceptors ?法中。
我們使用session來(lái)作為登錄校驗(yàn)的例子,實(shí)現(xiàn)如下:
package com.demo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 登錄攔截器
*/
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 為 false 則不能繼續(xù)往下執(zhí)行;為 true 則可以。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 判斷session的信息是否合法
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("userinfo") != null) {
return true;
}
log.error("當(dāng)前用戶沒(méi)有訪問(wèn)權(quán)限");
response.setStatus(401);
return false;
}
}
將寫好的?定義攔截器通過(guò)WebMvcConfigurer注冊(cè)到容器中,使得攔截器生效,具體實(shí)現(xiàn)代碼如下:
package com.demo;
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.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 攔截所有請(qǐng)求
.excludePathPatterns("/user/login"); // 排除不攔截的 url
}
}
如果不注入對(duì)象的話,addInterceptor() 的參數(shù)也可以直接 new 一個(gè)對(duì)象:
@Configuration // 一定不要忘記
public class MyConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") // 攔截所有請(qǐng)求
.excludePathPatterns("/user/login"); // 排除不攔截的 url
}
}
原理
所有的 Controller 執(zhí)?都會(huì)通過(guò)spring mvc的調(diào)度器 DispatcherServlet 來(lái)實(shí)現(xiàn),所有?法都會(huì)執(zhí)? DispatcherServlet 中的 doDispatch 調(diào)度?法,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 {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
// ... 忽略不重要的代碼
// 調(diào)?預(yù)處理
if (!mappedHandler.applyPreHandle(processedRequest, respon
se)) {
return;
}
// 執(zhí)? Controller 中的業(yè)務(wù)
mv = ha.handle(processedRequest, response, mappedHandler.g
etHandler());
// ... 忽略不重要的代碼
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler di
spatch failed", var21);
}
this.processDispatchResult(processedRequest, response, mappedH
andler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mapped
Handler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mapped
Handler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processe
dRequest, 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.intercep
torList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null
);
return false;
}
}
return true;
}
異常處理
請(qǐng)求時(shí)的異常處理也是比較常見(jiàn)的統(tǒng)一處理的對(duì)象。
統(tǒng)?異常處理使?的是 @ControllerAdvice + @ExceptionHandler 來(lái)實(shí)現(xiàn)的,@ControllerAdvice 表示控制器通知類,@ExceptionHandler 是異常處理器,兩個(gè)結(jié)合表示當(dāng)出現(xiàn)異常的時(shí)候執(zhí)?某個(gè)通知,也就是執(zhí)?某個(gè)?法事件,具體實(shí)現(xiàn)代碼如下:
package com.demo;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
/**
* 統(tǒng)一處理異常
* 一般都需要自定義一個(gè)異常對(duì)象,這里為了簡(jiǎn)單說(shuō)明只用一個(gè)map對(duì)象來(lái)說(shuō)明
*/
@ControllerAdvice
public class ErrorAdive {
@ExceptionHandler(Exception.class)
@ResponseBody
public HashMap<String, Object> exceptionAdvie(Exception e) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", "-1");
result.put("msg", e.getMessage());
return result;
}
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public HashMap<String, Object> arithmeticAdvie(ArithmeticException e) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", "-2");
result.put("msg", e.getMessage());
return result;
}
}
此時(shí)若出現(xiàn)異常就不會(huì)報(bào)錯(cuò)了,代碼會(huì)繼續(xù)執(zhí)行,但是會(huì)把自定義的異常信息返回給前端!
原理
@ControllerAdvice是spring的aop對(duì)于Controller進(jìn)行切面所有屬性的,包括切入點(diǎn)和需要織入的切面邏輯,配合@ExceptionHandler來(lái)捕獲Controller中拋出的指定類型的異常,從而達(dá)到不同類型的異常區(qū)別處理的目的。
返回?cái)?shù)據(jù)結(jié)構(gòu)
統(tǒng)?的返回?cái)?shù)據(jù)結(jié)構(gòu)可以使用 @ControllerAdvice + ResponseBodyAdvice接口 的方式實(shí)現(xiàn),具體實(shí)現(xiàn)代碼如下:
package com.demo;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyA
dvice;
import java.util.HashMap;
/**
* 統(tǒng)一返回?cái)?shù)據(jù)的處理
*/
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
/**
* 內(nèi)容是否需要重寫(通過(guò)此?法可以選擇性部分控制器和?法進(jìn)?重寫)
* 返回 true 表示重寫
*/
@Override
public boolean supports(MethodParameter returnType, Class converterTyp
e) {
return true;
}
/**
* ?法返回之前調(diào)?此?法
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class selectedConverterType, ServerHttpR
equest request,
ServerHttpResponse response) {
// 構(gòu)造統(tǒng)?返回對(duì)象
HashMap<String, Object> result = new HashMap<>();
result.put("state", 1);
result.put("msg", "");
result.put("data", body);
return result;
}
}
到此這篇關(guān)于SpringBoot統(tǒng)一處理功能實(shí)現(xiàn)的全過(guò)程的文章就介紹到這了,更多相關(guān)SpringBoot統(tǒng)一處理功能內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
搭建一個(gè)基礎(chǔ)的Resty項(xiàng)目框架
這篇文章主要為大家介紹了如何搭建一個(gè)基礎(chǔ)的Resty項(xiàng)目框架示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03
解決springmvc使用@PathVariable路徑匹配問(wèn)題
這篇文章主要介紹了解決springmvc使用@PathVariable路徑匹配問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
詳解Java的Struts框架中上傳文件和客戶端驗(yàn)證的實(shí)現(xiàn)
這篇文章主要介紹了Java的Struts框架中上傳文件和客戶端驗(yàn)證的實(shí)現(xiàn),Struts是Java的SSH三大web開(kāi)發(fā)框架之一,需要的朋友可以參考下2015-12-12
Java實(shí)現(xiàn)線程同步的四種方式總結(jié)
Java線程同步屬于Java多線程與并發(fā)編程的核心點(diǎn),需要重點(diǎn)掌握,下面我就來(lái)詳解Java線程同步的4種主要的實(shí)現(xiàn)方式,需要的可以參考一下2022-09-09
關(guān)于maven打包時(shí)的報(bào)錯(cuò): Return code is: 501 , ReasonPhrase:HTTPS Requ
這篇文章主要介紹了關(guān)于maven打包時(shí)的報(bào)錯(cuò): Return code is: 501 , ReasonPhrase:HTTPS Required,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
java 使用線程監(jiān)控文件目錄變化的實(shí)現(xiàn)方法
這篇文章主要介紹了java 使用線程監(jiān)控文件目錄變化的實(shí)現(xiàn)方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-10-10
Java如何使用HTTPclient訪問(wèn)url獲得數(shù)據(jù)
這篇文章主要介紹了Java使用HTTPclient訪問(wèn)url獲得數(shù)據(jù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
springboot簡(jiǎn)單集成Security配置的教程
這篇文章主要介紹了springboot簡(jiǎn)單集成Security配置的教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03

