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

Spring?Boot項目如何優(yōu)雅實現(xiàn)Excel導(dǎo)入與導(dǎo)出功能

 更新時間:2022年06月10日 16:47:40   作者:大鵬cool  
在我們平時工作中經(jīng)常會遇到要操作Excel的功能,比如導(dǎo)出個用戶信息或者訂單信息的Excel報表,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot項目中如何優(yōu)雅實現(xiàn)Excel導(dǎo)入與導(dǎo)出功能的相關(guān)資料,需要的朋友可以參考下

背景

Excel 導(dǎo)入與導(dǎo)出是項目中經(jīng)常用到的功能,在 Java 中常用 poi 實現(xiàn) Excel 的導(dǎo)入與導(dǎo)出。由于 poi 占用內(nèi)存較大,在高并發(fā)下很容易發(fā)生 OOM 或者頻繁 fullgc,阿里基于 poi 開源了 EasyExcel 項目。

除了節(jié)約內(nèi)存,EasyExcel 還簡化了 API,通過注解映射 Excel 單元格與對象字段之間的關(guān)系,簡單的幾行代碼就能搞定復(fù)雜的導(dǎo)入導(dǎo)出功能了。

EasyExcel 問題

看似一切美好,不過經(jīng)常做 Excel 導(dǎo)入與導(dǎo)出就會發(fā)現(xiàn),EasyExcel 還是沒那么完美的。

首先,導(dǎo)入與導(dǎo)出 Excel 本質(zhì)是上將 Excel 文件內(nèi)容與 Java 對象之間做一個映射,EasyExcel 做的只是在這兩者之間轉(zhuǎn)換。如果項目中的 Excel 導(dǎo)入與導(dǎo)出功能比較多,會產(chǎn)生大量的樣板式代碼,使用體驗類似于 JDBC。

另外,導(dǎo)入往往還伴隨著校驗,這是 EasyExcel 沒有支持的功能。如果需要校驗,要么寫代碼手動判斷,要么調(diào)用 Java Validation 規(guī)范 定義的 API 判斷,這又會產(chǎn)生大量樣板式代碼。

而且,當(dāng)前 spring boot 已經(jīng)成了必備的 Java 開發(fā)框架,easyexcel 也沒有進行整合。

分析與解決

導(dǎo)入與導(dǎo)出通常發(fā)生在 Web 環(huán)境,對于 Spring MVC 來說,可以將請求信息轉(zhuǎn)換為任意類型的 contoller 方法參數(shù),將 controller 方法返回值轉(zhuǎn)換為客戶端支持的內(nèi)容。

如果能夠使用自定義的 controller 方法參數(shù)接收 Excel 文件內(nèi)容,將 controller 方法返回值轉(zhuǎn)換為 Excel 文件響應(yīng),可以直接消除 Excel 導(dǎo)入與導(dǎo)出時的樣板式代碼。

另外在將請求內(nèi)容轉(zhuǎn)換為 controller 方法參數(shù)時還可以加入自定義的校驗邏輯。

由于 Excel 導(dǎo)入與導(dǎo)出樣板式代碼、校驗問題與具體的業(yè)務(wù)邏輯無關(guān),可以單獨抽象出來,我這里在 EasyExcel 的基礎(chǔ)上封裝了一個 easyexcel-spring-boot-starter 的項目,大大降低了 EasyExcel 上手的門檻,對用戶來說只需要使用 EasyExcel 定義的注解提供映射關(guān)系就可以了,適用于簡單場景的導(dǎo)入導(dǎo)出。

項目代碼已上傳 github easyexcel-spring-boot-starter 倉庫,點擊鏈接即可查閱。下面就來看看怎樣使用吧。

Spring Boot Excel 導(dǎo)入與導(dǎo)出

依賴引入

首先需要引入依賴,坐標(biāo)如下。

<dependency>
    <groupId>com.zzuhkp</groupId>
    <artifactId>easyexcel-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

不過很不幸的是目前還沒傳至中央倉庫,需要的小伙伴可自行上傳到私有倉庫或直接把代碼嵌入自己的項目。

Excel 導(dǎo)入

首先看下要導(dǎo)入的 Excel 內(nèi)容吧。

為了接收 Excel 文件內(nèi)容,我們需要定義一個對應(yīng)的 Model 類。

@Data
public class DemoData {
    @ExcelProperty(index = 0)
    private Integer integer;

    @ExcelProperty(index = 1)
    private String string;

    @ExcelProperty(index = 2)
    private Date date;
}

基本導(dǎo)入功能

然后使用 List<T> 參數(shù)接收即可。

@PostMapping("/list/obj")
public List<DemoData> listObj(@ExcelParam List<DemoData> list) {
    return list;
}

注意參數(shù)前添加了 @ExcelParam 注解,用來標(biāo)識 Excel 文件參數(shù)。這樣,一個導(dǎo)入功能實現(xiàn)了,是不是很簡單呢?

默認(rèn)情況下接收名稱為 file 的表單字段作為 Excel 文件,如果不滿足還可以修改。

@ExcelParam(value = "file", required = true)

進階導(dǎo)入功能

有時候,我們可能比較關(guān)心對象對應(yīng) Excel 的元數(shù)據(jù),例如這個對象是第幾行記錄產(chǎn)生的,這個對象的字段對應(yīng) Excel 第幾列,這個時候我們可以使用 ReadRows<T> 參數(shù)接收 Excel。

@PostMapping("/list/rows")
public ReadRows<DemoData> readRows(@ExcelParam ReadRows<DemoData> readRows) {
    return readRows;
}

ReadRows 使用兩個字段記錄行映射關(guān)系與列映射關(guān)系。

public class ReadRows<T> {

    private ExcelReadHeadProperty excelReadHeadProperty;

    private List<ReadRow<T>> rows;
}

ExcelReadHeadProperty 是 EasyExcel 自帶的類,表示列映射關(guān)系的元數(shù)據(jù)。ReadRow 是框架自定義的類,表示行映射關(guān)系的元數(shù)據(jù)。

看下 ReadRow 定義吧。

public class ReadRow<T> {

    // 行索引,從 0 開始
    private final Integer rowIndex;

    // 行記錄對應(yīng)對象
    private final T data;
}

使用 ExcelReadHeadProperty 獲取字段對應(yīng)列索引的示例代碼如下。

// 對象字段名稱 -> 從 0 開始的列索引
Map<String, Integer> fieldColumnIndexMap = readRows.getExcelReadHeadProperty().getHeadMap().values()
        .stream().collect(Collectors.toMap(Head::getFieldName, Head::getColumnIndex));

Excel 導(dǎo)出

這里對 Excel 的導(dǎo)出進行了簡單的支持。將 List<T> 定義為 controller 方法返回值即可。

@ExcelResponse
@GetMapping("/list/download")
public List<DemoData> downloadList() {
    return Arrays.asList(new DemoData(1, "hello", new Date()), new DemoData(2, "excel", new Date()));
}

需要注意的是使用 @ExcelResponse 注解表示響應(yīng)內(nèi)容為 Excel 文件。默認(rèn)情況,下載的文件名稱為 default.xlxs,寫入到名稱為 Sheet1 的工作表中。如果不滿足需求可以修改。

@ExcelResponse(fileName = "測試文件", sheetName = "工作表1")

Excel 導(dǎo)入?yún)?shù)校驗

參數(shù)校驗是 Excel 導(dǎo)入常用的功能,這里進行了強有力的支持,使用體驗如原生 spring boot 校驗般順滑。

開啟校驗

與 spring boot 原生使用方式一樣,將 @Validated@Valid 注解添加到 @ExcelParam 參數(shù)上即可。

@PostMapping("/list/obj")
public List<DemoData> listObj(@ExcelParam @Validated List<DemoData> list) {
    return list;
}

校驗規(guī)則定義

Bean Validation 定義校驗規(guī)則

默認(rèn)情況下框架使用 JSR-303 Bean Validation 規(guī)范定義的校驗注解校驗,需要手動引入 spring-boot-starter-validation,可通過設(shè)置環(huán)境變量 easyexcel.validator.default.enable=false 關(guān)閉。

@Data
public class DemoData {
    @NotNull(message = "參數(shù)不能為空")
    private Integer integer;

    private String string;

    private Date date;
}

另外還可以自定義注解對對象校驗。

... 省略其他元注解
@Constraint(validatedBy = {DemoDataValid.DemoDataValidator.class})
public @interface DemoDataValid {
		... 省略注解屬性
		
    class DemoDataValidator implements ConstraintValidator<DemoDataValid, DemoData> {

        @Override
        public boolean isValid(DemoData value, ConstraintValidatorContext context) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("測試對象校驗").addConstraintViolation();
            return false;
        }
    }
}
@DemoDataValid
public class DemoData {
    ... 省略屬性
}

ExcelValidator 接口定義校驗規(guī)則

Bean Validation 注解只能校驗單個字段或?qū)ο?,如果需要對所有的對象進行校驗,可以實現(xiàn)框架定義的 ExcelValidator 接口,然后將實現(xiàn)定義為 Spring Bean。

這個接口定義如下。

public interface ExcelValidator<T> {
    ExcelValidErrors validate(ReadRows<T> readRows);
}

ExcelValidErrors 用于接收校驗的錯誤信息,分別使用接口 ExcelValidObjectErrorExcelValidFieldError 接口定義行錯誤信息和單元格錯誤信息。

public class ExcelValidErrors {
	// 行錯誤信息或單元格錯誤信息列表
    private final List<ExcelValidObjectError> errors;
}

public interface ExcelValidObjectError {
    // 獲取行號,從 1 開始
    Integer getRow();

    // 獲取錯誤消息
    String getMessage();
}

public interface ExcelValidFieldError extends ExcelValidObjectError {
    // 獲取列,從 1 開始
    Integer getColumn();
}

例如,如果需要對所有的 DemoData 校驗 integer 字段的值不能重復(fù),可以使用如下的代碼。

@Component
public class CustomExcelValidator implements ExcelValidator<DemoData> {
    @Override
    public ExcelValidErrors validate(ReadRows<DemoData> readRows) {
        ExcelValidErrors errors = new ExcelValidErrors();

        Map<Integer, List<ReadRow<DemoData>>> group = readRows.getRows().stream()
                .collect(Collectors.groupingBy(item -> item.getData().getInteger()));

        for (Map.Entry<Integer, List<ReadRow<DemoData>>> entry : group.entrySet()) {
            if (entry.getValue().size() > 1) {
                for (ReadRow<DemoData> readRow : entry.getValue()) {
                    errors.addError(new DefaultExcelObjectError(readRow.getRowIndex() + 1, "參數(shù)重復(fù)"));
                }
            }
        }
        return errors;
    }
}

校驗結(jié)果接收

與 Spring MVC 設(shè)計類似,這里也提供了兩種接收校驗結(jié)果的方式。

異常捕獲接收校驗結(jié)果

開啟校驗后,如果校驗結(jié)果中包含錯誤,會將錯誤信息封裝到 ExcelValidException,并拋出異常,可以通過全局異常捕獲的方式收集錯誤信息。

@RestControllerAdvice
public class GlobalExceptionControllerAdvice {
    @ExceptionHandler(ExcelValidException.class)
    public String handleException(ExcelValidException e) {
        ExcelValidErrors errors = e.getErrors();
        return JSON.toJSONString(errors);
    }
}

controller 方法參數(shù)接收校驗結(jié)果

如果不想通過異常捕獲的方式接收校驗的錯誤信息,還可以將錯誤信息添加到 @ExcelParam 參數(shù)的后面,示例代碼如下。

@PostMapping("/list/obj")
public List<DemoData> listObj(@ExcelParam @Validated List<DemoData> list, ExcelValidErrors errors) {
    if (errors.hasErrors()) {
        String messages = errors.getAllErrors().stream().map(ExcelValidObjectError::getMessage).collect(Collectors.joining(" | "));
        throw new RuntimeException("發(fā)現(xiàn)異常:" + messages);
    }
    return list;
}

總結(jié)

easyexcel-spring-boot-starter 綜合應(yīng)用了前面文章介紹的各種 Spring 知識,代碼量并不大,對實現(xiàn)感興趣的小伙伴可自行查閱代碼。由于這個框架是把 Excel 中所有的行數(shù)據(jù)收集到內(nèi)存,因此只適合一些比較簡單的場景。

到此這篇關(guān)于Spring Boot項目如何優(yōu)雅實現(xiàn)Excel導(dǎo)入與導(dǎo)出功能的文章就介紹到這了,更多相關(guān)SpringBoot實現(xiàn)Excel導(dǎo)入導(dǎo)出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java List接口與Iterator接口及foreach循環(huán)使用解析

    Java List接口與Iterator接口及foreach循環(huán)使用解析

    這篇文章主要介紹了Java List接口與Iterator接口及foreach循環(huán),主要包括List接口與Iterator接口及foreach循環(huán)具體的使用方法和代碼,需要的朋友可以參考下
    2022-04-04
  • 深入dom4j使用selectSingleNode方法報錯分析

    深入dom4j使用selectSingleNode方法報錯分析

    本篇文章是對dom4j使用selectSingleNode方法報錯進行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • 詳解Spring 注解之@Import 注入的各種花活

    詳解Spring 注解之@Import 注入的各種花活

    這篇文章主要介紹了詳解Spring 注解之@Import 注入的各種花活,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • Java 照片對比功能的實現(xiàn)

    Java 照片對比功能的實現(xiàn)

    這篇文章主要介紹了Java 照片比對功能實現(xiàn)類的示例代碼,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-12-12
  • 詳解SpringCloud Zuul過濾器返回值攔截

    詳解SpringCloud Zuul過濾器返回值攔截

    Zuul作為網(wǎng)關(guān)服務(wù),是其他各服務(wù)對外中轉(zhuǎn)站,通過Zuul進行請求轉(zhuǎn)發(fā)。這篇文章主要介紹了詳解SpringCloud Zuul過濾器返回值攔截,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • Java線程休眠之sleep方法詳解

    Java線程休眠之sleep方法詳解

    這篇文章主要介紹了Java線程休眠之sleep方法詳解,Thread?類中有一個靜態(tài)方法的sleep方法,當(dāng)該線程調(diào)用sleep方法后,就會暫時讓CPU的調(diào)度權(quán),但是監(jiān)視器資源比如鎖并不會釋放出去,需要的朋友可以參考下
    2024-01-01
  • Java接口的回調(diào)機制的實現(xiàn)

    Java接口的回調(diào)機制的實現(xiàn)

    本文主要介紹了Java中回調(diào)機制的實現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-01-01
  • java實現(xiàn)emqx設(shè)備上下線監(jiān)聽詳解

    java實現(xiàn)emqx設(shè)備上下線監(jiān)聽詳解

    這篇文章主要為大家介紹了java實現(xiàn)emqx設(shè)備上下線監(jiān)聽詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • Java解析XML(4種方式)案例詳解

    Java解析XML(4種方式)案例詳解

    這篇文章主要介紹了Java解析XML(4種方式)案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • 詳解Java SpringAOP切面類

    詳解Java SpringAOP切面類

    這篇文章主要為大家介紹了Java SpringAOP的切面類,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-12-12

最新評論

得荣县| 荃湾区| 营口市| 五大连池市| 新野县| 育儿| 南召县| 耿马| 陵水| 鄂托克前旗| 汨罗市| 凤庆县| 敖汉旗| 泸西县| 犍为县| 长寿区| 城市| 株洲县| 长乐市| 杭锦旗| 西畴县| 广饶县| 江山市| 于田县| 德格县| 基隆市| 淮北市| 高青县| 勐海县| 富顺县| 阿巴嘎旗| 巴南区| 醴陵市| 鞍山市| 抚顺市| 辽阳县| 中阳县| 托克逊县| 揭阳市| 万盛区| 桦甸市|