1. 程式人生 > >Spring學習筆記之Spring MVC的高階技術

Spring學習筆記之Spring MVC的高階技術

本篇主要介紹利用Spring MVC處理檔案上傳,異常處理,為控制器新增通知以及跨重定向請求傳遞資料。

1.Spring MVC處理檔案上傳

1.1使用multipart格式提交表單

在有檔案上傳的表單中,我們需要使用multipart格式的資料來上傳,multipart格式的資料會將一個表單拆分成多個部分(part),每個部分對應一個輸入域。在一半的表單輸入域中,它所對應的部分會放置文字型資料,但是如果上傳檔案的話,它所對應的部分可以是二進位制。下面是前段頁面表單部分:

<form action="${ctx}/register" enctype="multipart/form-data"
method="post">
<label>使用者名稱:</label> <input type="text" name="username"><br/> <label>密碼:</label> <input type="password" name="password"><br/> <label>郵箱:</label> <input type="text" name="email"
>
<br/> <label>頭像:</label> <input type="file" name="image" accept="image/jpeg,image/png,image/gif"><br/> <input type="submit" value="註冊"> </form>

form表單現在將enctype屬性設定為multipart/form-data,這會告訴瀏覽器以multipart資料的形式提交表單,而不是以表單資料的形式進行提交。在multipart中,每個輸入域都會對應一個part。而檔案上傳輸入域中的accept屬性用來將檔案型別限制為JPEG、PNG和GIF圖片。

1.2配置multipart解析器

DispatcherServlet將解析multipart請求資料的任務委託給了Spring中MultipartResolver策略介面的實現。Spring內建了兩個MultipartResolver的實現供我們選擇:

  • CommonsMultipartResolver:使用Jakarta Commons FileUpload解析multipart請求
  • StandardServletMultipartResolver:依賴於Servlet3.0對multipart請求的支援。

使用Servlet 3.0解析multipart請求:
在Spring MVC上下文中將其宣告為bean:

@Configuration 
@EnableWebMvc //啟用Spring MVC
@ComponentScan("spittr.web") //啟用元件掃描
public class WebConfig 
        extends WebMvcConfigurerAdapter{
    /**
     * 配置multipart解析器
     * @return
     */
    @Bean
    public MultipartResolver multipartResolver(){
        return new StandardServletMultipartResolver();
    }
}

StandardServletMultipartResolver的配置是在Servlet中通過傳入一個MultipartConfigElement例項來指定:
如果是自定義的Servlet,即(自己實現WebApplicationInitializer),這樣做:

public class myServletIntializer implements WebApplicationInitializer{
    public void onStartup(ServletContext servletContext) throws ServletException {
        MyServlet myServlet = new MyServlet();
        Dynamic dynamic = servletContext.addServlet("myServlet", myServlet);
        dynamic.addMapping("/");
        dynamic.setMultipartConfig(
                new MultipartConfigElement("/tmp/spittr/uploads"));
    }
}

如果是通過繼承AbstractAnnotationConfigDispatcherServletInitializer的到的DispatcherServlet的話,將不會有對Dynamic的引用,還記得上一篇我們提到的麼,通過過載customizeRegistration會提供一個Dynamic引數,so:

@Override
    protected void customizeRegistration(Dynamic registration) {
        registration.setMultipartConfig(
                new MultipartConfigElement("/tmp/spittr/uploads"));
    }

這裡,我們都是通過構造器來對MultipartConfigElement進行配置的。new MultipartConfigElement(location, maxFileSize, maxRequestSize, fileSizeThreshold)這是其所有的構造器。

  • location:用來指定上傳檔案的臨時寫入目錄,一定要注意是絕對目錄,比如Windows下的F:\Workspace\spring,上面我們配置的是Linux下的/tmp/spittr/uploads
  • maxFileSize:上傳檔案的最大容量(以位元組為單位),預設無限制
  • maxRequestSize:整個multipart請求的最大容量(以位元組為單位),不會關心有多少個part以及每個part的大小。預設是沒有限制的
  • fileSizeThreshold:在檔案上傳的過程中,如果檔案大小達到了一個指定最大容量(以位元組為單位),將會寫入到臨時檔案路徑中。預設值為0,也就是所有上床的檔案都會寫入到磁碟上。

下面是用web.xml來配置MultipartConfigElement,使用<servlet><multipart-config>元素:

<servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <multipart-config>
            <location>/tmp/spittr/uploads</location>
            <max-file-size>2097152</max-file-size>
            <max-request-size>4194304</max-request-size>
            <file-size-threshold>0</file-size-threshold>
        </multipart-config>
    </servlet>

限制檔案的大小為2MB,整個請求不超過4MB,而且所有的檔案都寫到磁盤裡。

使用使用Jakarta Commons FileUpload解析multipart請求
宣告為bean:

@Bean
    public MultipartResolver multipartResolver(){
        //return new StandardServletMultipartResolver();
        return new CommonsMultipartResolver();
    }

CommonsMultipartResolver的配置不是在Servlet指定的,而是直接配置在例項中,而且CommonsMultipartResolver不會輕質要求設定臨時檔案路徑,預設情況下,這個路徑就是Servlet容器的臨時目錄。下面是配置一個等價於上面我們對MultipartConfigElement的配置:

@Bean
    public MultipartResolver multipartResolver() throws IOException{
        //return new StandardServletMultipartResolver();
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
        multipartResolver.setUploadTempDir(
                new FileSystemResource("/tmp/spittr/uploads"));//對應於MultipartConfigElement的location
        multipartResolver.setMaxUploadSize(2097152);//對應於MultipartConfigElement的maxFileSize
        multipartResolver.setMaxInMemorySize(0);//對應於MultipartConfigElement的fileSizeThreshold
        return multipartResolver;
    }

CommonsMultipartResolver 無法設定multipart請求整體的最大容量。

1.3處理multipart請求

1.3.1用byte[]陣列來接受上傳檔案的二進位制資料
程式碼:

@RequestMapping("/register")
    public void spittle(
            @RequestPart("image") byte[] image,
            User user){
        System.out.println(image.length);
    }

1.3.2接受MultipartFile:

@RequestMapping("/register")
    public void spittle(
            @RequestPart("image") MultipartFile image,
            User user,HttpServletRequest request) throws IllegalStateException, IOException{
        System.out.println(image.getContentType());
        System.out.println(image.getName());
        System.out.println(image.getOriginalFilename());
        System.out.println(image.getSize());
        image.transferTo(
                new File("/uploads/"+image.getOriginalFilename()));//將上傳檔案寫入到檔案系統中
    }

需要注意的是這裡File的位置是以你配置的臨時檔案路徑為前提的,比如說你的臨時檔案路徑為F:\Workspace\spring,那麼上面的檔案就是在F:\Workspace\spring\uploads\下

2.處理異常

Servlet請求的輸出都是一個Servlet響應。如果在請求處理的時候,出現了異常,那它放入輸出依然會使Servlet響應。異常必須要以某種方法轉換為響應。
Spring提供了多種方式將異常轉換為響應:

  • 特定的Spring異常將會自動對映為指定的HTTP狀態碼
  • 異常上可以新增@ResponseStatus註解,從而將其對映為某一個HTTP狀態碼
  • 在方法上可以新增@ExceptionHandler註解,使其用來處理異常。

2.1將異常對映為HTTP狀態碼

Spring的一些異常會預設對映為HTTP狀態碼

Spring異常 HTTP狀態碼
BindException 400-Bad Request
ConversionNotSupportedException 500-Internal Server Error
HttpMediaTypeNotAcceptableException 406-Not Acceptable
HttpMediaTypeNotSupportedException 415-Unsupported Media Type
HttpMessageNotReadableException 400-Bad Request
HttpMessageNotWritableException 500-Internal Server Error
HttpRequestMethodNotSupportedException 405-Method Not Allowed
MethodArgumentNotValidException 400-Bad Request
MissingServletRequestParameterException 400-Bad Request
MissingServletRequestPartException 400-Bad Request
NoSuchRequestHandlingMethodException 404-Not Found
TypeMismatchException 400-Bad Request

下面介紹將異常對映為特定的狀態碼
首先我們建立一個異常,在其上面使用@ResponseStatus來將異常對映為特定的狀態碼。

@ResponseStatus(value=HttpStatus.NOT_FOUND,
                reason="Param Not Found")//將異常對映為HTTP狀態404
public class ParamNotFountException extends RuntimeException{
    private static final long serialVersionUID = 1L;
}

在控制器如果接受到的username為空則丟擲這個異常:

@RequestMapping("/register")
    public String spittle(
            @RequestPart("image") MultipartFile image,
            User user,HttpServletRequest request){
        //如果使用者名稱為空則丟擲異常
        if(user.getUsername().isEmpty()){
            throw new ParamNotFountException();
        }
        return "register";
    }

截圖:
返回的異常

2.2編寫異常處理的方法

繼續上面的方法,如果我們不想顯示錯誤頁面,想要捕獲異常的話,平時我們會使用catch進行捕獲,但catch只能捕獲當前語句塊的異常並處理。下面我們來使用@ExceptionHandler標註的方法,它可以出來同一個控制器中所有處理器方法丟擲的異常。接著上面的程式碼,我們在控制器方法下面寫這樣一個方法:

@RequestMapping("/register")
    public String spittle(
            @RequestPart("image") MultipartFile image,
            User user,HttpServletRequest request){
        //如果使用者名稱為空則丟擲異常
        if(user.getUsername().isEmpty()){
            throw new ParamNotFountException();
        }
        return "register";
    }

    //捕獲這個控制器丟擲的ParamNotFountException異常,並處理
    @ExceptionHandler(value=ParamNotFountException.class)
    public String HandleException(){
        System.out.println("在這裡處理異常");
        return "error";
    }

這一次,返回的不是404頁面,而是error頁面,而且控制檯輸出:“在這裡處理異常”欄位。

3.為控制器新增通知

控制器通知(controller advice)是任意帶有@ControllerAdvice註解的類,這個類會包含一個或多個如下型別的方法:

  • @ExceptionHandler註解標註的方法;
  • @InitBinder註解標註的方法;
  • @ModelAttribute註解標註的方法。

在帶有@ControllerAdvice註解的類中,以上所述的這些方法會運用到整個應用程式所有控制器中帶有@RequestMapping註解的方法上。@ControllerAdvice註解本身自帶掃描特性,即使用了@Component。
還是上面的程式碼,我們把控制器中的HandleException()方法刪掉,然後新建一個類,執行效果是一樣的:

@ControllerAdvice
public class AppWideException {

    //捕獲所有控制器丟擲的ParamNotFountException異常,並處理
        @ExceptionHandler(value=ParamNotFountException.class)
        public String HandleException(){
            System.out.println("在這裡處理異常");
            return "error";
        }
}

4.跨重定向請求傳遞資料

當一個處理器方法完成之後,該方法所指定的模型資料將會複製到請求中,並作為請求的屬性,請求會轉發(forward)到檢視上進行渲染。因為控制器方法和檢視所處理的是同一個請求,所以在轉發的過程中,請求屬效能夠得以儲存。當控制器的結果是重定向的話,原始的請求就結束了,並且會發起一個新的get請求。原始請求中所帶有的模型資料也就隨之請求一起消亡了。
有兩種方法能夠從發起重定向的方法傳遞資料給處理重定向方法中:

  • 使用URL模板以路徑變數和/或查詢引數的形式傳遞資料
  • 通過flash屬性發生資料

4.1通過URL模板進行重定向

使用URL中的佔位符進行傳遞:

@RequestMapping("/register")
    public String spittle(User user,Model model){
        model.addAttribute("username", "xuexiaoqiang");
        model.addAttribute("email", "[email protected]");
        //重定向
        return "redirect:/test/{username}";
    }

    @RequestMapping("/test/{username}")
    public String test(
            @PathVariable(value="username") String username,
            HttpServletRequest request){
        String email = request.getParameter("email");
        System.out.println("username:"+username);
        System.out.println("email:"+email);
                return "register";
    }

輸出:

username:xuexiaoqiang
email:test@tom.com

這裡,因為模型中的email沒有匹配重定向URL中的任何佔位符,所以它會自動以查詢引數的形式附加到重定向URL上。這裡最後的重定向路徑是“/test/[email protected]”。

4.2使用flash屬性

如果我們想要發生一個物件,那麼URL就不能實現,Spring提供了將資料發生為flash屬性的功能。按照定義,flash屬性會一直攜帶這些資料知道下一次請求,然後才會消失。
RedirectAttributes是Model的一個子介面,提供了Model的所有功能,除此之外,還有幾個方法是用來設定flash屬性的。

@RequestMapping("/register")
    public String spittle(User user,RedirectAttributes model){
        user.setEmail("[email protected]");
        user.setUsername("xuexiaoqiang");
        model.addFlashAttribute("user", user);
        //重定向
        return "redirect:/test";
    }

    @RequestMapping("/test")
    public String test(Model model){
        if(model.containsAttribute("user")){
            System.out.println("存在");
        }
    return "testdata";
    }

testdata.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>使用者名稱:${user.username }</h1>
<h1>郵箱:${user.email }</h1>
</body>
</html>

最後結果截圖:
顯示model中的資料

在重定向執行之前,所有flash屬性都會複製到會話中。在重定向後,存在會話中的flash屬性會被取出,並從會話轉移到模型之中。處理重定向的方法就能從模型中訪問User物件了,就像獲取其他的模型物件一樣。

相關推薦

Spring學習筆記Spring MVC高階技術

本篇主要介紹利用Spring MVC處理檔案上傳,異常處理,為控制器新增通知以及跨重定向請求傳遞資料。 1.Spring MVC處理檔案上傳 1.1使用multipart格式提交表單 在有檔案上傳的表單中,我們需要使用multipart格式的資料來

Spring學習筆記BeanDefinition

spring bean definition 在Spring容器中,Bean的實例以BeanDefinition來表示的。一個BeanDefinition描述了一個Bean實例。本文出自 “十裏稻花香” 博客,請務必保留此出處http://5880861.blog.51cto.com/587086

Spring學習筆記BeanFactory

spring bean factory BeanFactory是一個頂級接口,下面看下它是幹什麽的。這裏說了,它是訪問Spring Bean容器的根接口,是Bean容器的基本視圖。它的一些子接口,比如ListableBeanFactory和ConfigurableBeanFactory都有特別的而

Spring學習筆記ApplicationContext

spring application context 對於一個Application提供配置的核心接口,在應用運行的時候它是只讀的。一個ApplicationContext提供以下能力:1、可以通過BeanFactory中的方法訪問應用組件2、可以加載文件資源3、可以給已經註冊的監聽器發送事件4、

Spring學習筆記啟動

spring 原理 啟動分析今天,以ClassPathXmlApplicationContext為例來看一下,Spring啟動的時候都做了什麽重點看refresh()方法refresh()方法是在AbstractApplicationContext類中定義的ClassPathXmlApplicationCo

Sprng Cloud學習筆記Spring Cloud簡介

Spring Cloud Spring Cloud是一系列框架的有序集合(Spring Cloud並不是一個專案,它是一套專案的組合)。它利用Spring Boot的開發便利性巧妙地簡化了分散式系統基礎設施的開發,如服務發現註冊、配置中心、訊息匯流排、負載均衡、斷路器、資料監控等,都可以

1.Spring學習筆記 ———— Bean的例項化

    什麼是Bean的例項化?     在面向物件的程式中,想要使用某個物件,就需要先例項化這個物件。Spring中,想要使用容器中的Bean,也需要例項化Bean。     其類似於當我們需要建立一個類物件而去new這個類一樣。通常來說,當我們需要用到一個Bean的時

2.Spring學習筆記 ————IoC(控制反轉)

控制反轉(IoC),是Spring裡一個專有的名詞,其意思就是說,物件的例項由Spring容器來進行建立而不是我們自己手動建立,當我們在Spring容器中設定好Bean屬性後,Spring容器就會自動建立其例項,我們只要去呼叫Spring的Bean就行。 接下來是例子:

Spring學習筆記自動化裝配Bean

在Spring中可以使用Java程式碼、XML和自動化裝配三種方式來裝配Bean。從便利性角度來說,最強大的還是Spring的自動化配置,如果Spring能夠進行自動化裝配的話,那何苦還要顯式的將這些Bean裝配在一起呢? Spring從兩個角度來實現自動化裝配: 元件

Spring學習筆記前置通知、後置通知

在學習之前先提一個概念。 AsprctJ:Java社群裡最完整最流行的AOP框架。在Spring2.0以上版本中,可以使用基於AspectJ註解或基於XML配置的AOP。 啟用AspectJ註解支援 要在 Spring 中宣告 AspectJ 切面,

Spring學習筆記Bean的作用域

在預設情況下,Spring的應用上下文中所有的bean都是單例的形式建立的。也就是說,不管給定的一個bean被注入到其它bean多少次,每次注入的都是同一個例項。 在大多數情況下,單例bean是非常理想的方案。初始化和垃圾回收物件例項所帶來的成本只留給一些小規模任務,在這些任

spring學習筆記:ioc容器高階特性

Spring容器的高階特性涉及到屬性編輯器,使用外部屬性檔案,國際化,容器事件等等; 今天講解一下屬性編輯器,使用外部資源,國際化。 屬性編輯器  如果你沒有了解過屬性編輯器,建議你先google一下,我簡單的解釋一下什麼是屬性編輯器,看一個新寫的有代表性的bean:pack

《Pro Spring學習筆記Spring+ActiveMQ實現Queue通訊(點對點)

spring配置檔案: <?xml version="1.0" encoding="UTF-8"?><beans    xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="h

spring學習筆記spring mvc不得不說的幾處配置

通常情況下會有這麼幾個配置檔案:web.xml, applicationContext.xml, xxxx-servlet.xml, 在web.xml中通常會這麼配置DispatcherServlet:<servlet> <servlet-name>

Spring學習筆記--SpEL

本文內容來源於尚矽谷視訊教程,僅作為本文學習筆記使用,請勿轉載! 1.什麼是SpEL? Spring表示式語言,簡稱SpEL,是一個支援執行時查詢和操作物件的強大的表示式語言。語法類似於EL,格式為#{...}。SpEL為Bean的屬性進行動態賦值提供了便利。 2.可以做什

新手學習筆記Spring編寫AOP半自動代理

invoke proc pro aspect 新手 接口 src info nbsp 1.導包: 2.目標類 package oyb.service; public interface UserService { public void add(

spring學習筆記(一) Spring概述

數據庫 spring容器 oot 基礎知識 spa 遠程 組合 主動 拓展 博主Spring學習筆記整理大部分內容來自Spring實戰(第四版)這本書. 強烈建議新手購入或者需要電子書的留言. 在學習Spring之前,我們要了解這麽幾個問題:什麽是Spring?Sprin

學習筆記《Java核心技術卷I》---- 第八章 泛型程式設計

泛型類的定義格式:class Pair<T>{ } 普通類中泛型方法的定義:public static <T> T getMiddle(T... a){ return a[a.length / 2]; } 呼叫方法時,可以使用:ClassName.getMi

學習筆記《Java核心技術卷I》---- 第七章 異常、斷言和日誌

異常物件都是派生與Throwable的一個例項 派生於Error類或RuntimeException類的所有異常稱為非受查異常,所有其他異常稱為受查異常 一個方法必須宣告所有可能丟擲的受查異常,而非受查異常要麼不可控制,要麼就應該避免發生 關鍵字throws位於方法之

學習筆記《Java核心技術卷I》---- 第六章 介面、lambda表示式與內部類

介面中的所有方法都自動地屬於public。因此,在介面中宣告方法時,不必提供關鍵字public;但是在實現介面的類中,必須在實現介面中的方法時把介面中的方法宣告為public,如果不宣告,那就預設包訪問許可權,編譯器會報錯 實現Comparabale介面,必須實現其中的compareTo