From e9856e5549d83fdcc08b182b98bc9f7a9cf64448 Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Fri, 28 Apr 2023 22:14:21 +0800 Subject: [PATCH] =?UTF-8?q?*=20=E9=BB=98=E8=AE=A4=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E5=8F=8D=E5=B0=84=E7=BC=93=E5=AD=98=E6=94=B9=E6=88=90`ThreadLo?= =?UTF-8?q?cal`,=E5=B9=B6=E6=94=AF=E6=8C=81=E8=AE=BE=E7=BD=AE=20[Issue=20#?= =?UTF-8?q?2792](https://github.com/alibaba/easyexcel/issues/2792)=20*=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=A0=B9=E6=8D=AE`includeColumnIndexes`?= =?UTF-8?q?=E5=92=8C`includeColumnFieldNames`=E6=8E=92=E5=BA=8F=20[Issue?= =?UTF-8?q?=20#2697](https://github.com/alibaba/easyexcel/issues/2697)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/analysis/ExcelAnalyserImpl.java | 2 + .../excel/context/WriteContextImpl.java | 3 +- .../excel/enums/CacheLocationEnum.java | 23 + .../excel/metadata/AbstractHolder.java | 9 + .../excel/metadata/BasicParameter.java | 8 + .../alibaba/excel/metadata/FieldCache.java | 37 ++ .../excel/metadata/GlobalConfiguration.java | 11 + .../metadata/property/ExcelHeadProperty.java | 34 +- .../listener/ModelBuildEventListener.java | 3 +- .../property/ExcelReadHeadProperty.java | 5 +- .../com/alibaba/excel/util/ClassUtils.java | 435 +++++++++++------- .../AbstractExcelWriterParameterBuilder.java | 9 +- .../write/executor/ExcelWriteAddExecutor.java | 29 +- .../executor/ExcelWriteFillExecutor.java | 8 +- .../write/metadata/WriteBasicParameter.java | 7 +- .../metadata/holder/AbstractWriteHolder.java | 45 +- .../write/metadata/holder/WriteHolder.java | 29 +- .../metadata/holder/WriteWorkbookHolder.java | 1 + .../property/ExcelWriteHeadProperty.java | 5 +- .../ExcludeOrIncludeDataTest.java | 14 +- update.md | 5 +- 21 files changed, 483 insertions(+), 239 deletions(-) create mode 100644 easyexcel-core/src/main/java/com/alibaba/excel/enums/CacheLocationEnum.java create mode 100644 easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldCache.java diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java index f8f34d90..8421a160 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java @@ -19,6 +19,7 @@ import com.alibaba.excel.read.metadata.holder.csv.CsvReadWorkbookHolder; import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.ClassUtils; import com.alibaba.excel.util.DateUtils; import com.alibaba.excel.util.FileUtils; import com.alibaba.excel.util.NumberDataFormatterUtils; @@ -212,6 +213,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { private void removeThreadLocalCache() { NumberDataFormatterUtils.removeThreadLocalCache(); DateUtils.removeThreadLocalCache(); + ClassUtils.removeThreadLocalCache(); } private void clearEncrypt03() { diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index db892989..9fff8449 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -265,7 +265,7 @@ public class WriteContextImpl implements WriteContext { Head head = entry.getValue(); int columnIndex = entry.getKey(); ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(null, - currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), head.getFieldName()); + currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), head.getFieldName(), currentWriteHolder); CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext(this, row, rowIndex, head, columnIndex, relativeRowIndex, Boolean.TRUE, excelContentProperty); @@ -427,6 +427,7 @@ public class WriteContextImpl implements WriteContext { private void removeThreadLocalCache() { NumberDataFormatterUtils.removeThreadLocalCache(); DateUtils.removeThreadLocalCache(); + ClassUtils.removeThreadLocalCache(); } @Override diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/CacheLocationEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/CacheLocationEnum.java new file mode 100644 index 00000000..e2deed52 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/CacheLocationEnum.java @@ -0,0 +1,23 @@ +package com.alibaba.excel.enums; + +/** + * cache locaciton + * + * @author Jiaju Zhuang + **/ +public enum CacheLocationEnum { + /** + * The cache will be stored in {@code ThreadLocal}, and will be cleared when the excel read and write is completed. + */ + THREAD_LOCAL, + + /** + * The cache will not be cleared unless the app is stopped. + */ + MEMORY, + + /** + * No caching.It may lose some of performance. + */ + NONE; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java index 839a8b4b..ac97c03b 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java @@ -83,6 +83,15 @@ public abstract class AbstractHolder implements ConfigurationHolder { globalConfiguration.setLocale(basicParameter.getLocale()); } + if (basicParameter.getFiledCacheLocation() == null) { + if (prentAbstractHolder != null) { + globalConfiguration.setFiledCacheLocation( + prentAbstractHolder.getGlobalConfiguration().getFiledCacheLocation()); + } + } else { + globalConfiguration.setFiledCacheLocation(basicParameter.getFiledCacheLocation()); + } + } @Override diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/BasicParameter.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/BasicParameter.java index 0c11f942..e0127a8b 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/BasicParameter.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/BasicParameter.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Locale; import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CacheLocationEnum; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -54,4 +55,11 @@ public class BasicParameter { * default is false */ private Boolean useScientificFormat; + + /** + * The cache used when parsing fields such as head. + * + * default is THREAD_LOCAL. + */ + private CacheLocationEnum filedCacheLocation; } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldCache.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldCache.java new file mode 100644 index 00000000..19fbc3e3 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldCache.java @@ -0,0 +1,37 @@ +package com.alibaba.excel.metadata; + +import java.lang.reflect.Field; +import java.util.Map; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * filed cache + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@AllArgsConstructor +public class FieldCache { + + /** + * A field cache that has been sorted by a class. + * It will exclude fields that are not needed. + */ + private Map sortedFieldMap; + + /** + * Fields using the index attribute + */ + private Map indexFieldMap; + + /** + * Fields to ignore + */ + private Map ignoreMap; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java index 5cafe90c..e11f6e9d 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java @@ -2,6 +2,8 @@ package com.alibaba.excel.metadata; import java.util.Locale; +import com.alibaba.excel.enums.CacheLocationEnum; + import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; @@ -32,6 +34,7 @@ public class GlobalConfiguration { * used when formatting dates and numbers. */ private Locale locale; + /** * Whether to use scientific Format. * @@ -39,10 +42,18 @@ public class GlobalConfiguration { */ private Boolean useScientificFormat; + /** + * The cache used when parsing fields such as head. + * + * default is THREAD_LOCAL. + */ + private CacheLocationEnum filedCacheLocation; + public GlobalConfiguration() { this.autoTrim = Boolean.TRUE; this.use1904windowing = Boolean.FALSE; this.locale = Locale.getDefault(); this.useScientificFormat = Boolean.FALSE; + this.filedCacheLocation = CacheLocationEnum.MEMORY; } } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java index 61240d31..c8bc36d7 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java @@ -9,6 +9,8 @@ import java.util.TreeMap; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.metadata.ConfigurationHolder; +import com.alibaba.excel.metadata.FieldCache; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Holder; import com.alibaba.excel.util.ClassUtils; @@ -51,22 +53,17 @@ public class ExcelHeadProperty { * Configuration header information */ private Map headMap; - /** - * Fields ignored - */ - private Map ignoreMap; - public ExcelHeadProperty(Holder holder, Class headClazz, List> head) { + public ExcelHeadProperty(ConfigurationHolder configurationHolder, Class headClazz, List> head) { this.headClazz = headClazz; headMap = new TreeMap<>(); - ignoreMap = MapUtils.newHashMap(); headKind = HeadKindEnum.NONE; headRowNumber = 0; if (head != null && !head.isEmpty()) { int headIndex = 0; for (int i = 0; i < head.size(); i++) { - if (holder instanceof AbstractWriteHolder) { - if (((AbstractWriteHolder)holder).ignore(null, i)) { + if (configurationHolder instanceof AbstractWriteHolder) { + if (((AbstractWriteHolder)configurationHolder).ignore(null, i)) { continue; } } @@ -76,7 +73,7 @@ public class ExcelHeadProperty { headKind = HeadKindEnum.STRING; } // convert headClazz to head - initColumnProperties(holder); + initColumnProperties(configurationHolder); initHeadRowNumber(); if (LOGGER.isDebugEnabled()) { @@ -104,24 +101,15 @@ public class ExcelHeadProperty { } } - private void initColumnProperties(Holder holder) { + private void initColumnProperties(ConfigurationHolder configurationHolder) { if (headClazz == null) { return; } - // Declared fields - Map sortedAllFieldMap = MapUtils.newTreeMap(); - Map indexFieldMap = MapUtils.newTreeMap(); - - boolean needIgnore = (holder instanceof AbstractWriteHolder) && ( - !CollectionUtils.isEmpty(((AbstractWriteHolder)holder).getExcludeColumnFieldNames()) || !CollectionUtils - .isEmpty(((AbstractWriteHolder)holder).getExcludeColumnIndexes()) || !CollectionUtils - .isEmpty(((AbstractWriteHolder)holder).getIncludeColumnFieldNames()) || !CollectionUtils - .isEmpty(((AbstractWriteHolder)holder).getIncludeColumnIndexes())); - - ClassUtils.declaredFields(headClazz, sortedAllFieldMap, indexFieldMap, ignoreMap, needIgnore, holder); + FieldCache fieldCache = ClassUtils.declaredFields(headClazz, configurationHolder); - for (Map.Entry entry : sortedAllFieldMap.entrySet()) { - initOneColumnProperty(entry.getKey(), entry.getValue(), indexFieldMap.containsKey(entry.getKey())); + for (Map.Entry entry : fieldCache.getSortedFieldMap().entrySet()) { + initOneColumnProperty(entry.getKey(), entry.getValue(), + fieldCache.getIndexFieldMap().containsKey(entry.getKey())); } headKind = HeadKindEnum.CLASS; } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java index 41a4f1f6..c3fb3bc9 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java @@ -149,7 +149,8 @@ public class ModelBuildEventListener implements IgnoreExceptionReadListener cellData = cellDataMap.get(index); Object value = ConverterUtils.convertToJavaObject(cellData, head.getField(), ClassUtils.declaredExcelContentProperty(dataMap, readSheetHolder.excelReadHeadProperty().getHeadClazz(), - fieldName), readSheetHolder.converterMap(), context, context.readRowHolder().getRowIndex(), index); + fieldName, readSheetHolder), readSheetHolder.converterMap(), context, + context.readRowHolder().getRowIndex(), index); if (value != null) { dataMap.put(fieldName, value); } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/property/ExcelReadHeadProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/property/ExcelReadHeadProperty.java index 9c332ceb..0329715c 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/property/ExcelReadHeadProperty.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/property/ExcelReadHeadProperty.java @@ -2,6 +2,7 @@ package com.alibaba.excel.read.metadata.property; import java.util.List; +import com.alibaba.excel.metadata.ConfigurationHolder; import com.alibaba.excel.metadata.Holder; import com.alibaba.excel.metadata.property.ExcelHeadProperty; @@ -12,7 +13,7 @@ import com.alibaba.excel.metadata.property.ExcelHeadProperty; */ public class ExcelReadHeadProperty extends ExcelHeadProperty { - public ExcelReadHeadProperty(Holder holder, Class headClazz, List> head) { - super(holder, headClazz, head); + public ExcelReadHeadProperty(ConfigurationHolder configurationHolder, Class headClazz, List> head) { + super(configurationHolder, headClazz, head); } } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/ClassUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/ClassUtils.java index b42df514..d0a99c00 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/util/ClassUtils.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/ClassUtils.java @@ -14,6 +14,7 @@ import java.util.Map; import java.util.Optional; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; @@ -24,7 +25,10 @@ import com.alibaba.excel.annotation.write.style.ContentFontStyle; import com.alibaba.excel.annotation.write.style.ContentStyle; import com.alibaba.excel.converters.AutoConverter; import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CacheLocationEnum; import com.alibaba.excel.exception.ExcelCommonException; +import com.alibaba.excel.metadata.ConfigurationHolder; +import com.alibaba.excel.metadata.FieldCache; import com.alibaba.excel.metadata.Holder; import com.alibaba.excel.metadata.property.DateTimeFormatProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty; @@ -59,18 +63,38 @@ import org.springframework.util.CollectionUtils; */ public class ClassUtils { - public static final Map, FieldCache> FIELD_CACHE = new ConcurrentHashMap<>(); + /** + * memory cache + */ + public static final ConcurrentHashMap, FieldCache> FIELD_CACHE = new ConcurrentHashMap<>(); + /** + * thread local cahe + */ + private static final ThreadLocal, FieldCache>> FIELD_THREAD_LOCAL = new ThreadLocal<>(); /** * The cache configuration information for each of the class */ - public static final Map, Map> CLASS_CONTENT_CACHE + public static final ConcurrentHashMap, Map> CLASS_CONTENT_CACHE = new ConcurrentHashMap<>(); /** * The cache configuration information for each of the class */ - public static final Map CONTENT_CACHE = new ConcurrentHashMap<>(); + private static final ThreadLocal, Map>> CLASS_CONTENT_THREAD_LOCAL + = new ThreadLocal<>(); + + /** + * The cache configuration information for each of the class + */ + public static final ConcurrentHashMap CONTENT_CACHE + = new ConcurrentHashMap<>(); + + /** + * The cache configuration information for each of the class + */ + private static final ThreadLocal> CONTENT_THREAD_LOCAL + = new ThreadLocal<>(); /** * Calculate the configuration information for the class @@ -81,7 +105,7 @@ public class ClassUtils { * @return */ public static ExcelContentProperty declaredExcelContentProperty(Map dataMap, Class headClazz, - String fieldName) { + String fieldName, ConfigurationHolder configurationHolder) { Class clazz = null; if (dataMap instanceof BeanMap) { Object bean = ((BeanMap)dataMap).getBean(); @@ -89,26 +113,49 @@ public class ClassUtils { clazz = bean.getClass(); } } - return getExcelContentProperty(clazz, headClazz, fieldName); + return getExcelContentProperty(clazz, headClazz, fieldName, configurationHolder); } - private static ExcelContentProperty getExcelContentProperty(Class clazz, Class headClass, String fieldName) { - return CONTENT_CACHE.computeIfAbsent(buildKey(clazz, headClass, fieldName), key -> { - ExcelContentProperty excelContentProperty = Optional.ofNullable(declaredFieldContentMap(clazz)) - .map(map -> map.get(fieldName)) - .orElse(null); - ExcelContentProperty headExcelContentProperty = Optional.ofNullable(declaredFieldContentMap(headClass)) - .map(map -> map.get(fieldName)) - .orElse(null); - ExcelContentProperty combineExcelContentProperty = new ExcelContentProperty(); - - combineExcelContentProperty(combineExcelContentProperty, headExcelContentProperty); - if (clazz != headClass) { - combineExcelContentProperty(combineExcelContentProperty, excelContentProperty); - } - return combineExcelContentProperty; - }); + private static ExcelContentProperty getExcelContentProperty(Class clazz, Class headClass, String fieldName, + ConfigurationHolder configurationHolder) { + switch (configurationHolder.globalConfiguration().getFiledCacheLocation()) { + case THREAD_LOCAL: + Map contentCacheMap = CONTENT_THREAD_LOCAL.get(); + if (contentCacheMap == null) { + contentCacheMap = MapUtils.newHashMap(); + CONTENT_THREAD_LOCAL.set(contentCacheMap); + } + return contentCacheMap.computeIfAbsent(buildKey(clazz, headClass, fieldName), key -> { + return doGetExcelContentProperty(clazz, headClass, fieldName, configurationHolder); + }); + case MEMORY: + return CONTENT_CACHE.computeIfAbsent(buildKey(clazz, headClass, fieldName), key -> { + return doGetExcelContentProperty(clazz, headClass, fieldName, configurationHolder); + }); + case NONE: + return doGetExcelContentProperty(clazz, headClass, fieldName, configurationHolder); + default: + throw new UnsupportedOperationException("unsupported enum"); + } + } + private static ExcelContentProperty doGetExcelContentProperty(Class clazz, Class headClass, + String fieldName, ConfigurationHolder configurationHolder) { + ExcelContentProperty excelContentProperty = Optional.ofNullable( + declaredFieldContentMap(clazz, configurationHolder)) + .map(map -> map.get(fieldName)) + .orElse(null); + ExcelContentProperty headExcelContentProperty = Optional.ofNullable( + declaredFieldContentMap(headClass, configurationHolder)) + .map(map -> map.get(fieldName)) + .orElse(null); + ExcelContentProperty combineExcelContentProperty = new ExcelContentProperty(); + + combineExcelContentProperty(combineExcelContentProperty, headExcelContentProperty); + if (clazz != headClass) { + combineExcelContentProperty(combineExcelContentProperty, excelContentProperty); + } + return combineExcelContentProperty; } public static void combineExcelContentProperty(ExcelContentProperty combineExcelContentProperty, @@ -140,182 +187,248 @@ public class ClassUtils { return new ContentPropertyKey(clazz, headClass, fieldName); } - private static Map declaredFieldContentMap(Class clazz) { + private static Map declaredFieldContentMap(Class clazz, + ConfigurationHolder configurationHolder) { if (clazz == null) { return null; } - return CLASS_CONTENT_CACHE.computeIfAbsent(clazz, key -> { - List tempFieldList = new ArrayList<>(); - Class tempClass = clazz; - while (tempClass != null) { - Collections.addAll(tempFieldList, tempClass.getDeclaredFields()); - // Get the parent class and give it to yourself - tempClass = tempClass.getSuperclass(); - } - - ContentStyle parentContentStyle = clazz.getAnnotation(ContentStyle.class); - ContentFontStyle parentContentFontStyle = clazz.getAnnotation(ContentFontStyle.class); - Map fieldContentMap = MapUtils.newHashMapWithExpectedSize( - tempFieldList.size()); - for (Field field : tempFieldList) { - ExcelContentProperty excelContentProperty = new ExcelContentProperty(); - excelContentProperty.setField(field); - - ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); - if (excelProperty != null) { - Class> convertClazz = excelProperty.converter(); - if (convertClazz != AutoConverter.class) { - try { - Converter converter = convertClazz.getDeclaredConstructor().newInstance(); - excelContentProperty.setConverter(converter); - } catch (Exception e) { - throw new ExcelCommonException( - "Can not instance custom converter:" + convertClazz.getName()); - } - } + switch (configurationHolder.globalConfiguration().getFiledCacheLocation()) { + case THREAD_LOCAL: + Map, Map> classContentCacheMap + = CLASS_CONTENT_THREAD_LOCAL.get(); + if (classContentCacheMap == null) { + classContentCacheMap = MapUtils.newHashMap(); + CLASS_CONTENT_THREAD_LOCAL.set(classContentCacheMap); } + return classContentCacheMap.computeIfAbsent(clazz, key -> { + return doDeclaredFieldContentMap(clazz); + }); + case MEMORY: + return CLASS_CONTENT_CACHE.computeIfAbsent(clazz, key -> { + return doDeclaredFieldContentMap(clazz); + }); + case NONE: + return doDeclaredFieldContentMap(clazz); + default: + throw new UnsupportedOperationException("unsupported enum"); + } - ContentStyle contentStyle = field.getAnnotation(ContentStyle.class); - if (contentStyle == null) { - contentStyle = parentContentStyle; - } - excelContentProperty.setContentStyleProperty(StyleProperty.build(contentStyle)); + } + + private static Map doDeclaredFieldContentMap(Class clazz) { + if (clazz == null) { + return null; + } + List tempFieldList = new ArrayList<>(); + Class tempClass = clazz; + while (tempClass != null) { + Collections.addAll(tempFieldList, tempClass.getDeclaredFields()); + // Get the parent class and give it to yourself + tempClass = tempClass.getSuperclass(); + } - ContentFontStyle contentFontStyle = field.getAnnotation(ContentFontStyle.class); - if (contentFontStyle == null) { - contentFontStyle = parentContentFontStyle; + ContentStyle parentContentStyle = clazz.getAnnotation(ContentStyle.class); + ContentFontStyle parentContentFontStyle = clazz.getAnnotation(ContentFontStyle.class); + Map fieldContentMap = MapUtils.newHashMapWithExpectedSize( + tempFieldList.size()); + for (Field field : tempFieldList) { + ExcelContentProperty excelContentProperty = new ExcelContentProperty(); + excelContentProperty.setField(field); + + ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); + if (excelProperty != null) { + Class> convertClazz = excelProperty.converter(); + if (convertClazz != AutoConverter.class) { + try { + Converter converter = convertClazz.getDeclaredConstructor().newInstance(); + excelContentProperty.setConverter(converter); + } catch (Exception e) { + throw new ExcelCommonException( + "Can not instance custom converter:" + convertClazz.getName()); + } } - excelContentProperty.setContentFontProperty(FontProperty.build(contentFontStyle)); + } - excelContentProperty.setDateTimeFormatProperty( - DateTimeFormatProperty.build(field.getAnnotation(DateTimeFormat.class))); - excelContentProperty.setNumberFormatProperty( - NumberFormatProperty.build(field.getAnnotation(NumberFormat.class))); + ContentStyle contentStyle = field.getAnnotation(ContentStyle.class); + if (contentStyle == null) { + contentStyle = parentContentStyle; + } + excelContentProperty.setContentStyleProperty(StyleProperty.build(contentStyle)); - fieldContentMap.put(field.getName(), excelContentProperty); + ContentFontStyle contentFontStyle = field.getAnnotation(ContentFontStyle.class); + if (contentFontStyle == null) { + contentFontStyle = parentContentFontStyle; } - return fieldContentMap; - }); + excelContentProperty.setContentFontProperty(FontProperty.build(contentFontStyle)); + + excelContentProperty.setDateTimeFormatProperty( + DateTimeFormatProperty.build(field.getAnnotation(DateTimeFormat.class))); + excelContentProperty.setNumberFormatProperty( + NumberFormatProperty.build(field.getAnnotation(NumberFormat.class))); + + fieldContentMap.put(field.getName(), excelContentProperty); + } + return fieldContentMap; } /** * Parsing field in the class * - * @param clazz Need to parse the class - * @param sortedAllFieldMap Complete the map of sorts - * @param indexFieldMap Use the index to sort fields - * @param ignoreMap You want to ignore field map - * @param needIgnore If you want to ignore fields need to ignore - * @param holder holder + * @param clazz Need to parse the class + * @param needIgnore If you want to ignore fields need to ignore + * @param holder holder + * @param cacheLocation cache lcation */ - public static void declaredFields(Class clazz, Map sortedAllFieldMap, - Map indexFieldMap, Map ignoreMap, Boolean needIgnore, Holder holder) { - FieldCache fieldCache = declaredFields(clazz); - if (fieldCache == null) { - return; + public static FieldCache declaredFields(Class clazz, ConfigurationHolder configurationHolder) { + switch (configurationHolder.globalConfiguration().getFiledCacheLocation()) { + case THREAD_LOCAL: + Map, FieldCache> fieldCacheMap = FIELD_THREAD_LOCAL.get(); + if (fieldCacheMap == null) { + fieldCacheMap = MapUtils.newHashMap(); + FIELD_THREAD_LOCAL.set(fieldCacheMap); + } + return fieldCacheMap.computeIfAbsent(clazz, key -> { + return doDeclaredFields(clazz, configurationHolder); + }); + case MEMORY: + return FIELD_CACHE.computeIfAbsent(clazz, key -> { + return doDeclaredFields(clazz, configurationHolder); + }); + case NONE: + return doDeclaredFields(clazz, configurationHolder); + default: + throw new UnsupportedOperationException("unsupported enum"); } - if (ignoreMap != null) { - ignoreMap.putAll(fieldCache.getIgnoreMap()); + } + + private static FieldCache doDeclaredFields(Class clazz, ConfigurationHolder configurationHolder) { + List tempFieldList = new ArrayList<>(); + Class tempClass = clazz; + // When the parent class is null, it indicates that the parent class (Object class) has reached the top + // level. + while (tempClass != null) { + Collections.addAll(tempFieldList, tempClass.getDeclaredFields()); + // Get the parent class and give it to yourself + tempClass = tempClass.getSuperclass(); } - Map tempIndexFieldMap = indexFieldMap; - if (tempIndexFieldMap == null) { - tempIndexFieldMap = MapUtils.newTreeMap(); + // Screening of field + Map> orderFieldMap = new TreeMap>(); + Map indexFieldMap = new TreeMap(); + Map ignoreMap = new HashMap(16); + + ExcelIgnoreUnannotated excelIgnoreUnannotated = clazz.getAnnotation(ExcelIgnoreUnannotated.class); + for (Field field : tempFieldList) { + declaredOneField(field, orderFieldMap, indexFieldMap, ignoreMap, excelIgnoreUnannotated); } - tempIndexFieldMap.putAll(fieldCache.getIndexFieldMap()); + Map sortedFieldMap = buildSortedAllFieldMap(orderFieldMap, indexFieldMap); + FieldCache fieldCache = new FieldCache(sortedFieldMap, indexFieldMap, ignoreMap); - Map originSortedAllFieldMap = fieldCache.getSortedAllFieldMap(); - if (!needIgnore) { - sortedAllFieldMap.putAll(originSortedAllFieldMap); - return; + if (!(configurationHolder instanceof WriteHolder)) { + return fieldCache; } + WriteHolder writeHolder = (WriteHolder)configurationHolder; + + boolean needIgnore = !CollectionUtils.isEmpty(writeHolder.excludeColumnFieldNames()) + || !CollectionUtils.isEmpty(writeHolder.excludeColumnIndexes()) + || !CollectionUtils.isEmpty(writeHolder.includeColumnFieldNames()) + || !CollectionUtils.isEmpty(writeHolder.includeColumnIndexes()); + + if (!needIgnore) { + return fieldCache; + } + // ignore filed + Map tempSortedFieldMapp = MapUtils.newHashMap(); int index = 0; - for (Map.Entry entry : originSortedAllFieldMap.entrySet()) { + for (Map.Entry entry : sortedFieldMap.entrySet()) { Integer key = entry.getKey(); Field field = entry.getValue(); // The current field needs to be ignored - if (((WriteHolder)holder).ignore(entry.getValue().getName(), entry.getKey())) { + if (writeHolder.ignore(entry.getValue().getName(), entry.getKey())) { if (ignoreMap != null) { ignoreMap.put(field.getName(), field); } - tempIndexFieldMap.remove(index); + indexFieldMap.remove(index); } else { // Mandatory sorted fields - if (tempIndexFieldMap.containsKey(key)) { - sortedAllFieldMap.put(key, field); + if (indexFieldMap.containsKey(key)) { + tempSortedFieldMapp.put(key, field); } else { // Need to reorder automatically // Check whether the current key is already in use - while (sortedAllFieldMap.containsKey(index)) { + while (tempSortedFieldMapp.containsKey(index)) { index++; } - sortedAllFieldMap.put(index++, field); + tempSortedFieldMapp.put(index++, field); } } } - forceIndexIfNecessary(holder, sortedAllFieldMap); + fieldCache.setSortedFieldMap(tempSortedFieldMapp); + + // resort field + resortField(writeHolder, fieldCache); + return fieldCache; } /** - * it only works when {@link AbstractWriteHolder#getIncludeColumnFieldNames()} has value - * and {@link AbstractWriteHolder#getForceIndex()} is true + * it only works when {@link WriteHolder#getIncludeColumnFieldNames()} or + * {@link WriteHolder#getIncludeColumnIndexes()} ()} has value + * and {@link WriteHolder#getSortByIncludeColumn()} ()} is true **/ - private static void forceIndexIfNecessary(Holder holder, Map sortedAllFieldMap) { - if (!(holder instanceof AbstractWriteHolder)) { + private static void resortField(WriteHolder writeHolder, FieldCache fieldCache) { + if (!writeHolder.sortByIncludeColumn()) { return; } - AbstractWriteHolder writeHolder = (AbstractWriteHolder)holder; - Collection allCol = writeHolder.getIncludeColumnFieldNames(); - if (!CollectionUtils.isEmpty(allCol) && writeHolder.getForceIndex() != null && writeHolder.getForceIndex()) { - Map colIndexMap = MapUtils.newHashMap(); - Iterator iterator = allCol.iterator(); - int colIndex = 0; - while (iterator.hasNext()) { - String col = iterator.next(); - colIndexMap.put(col, colIndex); - colIndex++; + Map indexFieldMap = fieldCache.getIndexFieldMap(); + + Collection includeColumnFieldNames = writeHolder.includeColumnFieldNames(); + if (!CollectionUtils.isEmpty(includeColumnFieldNames)) { + // Field sorted map + Map filedIndexMap = MapUtils.newHashMap(); + int fieldIndex = 0; + for (String includeColumnFieldName : includeColumnFieldNames) { + filedIndexMap.put(includeColumnFieldName, fieldIndex++); } - Map temp = MapUtils.newHashMap(); - sortedAllFieldMap.forEach((index, field) -> { - Integer fieldIndex = colIndexMap.get(field.getName()); - temp.put(fieldIndex, field); + + // rebuild sortedFieldMap + Map tempSortedFieldMap = MapUtils.newHashMap(); + fieldCache.getSortedFieldMap().forEach((index, field) -> { + Integer tempFieldIndex = filedIndexMap.get(field.getName()); + if (tempFieldIndex != null) { + tempSortedFieldMap.put(tempFieldIndex, field); + + // The user has redefined the ordering and the ordering of annotations needs to be invalidated + if (!tempFieldIndex.equals(index)) { + indexFieldMap.remove(index); + } + } }); - sortedAllFieldMap.clear(); - sortedAllFieldMap.putAll(temp); + fieldCache.setSortedFieldMap(tempSortedFieldMap); + return; } - } - public static void declaredFields(Class clazz, Map sortedAllFieldMap, Boolean needIgnore, - WriteHolder writeHolder) { - declaredFields(clazz, sortedAllFieldMap, null, null, needIgnore, writeHolder); - } + Collection includeColumnIndexes = writeHolder.includeColumnIndexes(); + if (!CollectionUtils.isEmpty(includeColumnFieldNames)) { + // Index sorted map + Map filedIndexMap = MapUtils.newHashMap(); + int fieldIndex = 0; + for (Integer includeColumnIndexe : includeColumnIndexes) { + filedIndexMap.put(includeColumnIndexe, fieldIndex++); + } - private static FieldCache declaredFields(Class clazz) { - if (clazz == null) { - return null; + // rebuild sortedFieldMap + Map tempSortedFieldMap = MapUtils.newHashMap(); + fieldCache.getSortedFieldMap().forEach((index, field) -> { + Integer tempFieldIndex = filedIndexMap.get(index); + + // The user has redefined the ordering and the ordering of annotations needs to be invalidated + if (tempFieldIndex != null) { + tempSortedFieldMap.put(tempFieldIndex, field); + } + }); + fieldCache.setSortedFieldMap(tempSortedFieldMap); } - return FIELD_CACHE.computeIfAbsent(clazz, key -> { - List tempFieldList = new ArrayList<>(); - Class tempClass = clazz; - // When the parent class is null, it indicates that the parent class (Object class) has reached the top - // level. - while (tempClass != null) { - Collections.addAll(tempFieldList, tempClass.getDeclaredFields()); - // Get the parent class and give it to yourself - tempClass = tempClass.getSuperclass(); - } - // Screening of field - Map> orderFieldMap = new TreeMap>(); - Map indexFieldMap = new TreeMap(); - Map ignoreMap = new HashMap(16); - - ExcelIgnoreUnannotated excelIgnoreUnannotated = clazz.getAnnotation(ExcelIgnoreUnannotated.class); - for (Field field : tempFieldList) { - declaredOneField(field, orderFieldMap, indexFieldMap, ignoreMap, excelIgnoreUnannotated); - } - return new FieldCache(buildSortedAllFieldMap(orderFieldMap, indexFieldMap), indexFieldMap, ignoreMap); - }); } private static Map buildSortedAllFieldMap(Map> orderFieldMap, @@ -380,32 +493,6 @@ public class ClassUtils { orderFieldList.add(field); } - private static class FieldCache { - - private final Map sortedAllFieldMap; - private final Map indexFieldMap; - private final Map ignoreMap; - - public FieldCache(Map sortedAllFieldMap, Map indexFieldMap, - Map ignoreMap) { - this.sortedAllFieldMap = sortedAllFieldMap; - this.indexFieldMap = indexFieldMap; - this.ignoreMap = ignoreMap; - } - - public Map getSortedAllFieldMap() { - return sortedAllFieldMap; - } - - public Map getIndexFieldMap() { - return indexFieldMap; - } - - public Map getIgnoreMap() { - return ignoreMap; - } - } - /** *

Gets a {@code List} of all interfaces implemented by the given * class and its superclasses.

@@ -459,4 +546,10 @@ public class ClassUtils { private Class headClass; private String fieldName; } + + public static void removeThreadLocalCache() { + FIELD_THREAD_LOCAL.remove(); + CLASS_CONTENT_THREAD_LOCAL.remove(); + CONTENT_THREAD_LOCAL.remove(); + } } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java index 89ff01a3..f7f9416c 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java @@ -122,11 +122,12 @@ public abstract class AbstractExcelWriterParameterBuilder sortedAllFieldMap = new TreeMap<>(); int relativeRowIndex = 0; for (Object oneRowData : data) { int lastRowIndex = relativeRowIndex + newRowIndex; - addOneRowOfDataToExcel(oneRowData, lastRowIndex, relativeRowIndex, sortedAllFieldMap); + addOneRowOfDataToExcel(oneRowData, lastRowIndex, relativeRowIndex); relativeRowIndex++; } } - private void addOneRowOfDataToExcel(Object oneRowData, int rowIndex, int relativeRowIndex, - Map sortedAllFieldMap) { + private void addOneRowOfDataToExcel(Object oneRowData, int rowIndex, int relativeRowIndex) { if (oneRowData == null) { return; } @@ -79,7 +77,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { } else if (oneRowData instanceof Map) { addBasicTypeToExcel(new MapRowData((Map)oneRowData), row, rowIndex, relativeRowIndex); } else { - addJavaObjectToExcel(oneRowData, row, rowIndex, relativeRowIndex, sortedAllFieldMap); + addJavaObjectToExcel(oneRowData, row, rowIndex, relativeRowIndex); } WriteHandlerUtils.afterRowDispose(rowWriteHandlerContext); @@ -119,7 +117,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { int dataIndex, int columnIndex) { ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(null, writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), - head == null ? null : head.getFieldName()); + head == null ? null : head.getFieldName(), writeContext.currentWriteHolder()); CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext(writeContext, row, rowIndex, head, columnIndex, relativeRowIndex, Boolean.FALSE, excelContentProperty); @@ -138,8 +136,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext); } - private void addJavaObjectToExcel(Object oneRowData, Row row, int rowIndex, int relativeRowIndex, - Map sortedAllFieldMap) { + private void addJavaObjectToExcel(Object oneRowData, Row row, int rowIndex, int relativeRowIndex) { WriteHolder currentWriteHolder = writeContext.currentWriteHolder(); BeanMap beanMap = BeanMapUtils.create(oneRowData); // Bean the contains of the Map Key method with poor performance,So to create a keySet here @@ -158,7 +155,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { } ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(beanMap, - currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), name); + currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), name, currentWriteHolder); CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext( writeContext, row, rowIndex, head, columnIndex, relativeRowIndex, Boolean.FALSE, excelContentProperty); @@ -185,19 +182,17 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { } maxCellIndex++; - Map ignoreMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getIgnoreMap(); - initSortedAllFieldMapFieldList(oneRowData.getClass(), sortedAllFieldMap); - for (Map.Entry entry : sortedAllFieldMap.entrySet()) { + FieldCache fieldCache = ClassUtils.declaredFields(oneRowData.getClass(), writeContext.currentWriteHolder()); + for (Map.Entry entry : fieldCache.getSortedFieldMap().entrySet()) { Field field = entry.getValue(); String fieldName = FieldUtils.resolveCglibFieldName(field); - boolean uselessData = !beanKeySet.contains(fieldName) || beanMapHandledSet.contains(fieldName) - || ignoreMap.containsKey(fieldName); + boolean uselessData = !beanKeySet.contains(fieldName) || beanMapHandledSet.contains(fieldName); if (uselessData) { continue; } Object value = beanMap.get(fieldName); ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(beanMap, - currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), fieldName); + currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), fieldName, currentWriteHolder); CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext( writeContext, row, rowIndex, null, maxCellIndex, relativeRowIndex, Boolean.FALSE, excelContentProperty); WriteHandlerUtils.beforeCellCreate(cellWriteHandlerContext); @@ -229,7 +224,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { .isEmpty(writeSheetHolder.getExcludeColumnIndexes()) || !CollectionUtils .isEmpty(writeSheetHolder.getIncludeColumnFieldNames()) || !CollectionUtils .isEmpty(writeSheetHolder.getIncludeColumnIndexes()); - ClassUtils.declaredFields(clazz, sortedAllFieldMap, needIgnore, writeSheetHolder); + ClassUtils.declaredFields(clazz, writeSheetHolder); } } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java index f6dc1955..81524223 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java @@ -37,7 +37,9 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import org.apache.commons.collections4.CollectionUtils; + import com.alibaba.excel.util.PoiUtils; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellType; @@ -215,7 +217,8 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } Object value = dataMap.get(variable); ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(dataMap, - writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), variable); + writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), variable, + writeContext.currentWriteHolder()); cellWriteHandlerContext.setExcelContentProperty(excelContentProperty); createCell(analysisCell, fillConfig, cellWriteHandlerContext, rowWriteHandlerContext); @@ -249,7 +252,8 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } Object value = dataMap.get(variable); ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(dataMap, - writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), variable); + writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), variable, + writeContext.currentWriteHolder()); cellWriteHandlerContext.setOriginalValue(value); cellWriteHandlerContext.setOriginalFieldClass(FieldUtils.getFieldClass(dataMap, variable, value)); cellWriteHandlerContext.setExcelContentProperty(excelContentProperty); diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java index 313d766d..68cc09c9 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java @@ -56,8 +56,11 @@ public class WriteBasicParameter extends BasicParameter { * Only output the custom columns. */ private Collection includeColumnFieldNames; + /** - * head sorted use includeColumnFieldNames sort + * Data will be sorted according to {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}. + * + * default is false. */ - private Boolean forceIndex; + private Boolean sortByIncludeColumn; } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java index 4d7bc476..11566e77 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java @@ -14,6 +14,7 @@ import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.ConverterKeyBuild; import com.alibaba.excel.converters.DefaultConverterLoader; import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.enums.HolderEnum; import com.alibaba.excel.event.NotRepeatExecutor; import com.alibaba.excel.metadata.AbstractHolder; import com.alibaba.excel.metadata.Head; @@ -94,10 +95,14 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ * Only output the custom columns. */ private Collection includeColumnFieldNames; + /** - * head sorted use {@link #includeColumnFieldNames} sort + * Data will be sorted according to {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}. + * + * default is false. */ - private Boolean forceIndex; + private Boolean sortByIncludeColumn; + /** * Write handler */ @@ -197,11 +202,17 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ } else { this.includeColumnFieldNames = writeBasicParameter.getIncludeColumnFieldNames(); } - if (writeBasicParameter.getForceIndex() == null && parentAbstractWriteHolder != null) { - this.forceIndex = parentAbstractWriteHolder.getForceIndex(); + + if (writeBasicParameter.getSortByIncludeColumn() == null) { + if (parentAbstractWriteHolder == null) { + this.sortByIncludeColumn = Boolean.FALSE; + } else { + this.sortByIncludeColumn = parentAbstractWriteHolder.getSortByIncludeColumn(); + } } else { - this.forceIndex = writeBasicParameter.getForceIndex(); + this.sortByIncludeColumn = writeBasicParameter.getSortByIncludeColumn(); } + if (writeBasicParameter.getIncludeColumnIndexes() == null && parentAbstractWriteHolder != null) { this.includeColumnIndexes = parentAbstractWriteHolder.getIncludeColumnIndexes(); } else { @@ -488,4 +499,28 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ return getAutomaticMergeHead(); } + @Override + public boolean sortByIncludeColumn() { + return getSortByIncludeColumn(); + } + + @Override + public Collection includeColumnIndexes() { + return getIncludeColumnIndexes(); + } + + @Override + public Collection includeColumnFieldNames() { + return getIncludeColumnFieldNames(); + } + + @Override + public Collection excludeColumnIndexes() { + return getExcludeColumnIndexes(); + } + + @Override + public Collection excludeColumnFieldNames() { + return getExcludeColumnFieldNames(); + } } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java index 77f3c14f..3f60a4d9 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java @@ -1,10 +1,11 @@ package com.alibaba.excel.write.metadata.holder; +import java.util.Collection; + import com.alibaba.excel.metadata.ConfigurationHolder; import com.alibaba.excel.write.property.ExcelWriteHeadProperty; /** - * * Get the corresponding Holder * * @author Jiaju Zhuang @@ -46,4 +47,30 @@ public interface WriteHolder extends ConfigurationHolder { * @return */ int relativeHeadRowIndex(); + + /** + * Data will be sorted according to {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}. + * + * default is false. + */ + boolean sortByIncludeColumn(); + + /** + * Only output the custom columns. + */ + Collection includeColumnIndexes(); + + /** + * Only output the custom columns. + */ + Collection includeColumnFieldNames(); + + /** + * Ignore the custom columns. + */ + Collection excludeColumnIndexes(); + /** + * Ignore the custom columns. + */ + Collection excludeColumnFieldNames(); } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java index 7cc23917..37d91975 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java @@ -11,6 +11,7 @@ import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; +import com.alibaba.excel.enums.CacheLocationEnum; import com.alibaba.excel.enums.HolderEnum; import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.metadata.data.DataFormatData; diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java index 4c11010a..7affadbe 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java @@ -16,6 +16,7 @@ import com.alibaba.excel.annotation.write.style.HeadStyle; import com.alibaba.excel.annotation.write.style.OnceAbsoluteMerge; import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.metadata.CellRange; +import com.alibaba.excel.metadata.ConfigurationHolder; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Holder; import com.alibaba.excel.metadata.property.ColumnWidthProperty; @@ -44,8 +45,8 @@ public class ExcelWriteHeadProperty extends ExcelHeadProperty { private RowHeightProperty contentRowHeightProperty; private OnceAbsoluteMergeProperty onceAbsoluteMergeProperty; - public ExcelWriteHeadProperty(Holder holder, Class headClazz, List> head) { - super(holder, headClazz, head); + public ExcelWriteHeadProperty(ConfigurationHolder configurationHolder, Class headClazz, List> head) { + super(configurationHolder, headClazz, head); if (getHeadKind() != HeadKindEnum.CLASS) { return; } diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeDataTest.java index 6bd824db..578b5a63 100644 --- a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeDataTest.java +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeDataTest.java @@ -34,9 +34,9 @@ public class ExcludeOrIncludeDataTest { private static File includeFieldName07; private static File includeFieldName03; private static File includeFieldNameCsv; - private static File includeFieldNameForceIndex07; - private static File includeFieldNameForceIndex03; - private static File includeFieldNameForceIndexCsv; + private static File includeFieldNameSort07; + private static File includeFieldNameSort03; + private static File includeFieldNameSortCsv; @BeforeClass public static void init() { @@ -52,9 +52,9 @@ public class ExcludeOrIncludeDataTest { includeFieldName07 = TestFileUtil.createNewFile("includeFieldName.xlsx"); includeFieldName03 = TestFileUtil.createNewFile("includeFieldName.xls"); includeFieldNameCsv = TestFileUtil.createNewFile("includeFieldName.csv"); - includeFieldNameForceIndex07 = TestFileUtil.createNewFile("includeFieldNameForceIndex.xlsx"); - includeFieldNameForceIndex03 = TestFileUtil.createNewFile("includeFieldNameForceIndex.xls"); - includeFieldNameForceIndexCsv = TestFileUtil.createNewFile("includeFieldNameForceIndex.csv"); + includeFieldNameSort07 = TestFileUtil.createNewFile("includeFieldNameSort.xlsx"); + includeFieldNameSort03 = TestFileUtil.createNewFile("includeFieldNameSort.xls"); + includeFieldNameSortCsv = TestFileUtil.createNewFile("includeFieldNameSort.csv"); } @Test @@ -197,7 +197,7 @@ public class ExcludeOrIncludeDataTest { includeColumnFieldNames.add("column3"); includeColumnFieldNames.add("column2"); EasyExcel.write(file, ExcludeOrIncludeData.class).includeColumnFieldNames(includeColumnFieldNames) - .forceIndex(true).sheet().doWrite(data()); + .sortByIncludeColumn(true).sheet().doWrite(data()); List> dataMap = EasyExcel.read(file).sheet().doReadSync(); Assert.assertEquals(1, dataMap.size()); Map record = dataMap.get(0); diff --git a/update.md b/update.md index 76f8aa1a..1c820ec8 100644 --- a/update.md +++ b/update.md @@ -5,7 +5,10 @@ * xlsx存在隐藏字符时需要忽略,确保和展示看到的一样 * 新增`commons-io` 2.11.0 包 * 在`easyexcel-parent` 包中移除测试包的`dependencyManagement` -* 删除`org.apache.poi.hssf.usermodel.PoiUtils.java`, 使用反射获取 [Issue #2804](https://github.com/alibaba/easyexcel/issues/2804) +* 删除`org.apache.poi.hssf.usermodel.PoiUtils.java`, + 使用反射获取 [Issue #2804](https://github.com/alibaba/easyexcel/issues/2804) +* 默认对象反射缓存改成`ThreadLocal`,并支持设置 [Issue #2792](https://github.com/alibaba/easyexcel/issues/2792) +* 支持根据`includeColumnIndexes`和`includeColumnFieldNames`排序 [Issue #2697](https://github.com/alibaba/easyexcel/issues/2697) # 3.2.1