SpringBoot攔截器與文件上傳實(shí)現(xiàn)方法與源碼分析_第1頁
SpringBoot攔截器與文件上傳實(shí)現(xiàn)方法與源碼分析_第2頁
SpringBoot攔截器與文件上傳實(shí)現(xiàn)方法與源碼分析_第3頁
SpringBoot攔截器與文件上傳實(shí)現(xiàn)方法與源碼分析_第4頁
SpringBoot攔截器與文件上傳實(shí)現(xiàn)方法與源碼分析_第5頁
已閱讀5頁,還剩10頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第SpringBoot攔截器與文件上傳實(shí)現(xiàn)方法與源碼分析目錄一、攔截器1、創(chuàng)建一個攔截器2、配置攔截器二、攔截器原理三、文件上傳四、文件上傳流程

一、攔截器

攔截器我們之前在springmvc已經(jīng)做過介紹了

大家可以看下【SpringMVC】自定義攔截器和過濾器

為什么在這里還要再講一遍呢?

因?yàn)閟pringboot里面對它做了簡化,大大節(jié)省了我們配置那些煩人的xml文件的時(shí)間

接下來,我們就通過一個小例子來了解一下攔截器在springboot中的使用

1、創(chuàng)建一個攔截器

首先我們創(chuàng)建一個攔截器,實(shí)現(xiàn)HandlerInterceptor接口

packageerceptor;

importlombok.extern.slf4j.Slf4j;

importorg.springframework.web.servlet.HandlerInterceptor;

importorg.springframework.web.servlet.ModelAndView;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;

importjavax.servlet.http.HttpSession;

@Slf4j

publicclassLoginInterceptorimplementsHandlerInterceptor{

//在調(diào)用控制器接口方法之前進(jìn)入,如果放回true就放行,進(jìn)入下一個攔截器或者控制器,如果返回false就不繼續(xù)往下走

@Override

publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{

//獲取當(dāng)前請求路徑

finalStringrequestURL=request.getRequestURI();

("攔截到的請求為:{}",requestURL);

finalHttpSessionsession=request.getSession();

finalObjectuserSession=session.getAttribute("loginUser");

//如果session中存在用戶登錄信息,那么就判定為用戶已登錄,放行

if(null!=userSession){

returntrue;

}else{

//model和request都會往請求域中塞信息,所以這里可以使用request傳遞我們需要返回給前端的信息

request.setAttribute("msg","請登錄!");

//轉(zhuǎn)發(fā)到登錄頁

request.getRequestDispatcher("/").forward(request,response);

returnfalse;

//調(diào)用前提:preHandle返回true

//調(diào)用時(shí)間:Controller方法處理完之后,DispatcherServlet進(jìn)行視圖的渲染之前,也就是說在這個方法中你可以對ModelAndView進(jìn)行操作

//執(zhí)行順序:鏈?zhǔn)絀nterceptor情況下,Interceptor按照聲明的順序倒著執(zhí)行。

//備注:postHandle雖然post打頭,但post、get方法都能處理

@Override

publicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{

("postHandle執(zhí)行{}",modelAndView);

//調(diào)用前提:preHandle返回true

//調(diào)用時(shí)間:DispatcherServlet進(jìn)行視圖的渲染之后

//多用于清理資源

@Override

publicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{

("頁面渲染完成后執(zhí)行");

}

2、配置攔截器

創(chuàng)建完之后,我們就需要將攔截器注冊到容器中,并指定攔截規(guī)則

那么,我們創(chuàng)建一個配置類,實(shí)現(xiàn)WebMvcConfigurer接口,重寫addInterceptors方法,將我們之前創(chuàng)建好的攔截器放入即可

值得注意的是,我們要放開對登錄頁以及靜態(tài)資源的限制

packagecom.decade.config;

importerceptor.LoginInterceptor;

importorg.springframework.context.annotation.Configuration;

importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;

importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration

publicclassMyConfigimplementsWebMvcConfigurer{

@Override

publicvoidaddInterceptors(InterceptorRegistryregistry){

//addPathPatterns:設(shè)置要攔截的請求,如果是/**,那么會攔截包括靜態(tài)資源在內(nèi)的所有請求

//excludePathPatterns:設(shè)置不被攔截的請求,這里我們放行登錄頁請求和靜態(tài)資源

registry.addInterceptor(newLoginInterceptor())

.addPathPatterns("/**")

.excludePathPatterns("/","/login","/css/**","/images/**","/js/**","/fonts/**");

}

我們在未登錄的狀態(tài)下,對主頁發(fā)起一個請求,可以發(fā)現(xiàn),攔截器生效,而且攔截器中的方法所執(zhí)行的順序也符合預(yù)期

二、攔截器原理

我們還是使用debug模式,通過斷點(diǎn)來進(jìn)行分析

調(diào)用之前的主頁面接口,可以發(fā)現(xiàn)斷點(diǎn)還是走到了DispatcherServlet類下的doDispatch()

首先,他還是會返回給我們一個處理器執(zhí)行鏈HandlerExecutionChain

這個里面除了包含我們的請求應(yīng)該由哪個控制器類的哪個方法進(jìn)行處理之外,還包含了攔截器鏈

然后在使用mv=ha.handle(processedRequest,response,mappedHandler.getHandler());執(zhí)行目標(biāo)方法之前,他會調(diào)用一個applyPreHandle()方法

如果這個方法返回false,那么就會直接返回,不再繼續(xù)往下走

我們進(jìn)入applyPreHandle()方法可以看到,這個方法里會遍歷所有的攔截器,如果preHandle()方法返回結(jié)果為true,那就繼續(xù)調(diào)用下一個攔截器的preHandle()方法

只要有一個攔截器的preHandle()方法返回false,那么就會從當(dāng)前遍歷到的攔截器開始,倒序執(zhí)行afterCompletion()方法

booleanapplyPreHandle(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{

for(inti=0;ierceptorList.size();erceptorIndex=i++){

HandlerInterceptorinterceptor=(HandlerInterceptor)erceptorList.get(i);

//如果攔截器的preHandle()返回false,那么就會調(diào)用下面的triggerAfterCompletion()

if(!interceptor.preHandle(request,response,this.handler)){

this.triggerAfterCompletion(request,response,(Exception)null);

returnfalse;

returntrue;

//這個方法里面會從當(dāng)前遍歷到的攔截器開始,倒序執(zhí)行afterCompletion()方法

voidtriggerAfterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,@NullableExceptionex){

for(inti=erceptorIndex;i--i){

HandlerInterceptorinterceptor=(HandlerInterceptor)erceptorList.get(i);

try{

interceptor.afterCompletion(request,response,this.handler,ex);

}catch(Throwablevar7){

logger.error("HandlerInterceptor.afterCompletionthrewexception",var7);

}

執(zhí)行完目標(biāo)方法之后,斷點(diǎn)又走到mappedHandler.applyPostHandle(processedRequest,response,mv);

深入這個方法,我們可以發(fā)現(xiàn),這里是倒序執(zhí)行了所有攔截器的postHandle()方法

voidapplyPostHandle(HttpServletRequestrequest,HttpServletResponseresponse,@NullableModelAndViewmv)throwsException{

for(inti=erceptorList.size()-1;i--i){

HandlerInterceptorinterceptor=(HandlerInterceptor)erceptorList.get(i);

interceptor.postHandle(request,response,this.handler,mv);

}

最后,頁面渲染完成之后,他也會倒序執(zhí)行所有攔截器的afterCompletion()方法

注意:只要在請求處理期間出現(xiàn)任何異常,它都會倒序執(zhí)行所有攔截器的postHandle()方法

三、文件上傳

之前博主也寫過關(guān)于SpringMVC的文件上傳和下載

使用SpringBoot之后,我們節(jié)約了很多的配置

接下來,我們就通過一個例子,了解SpringBoot中的文件上傳

首先,我們先創(chuàng)建一個頁面,這里我們只貼核心代碼

默認(rèn)情況下,enctype的值是application/x-www-form-urlencoded,不能用于文件上傳,只有使用了multipart/form-data,才能完整的傳遞文件數(shù)據(jù)multiple表示可接受多個值的文件上傳字段

div

formrole="form"th:action="@{/upload}"method="post"enctype="multipart/form-data"

div

labelfor="exampleInputEmail1"郵箱/label

inputtype="email"name="email"id="exampleInputEmail1"placeholder="Enteremail"

/div

div

labelfor="exampleInputPassword1"名字/label

inputtype="text"name="username"id="exampleInputPassword1"placeholder="Password"

/div

div

labelfor="exampleInputFile"頭像/label

inputtype="file"name="headerImg"id="exampleInputFile"

/div

div

labelfor="exampleInputFile"生活照/label

inputtype="file"name="photos"multiple

/div

div

label

inputtype="checkbox"Checkmeout

/label

/div

buttontype="submit"提交/button

/form

/div

然后我們寫一下后端的業(yè)務(wù)代碼

packagecom.decade.controller;

importlombok.extern.slf4j.Slf4j;

importorg.springframework.stereotype.Controller;

importorg.springframework.web.bind.annotation.GetMapping;

importorg.springframework.web.bind.annotation.PostMapping;

importorg.springframework.web.bind.annotation.RequestParam;

importorg.springframework.web.bind.annotation.RequestPart;

importorg.springframework.web.multipart.MultipartFile;

importjava.io.File;

importjava.io.IOException;

@Controller

@Slf4j

publicclassFileUploadController{

*頁面跳轉(zhuǎn),跳轉(zhuǎn)到文件上傳頁面

*@return跳轉(zhuǎn)到文件上傳頁面

@GetMapping(value="/form_layouts")

publicStringuploadPage(){

return"form/form_layouts";

*文件上傳請求

*@paramemail郵件

*@paramusername用戶名

*@paramheaderImg頭像文件

*@paramphotos生活照

*@return如果上傳文件成功,跳轉(zhuǎn)到首頁

@PostMapping(value="/upload")

publicStringuploadFile(@RequestParam(name="email")Stringemail,

@RequestParam(name="username")Stringusername,@RequestPart("headerImg")MultipartFileheaderImg,

@RequestPart("photos")MultipartFile[]photos){

("請求參數(shù)email{},username{},頭像headerImg大小{},生活照photos張數(shù){}",

email,username,headerImg.getSize(),photos.length);

try{

//判斷頭像文件是否為空,如果不是為空,那么就保存到本地

if(!headerImg.isEmpty()){

finalStringfilename=headerImg.getOriginalFilename();

headerImg.transferTo(newFile("D:\\test1\\"+filename));

//判斷生活照是否上傳,循環(huán)保存到本地

if(photos.length0){

for(MultipartFilephoto:photos){

finalStringoriginalFilename=photo.getOriginalFilename();

photo.transferTo(newFile("D:\\test1\\"+originalFilename));

}catch(IOExceptione){

log.error("上傳文件出錯!",e);

return"redirect:/main.html";

}

如果報(bào)錯信息如下,那么我們需要去SpringBoot的默認(rèn)文件中添加如下配置

#單個文件最大限制

spring.servlet.multipart.max-file-size=10MB

#單次請求最大限制

spring.servlet.multipart.max-request-size=100MB

修改相關(guān)配置之后,文件上傳成功

四、文件上傳流程

文件上傳相關(guān)配置類MultipartAutoConfiguration,相關(guān)配置類MultipartProperties

在MultipartAutoConfiguration中我們自動配置好了文件上傳解析器StandardServletMultipartResolver(它在容器中的beanName為multipartResolver)

然后我們跟著上面文件上傳的例子進(jìn)行一個debug,分析一下流程

首先,斷點(diǎn)還是來到DispatcherServlet下面的doDispatch()方法

protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{

HttpServletRequestprocessedRequest=request;

HandlerExecutionChainmappedHandler=null;

//設(shè)置文件解析默認(rèn)值為false

booleanmultipartRequestParsed=false;

WebAsyncManagerasyncManager=WebAsyncUtils.getAsyncManager(request);

try{

try{

ModelAndViewmv=null;

ObjectdispatchException=null;

try{

//檢查當(dāng)前請求是否涉及文件上傳

processedRequest=this.checkMultipart(request);

//將文件解析設(shè)置為true,表明當(dāng)前請求涉及文件上傳

multipartRequestParsed=processedRequest!=request;

這里的processedRequest=this.checkMultipart(request);

會調(diào)用StandardServletMultipartResolver類中的isMultipart()判斷當(dāng)前請求是否涉及文件上傳

如果涉及那么就會對當(dāng)前請求做一個處理,將原生的請求封裝成一個StandardMultipartHttpServletRequest請求,把文件相關(guān)信息解析后放進(jìn)Map中(具體可以看StandardMultipartHttpServletRequest類中的parseRequest方法)

protectedHttpServletRequestcheckMultipart(HttpServletRequestrequest)throwsMultipartException{

//如果文件上傳解析器不為空,那么就調(diào)用StandardServletMultipartResolver類中的isMultipart()判斷當(dāng)前請求是否涉及文件上傳

if(this.multipartResolver!=nullthis.multipartResolver.isMultipart(request)){

if(WebUtils.getNativeRequest(request,MultipartHttpServletRequest.class)!=null){

if(DispatcherType.REQUEST.equals(request.getDispatcherType())){

this.logger.trace("RequestalreadyresolvedtoMultipartHttpServletRequest,e.g.byMultipartFilter");

}elseif(this.hasMultipartException(request)){

this.logger.debug("Multipartresolutionpreviouslyfailedforcurrentrequest-skippingre-resolutionforundisturbederrorrendering");

}else{

try{

//將原生的請求封裝成一個StandardMultipartHttpServletRequest請求,把文件相關(guān)信息解析放進(jìn)Map中

returnthis.multipartResolver.resolveMultipart(request);

然后我們按照之前請求處理那篇博客里的路徑,從mv=ha.handle(processedRequest,response,mappedHandler.getHandler())進(jìn)入

一直走到InvocableHandlerMethod下面的getMethodArgumentValues()方法,深入斷點(diǎn)

我們得知,使用@RequestParam注解的參數(shù)使用RequestParamMethodArgumentResolver這個解析器

而文件相關(guān)入?yún)⑹鞘褂聾RequestPart注解的,它使用RequestPartMethodArgumentResolver來進(jìn)行文件相關(guān)參數(shù)解析

在這個解析器中,他又會根據(jù)參數(shù)的名稱去上面checkM

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論