




版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 材料抵債協(xié)議書
- 運(yùn)輸供應(yīng)商合同協(xié)議
- 運(yùn)輸拖掛車隊(duì)合同協(xié)議
- 鄰里房屋協(xié)議書范本
- 水下砌墻協(xié)議書
- 化妝品代理銷售合同
- 通訊工程設(shè)計(jì)合同協(xié)議
- 活動委托協(xié)議書
- 課程顧問招聘合同協(xié)議
- 返傭協(xié)議書范本模板
- 公立醫(yī)院運(yùn)營分析總結(jié)報(bào)告
- MOOC 引領(lǐng)世界的中國乒乓-西南交通大學(xué) 中國大學(xué)慕課答案
- 低碳示范區(qū)評價(jià)技術(shù)規(guī)范低碳景區(qū)
- 語法填空謂語和非謂語動詞解題技巧課件(共16張)
- 人教版七年級上冊數(shù)學(xué)《整式的加減》單元作業(yè)設(shè)計(jì)
- (2024年)勞動法課件勞務(wù)派遣
- 2023年福建省招聘事業(yè)單位人員考試真題及答案
- 兒童支氣管哮喘共患病診治專家共識
- 新修訂《中小學(xué)教師職業(yè)道德規(guī)范》解讀
- 小學(xué)語文-人教部編版四年級語文下冊第七單元測試卷(三)(有答案)
- 聚焦強(qiáng)軍目標(biāo)投身強(qiáng)軍實(shí)踐課件
評論
0/150
提交評論