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

SpringBoot攔截器與文件上傳實(shí)現(xiàn)方法與源碼分析

 更新時(shí)間:2022年10月08日 10:31:40   作者:Decade0712  
其實(shí)spring boot攔截器的配置方式和springMVC差不多,只有一些小的改變需要注意下就ok了。本文主要給大家介紹了關(guān)于如何在Springboot實(shí)現(xiàn)登陸攔截器與文件上傳功能,需要的朋友可以參考下

一、攔截器

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

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

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

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

接下來(lái),我們就通過(guò)一個(gè)小例子來(lái)了解一下攔截器在spring boot中的使用

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

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

package com.decade.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    // 在調(diào)用控制器接口方法之前進(jìn)入,如果放回true就放行,進(jìn)入下一個(gè)攔截器或者控制器,如果返回false就不繼續(xù)往下走
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 獲取當(dāng)前請(qǐng)求路徑
        final String requestURL = request.getRequestURI();
        log.info("攔截到的請(qǐng)求為:{}", requestURL);
        final HttpSession session = request.getSession();
        final Object userSession = session.getAttribute("loginUser");
        // 如果session中存在用戶登錄信息,那么就判定為用戶已登錄,放行
        if (null != userSession) {
            return true;
        } else {
            // model和request都會(huì)往請(qǐng)求域中塞信息,所以這里可以使用request傳遞我們需要返回給前端的信息
            request.setAttribute("msg", "請(qǐng)登錄!");
            // 轉(zhuǎn)發(fā)到登錄頁(yè)
            request.getRequestDispatcher("/").forward(request, response);
            return false;
        }
    }
    //調(diào)用前提:preHandle返回true
    //調(diào)用時(shí)間:Controller方法處理完之后,DispatcherServlet進(jìn)行視圖的渲染之前,也就是說(shuō)在這個(gè)方法中你可以對(duì)ModelAndView進(jìn)行操作
    //執(zhí)行順序:鏈?zhǔn)絀nterceptor情況下,Interceptor按照聲明的順序倒著執(zhí)行。
    //備注:postHandle雖然post打頭,但post、get方法都能處理
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle執(zhí)行{}", modelAndView);
    }
    //調(diào)用前提:preHandle返回true
    //調(diào)用時(shí)間:DispatcherServlet進(jìn)行視圖的渲染之后
    //多用于清理資源
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("頁(yè)面渲染完成后執(zhí)行");
    }
}

2、配置攔截器

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

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

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

package com.decade.config;
import com.decade.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // addPathPatterns:設(shè)置要攔截的請(qǐng)求,如果是/**,那么會(huì)攔截包括靜態(tài)資源在內(nèi)的所有請(qǐng)求
        // excludePathPatterns:設(shè)置不被攔截的請(qǐng)求,這里我們放行登錄頁(yè)請(qǐng)求和靜態(tài)資源
        registry.addInterceptor(new LoginInterceptor())
            .addPathPatterns("/**")
            .excludePathPatterns("/", "/login", "/css/**", "/images/**", "/js/**", "/fonts/**");
    }
}

我們?cè)谖吹卿浀臓顟B(tài)下,對(duì)主頁(yè)發(fā)起一個(gè)請(qǐng)求,可以發(fā)現(xiàn),攔截器生效,而且攔截器中的方法所執(zhí)行的順序也符合預(yù)期

二、攔截器原理

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

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

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

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

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

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

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

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

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        // 如果攔截器的preHandle()返回false,那么就會(huì)調(diào)用下面的triggerAfterCompletion()
        if (!interceptor.preHandle(request, response, this.handler)) {
            this.triggerAfterCompletion(request, response, (Exception)null);
            return false;
        }
    }
    return true;
}
// 這個(gè)方法里面會(huì)從當(dāng)前遍歷到的攔截器開(kāi)始,倒序執(zhí)行afterCompletion()方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
    for(int i = this.interceptorIndex; i >= 0; --i) {
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        try {
            interceptor.afterCompletion(request, response, this.handler, ex);
        } catch (Throwable var7) {
            logger.error("HandlerInterceptor.afterCompletion threw exception", var7);
        }
    }
}

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

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

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
    for(int i = this.interceptorList.size() - 1; i >= 0; --i) {
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        interceptor.postHandle(request, response, this.handler, mv);
    }
}

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

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

三、文件上傳

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

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

接下來(lái),我們就通過(guò)一個(gè)例子,了解Spring Boot中的文件上傳

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

  • 默認(rèn)情況下,enctype的值是application/x-www-form-urlencoded,不能用于文件上傳,只有使用了multipart/form-data,才能完整的傳遞文件數(shù)據(jù)
  • multiple表示可接受多個(gè)值的文件上傳字段
<div class="panel-body">
    <form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
        <div class="form-group">
            <label for="exampleInputEmail1">郵箱</label>
            <input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
        </div>
        <div class="form-group">
            <label for="exampleInputPassword1">名字</label>
            <input type="text" name="username" class="form-control" id="exampleInputPassword1" placeholder="Password">
        </div>
        <div class="form-group">
            <label for="exampleInputFile">頭像</label>
            <input type="file" name="headerImg" id="exampleInputFile">
        </div>
        <div class="form-group">
            <label for="exampleInputFile">生活照</label>
            <input type="file" name="photos" multiple>
        </div>
        <div class="checkbox">
            <label>
                <input type="checkbox"> Check me out
            </label>
        </div>
        <button type="submit" class="btn btn-primary">提交</button>
    </form>
</div>

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

package com.decade.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@Controller
@Slf4j
public class FileUploadController {
    /**
     * 頁(yè)面跳轉(zhuǎn),跳轉(zhuǎn)到文件上傳頁(yè)面
     * @return 跳轉(zhuǎn)到文件上傳頁(yè)面
     */
    @GetMapping(value = "/form_layouts")
    public String uploadPage() {
        return "form/form_layouts";
    }
    /**
     * 文件上傳請(qǐng)求
     * @param email 郵件
     * @param username 用戶名
     * @param headerImg 頭像文件
     * @param photos 生活照
     * @return 如果上傳文件成功,跳轉(zhuǎn)到首頁(yè)
     */
    @PostMapping(value = "/upload")
    public String uploadFile(@RequestParam(name = "email") String email,
        @RequestParam(name = "username") String username, @RequestPart("headerImg") MultipartFile headerImg,
        @RequestPart("photos") MultipartFile[] photos) {
        log.info("請(qǐng)求參數(shù)email{}, username{}, 頭像headerImg大小{}, 生活照photos張數(shù){}",
            email, username, headerImg.getSize(), photos.length);
        try {
            // 判斷頭像文件是否為空,如果不是為空,那么就保存到本地
            if (!headerImg.isEmpty()) {
                final String filename = headerImg.getOriginalFilename();
                headerImg.transferTo(new File("D:\\test1\\" + filename));
            }
            // 判斷生活照是否上傳,循環(huán)保存到本地
            if (photos.length > 0) {
                for (MultipartFile photo : photos) {
                    final String originalFilename = photo.getOriginalFilename();
                    photo.transferTo(new File("D:\\test1\\" + originalFilename));
                }
            }
        } catch (IOException e) {
            log.error("上傳文件出錯(cuò)!", e);
        }
        return "redirect:/main.html";
    }
}

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

# 單個(gè)文件最大限制
spring.servlet.multipart.max-file-size=10MB
# 單次請(qǐng)求最大限制
spring.servlet.multipart.max-request-size=100MB

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

四、文件上傳流程

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

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

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

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

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    // 設(shè)置文件解析默認(rèn)值為false
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    try {
        try {
            ModelAndView mv = null;
            Object dispatchException = null;
            try {
            	// 檢查當(dāng)前請(qǐng)求是否涉及文件上傳
                processedRequest = this.checkMultipart(request);
                // 將文件解析設(shè)置為true,表明當(dāng)前請(qǐng)求涉及文件上傳
                multipartRequestParsed = processedRequest != request;

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

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

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

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
	// 如果文件上傳解析器不為空,那么就調(diào)用StandardServletMultipartResolver類中的isMultipart()判斷當(dāng)前請(qǐng)求是否涉及文件上傳
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
        if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
            if (DispatcherType.REQUEST.equals(request.getDispatcherType())) {
                this.logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
            }
        } else if (this.hasMultipartException(request)) {
            this.logger.debug("Multipart resolution previously failed for current request - skipping re-resolution for undisturbed error rendering");
        } else {
            try {
            	// 將原生的請(qǐng)求封裝成一個(gè)StandardMultipartHttpServletRequest請(qǐng)求,把文件相關(guān)信息解析放進(jìn)Map中
                return this.multipartResolver.resolveMultipart(request);

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

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

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

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

在這個(gè)解析器中,他又會(huì)根據(jù)參數(shù)的名稱去上面checkMultipart()方法所生成的Map中獲取文件相關(guān)信息

@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    HttpServletRequest servletRequest = (HttpServletRequest)request.getNativeRequest(HttpServletRequest.class);
    Assert.state(servletRequest != null, "No HttpServletRequest");
    RequestPart requestPart = (RequestPart)parameter.getParameterAnnotation(RequestPart.class);
    boolean isRequired = (requestPart == null || requestPart.required()) && !parameter.isOptional();
    // 獲取文件上傳的參數(shù)名稱
    String name = this.getPartName(parameter, requestPart);
    parameter = parameter.nestedIfOptional();
    Object arg = null;
    // 根據(jù)參數(shù)名稱去獲取前面map中的value,也就是MultipartFile對(duì)象
    Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);

后面的調(diào)用鏈為MultipartResolutionDelegate.resolveMultipartArgument()—>判斷當(dāng)前參數(shù)是否是文件上傳,如果是,繼續(xù)判斷是多文件上傳還是單文件上傳—>然后進(jìn)入AbstractMultipartHttpServletRequest中,單文件走getFile()從map中獲取文件信息,多文件走getFiles()從map中獲取文件信息

最后,在控制器的目標(biāo)方法處使用MultipartFile類實(shí)現(xiàn)文件上傳的相關(guān)功能

到此這篇關(guān)于SpringBoot攔截器與文件上傳實(shí)現(xiàn)方法與源碼分析的文章就介紹到這了,更多相關(guān)SpringBoot攔截器與文件上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringMVC 中HttpMessageConverter簡(jiǎn)介和Http請(qǐng)求415 的問(wèn)題

    SpringMVC 中HttpMessageConverter簡(jiǎn)介和Http請(qǐng)求415 的問(wèn)題

    本文介紹且記錄如何解決在SpringMVC 中遇到415 Unsupported Media Type 的問(wèn)題,并且順便介紹Spring MVC的HTTP請(qǐng)求信息轉(zhuǎn)換器HttpMessageConverter
    2016-07-07
  • Spring?AOP底層原理及代理模式

    Spring?AOP底層原理及代理模式

    這篇文章主要為大家介紹了Spring?AOP底層原理及代理模式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • Java基礎(chǔ)面試題之volatile詳解

    Java基礎(chǔ)面試題之volatile詳解

    Volatile可以看做是輕量級(jí)的 Synchronized,它只保證了共享變量的可見(jiàn)性,下面這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)面試題之volatile的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • java高級(jí)用法之注解和反射講義

    java高級(jí)用法之注解和反射講義

    這篇文章主要給大家介紹了關(guān)于java高級(jí)用法之注解和反射講義的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • Java回溯法解決全排列問(wèn)題流程詳解

    Java回溯法解決全排列問(wèn)題流程詳解

    從n個(gè)不同元素中任取m(m≤n)個(gè)元素,按照一定的順序排列起來(lái),叫做從n個(gè)不同元素中取出m個(gè)元素的一個(gè)排列。當(dāng)m=n時(shí)所有的排列情況叫全排列。這篇文章主要介紹了Java回溯法解決全排列問(wèn)題
    2022-10-10
  • 一篇文章帶你入門(mén)Java接口

    一篇文章帶你入門(mén)Java接口

    這篇文章主要介紹了JAVA中接口的定義和接口的實(shí)現(xiàn),文中講解非常細(xì)致,配合代碼更好的幫大家學(xué)習(xí)參考,感興趣的朋友可以了解下
    2021-08-08
  • Java中的對(duì)象和引用詳解

    Java中的對(duì)象和引用詳解

    這篇文章主要介紹了Java中的對(duì)象和引用詳解的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • java-servlet-轉(zhuǎn)發(fā)AND路徑(詳解)

    java-servlet-轉(zhuǎn)發(fā)AND路徑(詳解)

    下面小編就為大家?guī)?lái)一篇java-servlet-轉(zhuǎn)發(fā)AND路徑(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • fastjson 使用方法詳細(xì)介紹

    fastjson 使用方法詳細(xì)介紹

    Fastjson是一個(gè)Java語(yǔ)言編寫(xiě)的JSON處理器,由阿里巴巴公司開(kāi)發(fā)。接下來(lái)通過(guò)本文給大家分享fastjson 使用方法詳細(xì)介紹,感興趣的朋友一起看看吧
    2017-11-11
  • Java實(shí)現(xiàn)發(fā)送HTML內(nèi)容并帶附件的電子郵件

    Java實(shí)現(xiàn)發(fā)送HTML內(nèi)容并帶附件的電子郵件

    這篇文章主要為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)發(fā)送HTML內(nèi)容并帶附件的電子郵件,文中的示例代碼講解詳細(xì),有需要的小伙伴可以參考一下
    2025-01-01

最新評(píng)論

逊克县| 黑水县| 公主岭市| 漳浦县| 鹤岗市| 龙里县| 屏山县| 嵊州市| 哈尔滨市| 永兴县| 土默特右旗| 西贡区| 固始县| 育儿| 北碚区| 罗源县| 南宫市| 东丽区| 通河县| 崇文区| 马尔康县| 东山县| 灵寿县| 绥化市| 马鞍山市| 景宁| 宁海县| 四子王旗| 新绛县| 石景山区| 江安县| 晋城| 巴中市| 噶尔县| 杭州市| 玉环县| 蕉岭县| 乌拉特前旗| 平顶山市| 凤翔县| 丰镇市|