Browse Source

* 默认对象反射缓存改成`ThreadLocal`,并支持设置 [Issue #2792](https://github.com/alibaba/easyexcel/issues/2792)

* 支持根据`includeColumnIndexes`和`includeColumnFieldNames`排序 [Issue #2697](https://github.com/alibaba/easyexcel/issues/2697)
pull/3168/head
Jiaju Zhuang 2 years ago
parent
commit
e9856e5549
  1. 2
      easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java
  2. 3
      easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java
  3. 23
      easyexcel-core/src/main/java/com/alibaba/excel/enums/CacheLocationEnum.java
  4. 9
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java
  5. 8
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/BasicParameter.java
  6. 37
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldCache.java
  7. 11
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java
  8. 34
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java
  9. 3
      easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java
  10. 5
      easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/property/ExcelReadHeadProperty.java
  11. 315
      easyexcel-core/src/main/java/com/alibaba/excel/util/ClassUtils.java
  12. 9
      easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java
  13. 29
      easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java
  14. 8
      easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java
  15. 7
      easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java
  16. 45
      easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java
  17. 29
      easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java
  18. 1
      easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java
  19. 5
      easyexcel-core/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java
  20. 14
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeDataTest.java
  21. 5
      update.md

2
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.xls.XlsReadWorkbookHolder;
import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder;
import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.util.DateUtils; import com.alibaba.excel.util.DateUtils;
import com.alibaba.excel.util.FileUtils; import com.alibaba.excel.util.FileUtils;
import com.alibaba.excel.util.NumberDataFormatterUtils; import com.alibaba.excel.util.NumberDataFormatterUtils;
@ -212,6 +213,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser {
private void removeThreadLocalCache() { private void removeThreadLocalCache() {
NumberDataFormatterUtils.removeThreadLocalCache(); NumberDataFormatterUtils.removeThreadLocalCache();
DateUtils.removeThreadLocalCache(); DateUtils.removeThreadLocalCache();
ClassUtils.removeThreadLocalCache();
} }
private void clearEncrypt03() { private void clearEncrypt03() {

3
easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java

@ -265,7 +265,7 @@ public class WriteContextImpl implements WriteContext {
Head head = entry.getValue(); Head head = entry.getValue();
int columnIndex = entry.getKey(); int columnIndex = entry.getKey();
ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(null, ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(null,
currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), head.getFieldName()); currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), head.getFieldName(), currentWriteHolder);
CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext(this, row, CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext(this, row,
rowIndex, head, columnIndex, relativeRowIndex, Boolean.TRUE, excelContentProperty); rowIndex, head, columnIndex, relativeRowIndex, Boolean.TRUE, excelContentProperty);
@ -427,6 +427,7 @@ public class WriteContextImpl implements WriteContext {
private void removeThreadLocalCache() { private void removeThreadLocalCache() {
NumberDataFormatterUtils.removeThreadLocalCache(); NumberDataFormatterUtils.removeThreadLocalCache();
DateUtils.removeThreadLocalCache(); DateUtils.removeThreadLocalCache();
ClassUtils.removeThreadLocalCache();
} }
@Override @Override

23
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;
}

9
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()); globalConfiguration.setLocale(basicParameter.getLocale());
} }
if (basicParameter.getFiledCacheLocation() == null) {
if (prentAbstractHolder != null) {
globalConfiguration.setFiledCacheLocation(
prentAbstractHolder.getGlobalConfiguration().getFiledCacheLocation());
}
} else {
globalConfiguration.setFiledCacheLocation(basicParameter.getFiledCacheLocation());
}
} }
@Override @Override

8
easyexcel-core/src/main/java/com/alibaba/excel/metadata/BasicParameter.java

@ -4,6 +4,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CacheLocationEnum;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
@ -54,4 +55,11 @@ public class BasicParameter {
* default is false * default is false
*/ */
private Boolean useScientificFormat; private Boolean useScientificFormat;
/**
* The cache used when parsing fields such as head.
*
* default is THREAD_LOCAL.
*/
private CacheLocationEnum filedCacheLocation;
} }

37
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<Integer, Field> sortedFieldMap;
/**
* Fields using the index attribute
*/
private Map<Integer, Field> indexFieldMap;
/**
* Fields to ignore
*/
private Map<String, Field> ignoreMap;
}

11
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 java.util.Locale;
import com.alibaba.excel.enums.CacheLocationEnum;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -32,6 +34,7 @@ public class GlobalConfiguration {
* used when formatting dates and numbers. * used when formatting dates and numbers.
*/ */
private Locale locale; private Locale locale;
/** /**
* Whether to use scientific Format. * Whether to use scientific Format.
* *
@ -39,10 +42,18 @@ public class GlobalConfiguration {
*/ */
private Boolean useScientificFormat; private Boolean useScientificFormat;
/**
* The cache used when parsing fields such as head.
*
* default is THREAD_LOCAL.
*/
private CacheLocationEnum filedCacheLocation;
public GlobalConfiguration() { public GlobalConfiguration() {
this.autoTrim = Boolean.TRUE; this.autoTrim = Boolean.TRUE;
this.use1904windowing = Boolean.FALSE; this.use1904windowing = Boolean.FALSE;
this.locale = Locale.getDefault(); this.locale = Locale.getDefault();
this.useScientificFormat = Boolean.FALSE; this.useScientificFormat = Boolean.FALSE;
this.filedCacheLocation = CacheLocationEnum.MEMORY;
} }
} }

34
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.annotation.ExcelProperty;
import com.alibaba.excel.enums.HeadKindEnum; 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.Head;
import com.alibaba.excel.metadata.Holder; import com.alibaba.excel.metadata.Holder;
import com.alibaba.excel.util.ClassUtils; import com.alibaba.excel.util.ClassUtils;
@ -51,22 +53,17 @@ public class ExcelHeadProperty {
* Configuration header information * Configuration header information
*/ */
private Map<Integer, Head> headMap; private Map<Integer, Head> headMap;
/**
* Fields ignored
*/
private Map<String, Field> ignoreMap;
public ExcelHeadProperty(Holder holder, Class<?> headClazz, List<List<String>> head) { public ExcelHeadProperty(ConfigurationHolder configurationHolder, Class<?> headClazz, List<List<String>> head) {
this.headClazz = headClazz; this.headClazz = headClazz;
headMap = new TreeMap<>(); headMap = new TreeMap<>();
ignoreMap = MapUtils.newHashMap();
headKind = HeadKindEnum.NONE; headKind = HeadKindEnum.NONE;
headRowNumber = 0; headRowNumber = 0;
if (head != null && !head.isEmpty()) { if (head != null && !head.isEmpty()) {
int headIndex = 0; int headIndex = 0;
for (int i = 0; i < head.size(); i++) { for (int i = 0; i < head.size(); i++) {
if (holder instanceof AbstractWriteHolder) { if (configurationHolder instanceof AbstractWriteHolder) {
if (((AbstractWriteHolder)holder).ignore(null, i)) { if (((AbstractWriteHolder)configurationHolder).ignore(null, i)) {
continue; continue;
} }
} }
@ -76,7 +73,7 @@ public class ExcelHeadProperty {
headKind = HeadKindEnum.STRING; headKind = HeadKindEnum.STRING;
} }
// convert headClazz to head // convert headClazz to head
initColumnProperties(holder); initColumnProperties(configurationHolder);
initHeadRowNumber(); initHeadRowNumber();
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
@ -104,24 +101,15 @@ public class ExcelHeadProperty {
} }
} }
private void initColumnProperties(Holder holder) { private void initColumnProperties(ConfigurationHolder configurationHolder) {
if (headClazz == null) { if (headClazz == null) {
return; return;
} }
// Declared fields FieldCache fieldCache = ClassUtils.declaredFields(headClazz, configurationHolder);
Map<Integer, Field> sortedAllFieldMap = MapUtils.newTreeMap();
Map<Integer, Field> 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);
for (Map.Entry<Integer, Field> entry : sortedAllFieldMap.entrySet()) { for (Map.Entry<Integer, Field> entry : fieldCache.getSortedFieldMap().entrySet()) {
initOneColumnProperty(entry.getKey(), entry.getValue(), indexFieldMap.containsKey(entry.getKey())); initOneColumnProperty(entry.getKey(), entry.getValue(),
fieldCache.getIndexFieldMap().containsKey(entry.getKey()));
} }
headKind = HeadKindEnum.CLASS; headKind = HeadKindEnum.CLASS;
} }

3
easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java

@ -149,7 +149,8 @@ public class ModelBuildEventListener implements IgnoreExceptionReadListener<Map<
ReadCellData<?> cellData = cellDataMap.get(index); ReadCellData<?> cellData = cellDataMap.get(index);
Object value = ConverterUtils.convertToJavaObject(cellData, head.getField(), Object value = ConverterUtils.convertToJavaObject(cellData, head.getField(),
ClassUtils.declaredExcelContentProperty(dataMap, readSheetHolder.excelReadHeadProperty().getHeadClazz(), 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) { if (value != null) {
dataMap.put(fieldName, value); dataMap.put(fieldName, value);
} }

5
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 java.util.List;
import com.alibaba.excel.metadata.ConfigurationHolder;
import com.alibaba.excel.metadata.Holder; import com.alibaba.excel.metadata.Holder;
import com.alibaba.excel.metadata.property.ExcelHeadProperty; import com.alibaba.excel.metadata.property.ExcelHeadProperty;
@ -12,7 +13,7 @@ import com.alibaba.excel.metadata.property.ExcelHeadProperty;
*/ */
public class ExcelReadHeadProperty extends ExcelHeadProperty { public class ExcelReadHeadProperty extends ExcelHeadProperty {
public ExcelReadHeadProperty(Holder holder, Class headClazz, List<List<String>> head) { public ExcelReadHeadProperty(ConfigurationHolder configurationHolder, Class headClazz, List<List<String>> head) {
super(holder, headClazz, head); super(configurationHolder, headClazz, head);
} }
} }

315
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.Optional;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; 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.annotation.write.style.ContentStyle;
import com.alibaba.excel.converters.AutoConverter; import com.alibaba.excel.converters.AutoConverter;
import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CacheLocationEnum;
import com.alibaba.excel.exception.ExcelCommonException; 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.Holder;
import com.alibaba.excel.metadata.property.DateTimeFormatProperty; import com.alibaba.excel.metadata.property.DateTimeFormatProperty;
import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty;
@ -59,18 +63,38 @@ import org.springframework.util.CollectionUtils;
*/ */
public class ClassUtils { public class ClassUtils {
public static final Map<Class<?>, FieldCache> FIELD_CACHE = new ConcurrentHashMap<>(); /**
* memory cache
*/
public static final ConcurrentHashMap<Class<?>, FieldCache> FIELD_CACHE = new ConcurrentHashMap<>();
/**
* thread local cahe
*/
private static final ThreadLocal<Map<Class<?>, FieldCache>> FIELD_THREAD_LOCAL = new ThreadLocal<>();
/**
* The cache configuration information for each of the class
*/
public static final ConcurrentHashMap<Class<?>, Map<String, ExcelContentProperty>> CLASS_CONTENT_CACHE
= new ConcurrentHashMap<>();
/** /**
* The cache configuration information for each of the class * The cache configuration information for each of the class
*/ */
public static final Map<Class<?>, Map<String, ExcelContentProperty>> CLASS_CONTENT_CACHE private static final ThreadLocal<Map<Class<?>, Map<String, ExcelContentProperty>>> CLASS_CONTENT_THREAD_LOCAL
= new ThreadLocal<>();
/**
* The cache configuration information for each of the class
*/
public static final ConcurrentHashMap<ContentPropertyKey, ExcelContentProperty> CONTENT_CACHE
= new ConcurrentHashMap<>(); = new ConcurrentHashMap<>();
/** /**
* The cache configuration information for each of the class * The cache configuration information for each of the class
*/ */
public static final Map<ContentPropertyKey, ExcelContentProperty> CONTENT_CACHE = new ConcurrentHashMap<>(); private static final ThreadLocal<Map<ContentPropertyKey, ExcelContentProperty>> CONTENT_THREAD_LOCAL
= new ThreadLocal<>();
/** /**
* Calculate the configuration information for the class * Calculate the configuration information for the class
@ -81,7 +105,7 @@ public class ClassUtils {
* @return * @return
*/ */
public static ExcelContentProperty declaredExcelContentProperty(Map<?, ?> dataMap, Class<?> headClazz, public static ExcelContentProperty declaredExcelContentProperty(Map<?, ?> dataMap, Class<?> headClazz,
String fieldName) { String fieldName, ConfigurationHolder configurationHolder) {
Class<?> clazz = null; Class<?> clazz = null;
if (dataMap instanceof BeanMap) { if (dataMap instanceof BeanMap) {
Object bean = ((BeanMap)dataMap).getBean(); Object bean = ((BeanMap)dataMap).getBean();
@ -89,15 +113,40 @@ public class ClassUtils {
clazz = bean.getClass(); clazz = bean.getClass();
} }
} }
return getExcelContentProperty(clazz, headClazz, fieldName); return getExcelContentProperty(clazz, headClazz, fieldName, configurationHolder);
} }
private static ExcelContentProperty getExcelContentProperty(Class<?> clazz, Class<?> headClass, String fieldName) { private static ExcelContentProperty getExcelContentProperty(Class<?> clazz, Class<?> headClass, String fieldName,
ConfigurationHolder configurationHolder) {
switch (configurationHolder.globalConfiguration().getFiledCacheLocation()) {
case THREAD_LOCAL:
Map<ContentPropertyKey, ExcelContentProperty> 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 CONTENT_CACHE.computeIfAbsent(buildKey(clazz, headClass, fieldName), key -> {
ExcelContentProperty excelContentProperty = Optional.ofNullable(declaredFieldContentMap(clazz)) 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)) .map(map -> map.get(fieldName))
.orElse(null); .orElse(null);
ExcelContentProperty headExcelContentProperty = Optional.ofNullable(declaredFieldContentMap(headClass)) ExcelContentProperty headExcelContentProperty = Optional.ofNullable(
declaredFieldContentMap(headClass, configurationHolder))
.map(map -> map.get(fieldName)) .map(map -> map.get(fieldName))
.orElse(null); .orElse(null);
ExcelContentProperty combineExcelContentProperty = new ExcelContentProperty(); ExcelContentProperty combineExcelContentProperty = new ExcelContentProperty();
@ -107,8 +156,6 @@ public class ClassUtils {
combineExcelContentProperty(combineExcelContentProperty, excelContentProperty); combineExcelContentProperty(combineExcelContentProperty, excelContentProperty);
} }
return combineExcelContentProperty; return combineExcelContentProperty;
});
} }
public static void combineExcelContentProperty(ExcelContentProperty combineExcelContentProperty, public static void combineExcelContentProperty(ExcelContentProperty combineExcelContentProperty,
@ -140,11 +187,38 @@ public class ClassUtils {
return new ContentPropertyKey(clazz, headClass, fieldName); return new ContentPropertyKey(clazz, headClass, fieldName);
} }
private static Map<String, ExcelContentProperty> declaredFieldContentMap(Class<?> clazz) { private static Map<String, ExcelContentProperty> declaredFieldContentMap(Class<?> clazz,
ConfigurationHolder configurationHolder) {
if (clazz == null) { if (clazz == null) {
return null; return null;
} }
switch (configurationHolder.globalConfiguration().getFiledCacheLocation()) {
case THREAD_LOCAL:
Map<Class<?>, Map<String, ExcelContentProperty>> 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 CLASS_CONTENT_CACHE.computeIfAbsent(clazz, key -> {
return doDeclaredFieldContentMap(clazz);
});
case NONE:
return doDeclaredFieldContentMap(clazz);
default:
throw new UnsupportedOperationException("unsupported enum");
}
}
private static Map<String, ExcelContentProperty> doDeclaredFieldContentMap(Class<?> clazz) {
if (clazz == null) {
return null;
}
List<Field> tempFieldList = new ArrayList<>(); List<Field> tempFieldList = new ArrayList<>();
Class<?> tempClass = clazz; Class<?> tempClass = clazz;
while (tempClass != null) { while (tempClass != null) {
@ -195,127 +269,166 @@ public class ClassUtils {
fieldContentMap.put(field.getName(), excelContentProperty); fieldContentMap.put(field.getName(), excelContentProperty);
} }
return fieldContentMap; return fieldContentMap;
});
} }
/** /**
* Parsing field in the class * Parsing field in the class
* *
* @param clazz Need to parse 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 needIgnore If you want to ignore fields need to ignore
* @param holder holder * @param holder holder
* @param cacheLocation cache lcation
*/ */
public static void declaredFields(Class<?> clazz, Map<Integer, Field> sortedAllFieldMap, public static FieldCache declaredFields(Class<?> clazz, ConfigurationHolder configurationHolder) {
Map<Integer, Field> indexFieldMap, Map<String, Field> ignoreMap, Boolean needIgnore, Holder holder) { switch (configurationHolder.globalConfiguration().getFiledCacheLocation()) {
FieldCache fieldCache = declaredFields(clazz); case THREAD_LOCAL:
if (fieldCache == null) { Map<Class<?>, FieldCache> fieldCacheMap = FIELD_THREAD_LOCAL.get();
return; 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());
} }
Map<Integer, Field> tempIndexFieldMap = indexFieldMap;
if (tempIndexFieldMap == null) { private static FieldCache doDeclaredFields(Class<?> clazz, ConfigurationHolder configurationHolder) {
tempIndexFieldMap = MapUtils.newTreeMap(); List<Field> 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();
} }
tempIndexFieldMap.putAll(fieldCache.getIndexFieldMap()); // Screening of field
Map<Integer, List<Field>> orderFieldMap = new TreeMap<Integer, List<Field>>();
Map<Integer, Field> indexFieldMap = new TreeMap<Integer, Field>();
Map<String, Field> ignoreMap = new HashMap<String, Field>(16);
Map<Integer, Field> originSortedAllFieldMap = fieldCache.getSortedAllFieldMap(); ExcelIgnoreUnannotated excelIgnoreUnannotated = clazz.getAnnotation(ExcelIgnoreUnannotated.class);
if (!needIgnore) { for (Field field : tempFieldList) {
sortedAllFieldMap.putAll(originSortedAllFieldMap); declaredOneField(field, orderFieldMap, indexFieldMap, ignoreMap, excelIgnoreUnannotated);
return; }
Map<Integer, Field> sortedFieldMap = buildSortedAllFieldMap(orderFieldMap, indexFieldMap);
FieldCache fieldCache = new FieldCache(sortedFieldMap, indexFieldMap, ignoreMap);
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<Integer, Field> tempSortedFieldMapp = MapUtils.newHashMap();
int index = 0; int index = 0;
for (Map.Entry<Integer, Field> entry : originSortedAllFieldMap.entrySet()) { for (Map.Entry<Integer, Field> entry : sortedFieldMap.entrySet()) {
Integer key = entry.getKey(); Integer key = entry.getKey();
Field field = entry.getValue(); Field field = entry.getValue();
// The current field needs to be ignored // 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) { if (ignoreMap != null) {
ignoreMap.put(field.getName(), field); ignoreMap.put(field.getName(), field);
} }
tempIndexFieldMap.remove(index); indexFieldMap.remove(index);
} else { } else {
// Mandatory sorted fields // Mandatory sorted fields
if (tempIndexFieldMap.containsKey(key)) { if (indexFieldMap.containsKey(key)) {
sortedAllFieldMap.put(key, field); tempSortedFieldMapp.put(key, field);
} else { } else {
// Need to reorder automatically // Need to reorder automatically
// Check whether the current key is already in use // Check whether the current key is already in use
while (sortedAllFieldMap.containsKey(index)) { while (tempSortedFieldMapp.containsKey(index)) {
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 * it only works when {@link WriteHolder#getIncludeColumnFieldNames()} or
* and {@link AbstractWriteHolder#getForceIndex()} is true * {@link WriteHolder#getIncludeColumnIndexes()} ()} has value
* and {@link WriteHolder#getSortByIncludeColumn()} ()} is true
**/ **/
private static void forceIndexIfNecessary(Holder holder, Map<Integer, Field> sortedAllFieldMap) { private static void resortField(WriteHolder writeHolder, FieldCache fieldCache) {
if (!(holder instanceof AbstractWriteHolder)) { if (!writeHolder.sortByIncludeColumn()) {
return; return;
} }
AbstractWriteHolder writeHolder = (AbstractWriteHolder)holder; Map<Integer, Field> indexFieldMap = fieldCache.getIndexFieldMap();
Collection<String> allCol = writeHolder.getIncludeColumnFieldNames();
if (!CollectionUtils.isEmpty(allCol) && writeHolder.getForceIndex() != null && writeHolder.getForceIndex()) {
Map<String, Integer> colIndexMap = MapUtils.newHashMap();
Iterator<String> iterator = allCol.iterator();
int colIndex = 0;
while (iterator.hasNext()) {
String col = iterator.next();
colIndexMap.put(col, colIndex);
colIndex++;
}
Map<Integer, Field> temp = MapUtils.newHashMap();
sortedAllFieldMap.forEach((index, field) -> {
Integer fieldIndex = colIndexMap.get(field.getName());
temp.put(fieldIndex, field);
});
sortedAllFieldMap.clear();
sortedAllFieldMap.putAll(temp);
}
}
public static void declaredFields(Class<?> clazz, Map<Integer, Field> sortedAllFieldMap, Boolean needIgnore, Collection<String> includeColumnFieldNames = writeHolder.includeColumnFieldNames();
WriteHolder writeHolder) { if (!CollectionUtils.isEmpty(includeColumnFieldNames)) {
declaredFields(clazz, sortedAllFieldMap, null, null, needIgnore, writeHolder); // Field sorted map
Map<String, Integer> filedIndexMap = MapUtils.newHashMap();
int fieldIndex = 0;
for (String includeColumnFieldName : includeColumnFieldNames) {
filedIndexMap.put(includeColumnFieldName, fieldIndex++);
} }
private static FieldCache declaredFields(Class<?> clazz) { // rebuild sortedFieldMap
if (clazz == null) { Map<Integer, Field> tempSortedFieldMap = MapUtils.newHashMap();
return null; 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);
} }
return FIELD_CACHE.computeIfAbsent(clazz, key -> {
List<Field> 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<Integer, List<Field>> orderFieldMap = new TreeMap<Integer, List<Field>>(); fieldCache.setSortedFieldMap(tempSortedFieldMap);
Map<Integer, Field> indexFieldMap = new TreeMap<Integer, Field>(); return;
Map<String, Field> ignoreMap = new HashMap<String, Field>(16); }
ExcelIgnoreUnannotated excelIgnoreUnannotated = clazz.getAnnotation(ExcelIgnoreUnannotated.class); Collection<Integer> includeColumnIndexes = writeHolder.includeColumnIndexes();
for (Field field : tempFieldList) { if (!CollectionUtils.isEmpty(includeColumnFieldNames)) {
declaredOneField(field, orderFieldMap, indexFieldMap, ignoreMap, excelIgnoreUnannotated); // Index sorted map
Map<Integer, Integer> filedIndexMap = MapUtils.newHashMap();
int fieldIndex = 0;
for (Integer includeColumnIndexe : includeColumnIndexes) {
filedIndexMap.put(includeColumnIndexe, fieldIndex++);
}
// rebuild sortedFieldMap
Map<Integer, Field> 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);
} }
return new FieldCache(buildSortedAllFieldMap(orderFieldMap, indexFieldMap), indexFieldMap, ignoreMap);
}); });
fieldCache.setSortedFieldMap(tempSortedFieldMap);
}
} }
private static Map<Integer, Field> buildSortedAllFieldMap(Map<Integer, List<Field>> orderFieldMap, private static Map<Integer, Field> buildSortedAllFieldMap(Map<Integer, List<Field>> orderFieldMap,
@ -380,32 +493,6 @@ public class ClassUtils {
orderFieldList.add(field); orderFieldList.add(field);
} }
private static class FieldCache {
private final Map<Integer, Field> sortedAllFieldMap;
private final Map<Integer, Field> indexFieldMap;
private final Map<String, Field> ignoreMap;
public FieldCache(Map<Integer, Field> sortedAllFieldMap, Map<Integer, Field> indexFieldMap,
Map<String, Field> ignoreMap) {
this.sortedAllFieldMap = sortedAllFieldMap;
this.indexFieldMap = indexFieldMap;
this.ignoreMap = ignoreMap;
}
public Map<Integer, Field> getSortedAllFieldMap() {
return sortedAllFieldMap;
}
public Map<Integer, Field> getIndexFieldMap() {
return indexFieldMap;
}
public Map<String, Field> getIgnoreMap() {
return ignoreMap;
}
}
/** /**
* <p>Gets a {@code List} of all interfaces implemented by the given * <p>Gets a {@code List} of all interfaces implemented by the given
* class and its superclasses.</p> * class and its superclasses.</p>
@ -459,4 +546,10 @@ public class ClassUtils {
private Class<?> headClass; private Class<?> headClass;
private String fieldName; private String fieldName;
} }
public static void removeThreadLocalCache() {
FIELD_THREAD_LOCAL.remove();
CLASS_CONTENT_THREAD_LOCAL.remove();
CONTENT_THREAD_LOCAL.remove();
}
} }

9
easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java

@ -122,11 +122,12 @@ public abstract class AbstractExcelWriterParameterBuilder<T extends AbstractExce
} }
/** /**
* head index use {@link this#includeColumnFiledNames} sort * Data will be sorted according to {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}.
* useless if not set {@link this#includeColumnFiledNames} *
* default is false.
**/ **/
public T forceIndex(Boolean force) { public T sortByIncludeColumn(Boolean sortByIncludeColumn) {
parameter().setForceIndex(force); parameter().setSortByIncludeColumn(sortByIncludeColumn);
return self(); return self();
} }
} }

29
easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java

@ -10,6 +10,7 @@ import java.util.TreeMap;
import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.enums.HeadKindEnum;
import com.alibaba.excel.metadata.FieldCache;
import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.BeanMapUtils; import com.alibaba.excel.util.BeanMapUtils;
@ -50,18 +51,15 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) { if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) {
newRowIndex += writeContext.currentWriteHolder().relativeHeadRowIndex(); newRowIndex += writeContext.currentWriteHolder().relativeHeadRowIndex();
} }
// BeanMap is out of order, so use sortedAllFieldMap
Map<Integer, Field> sortedAllFieldMap = new TreeMap<>();
int relativeRowIndex = 0; int relativeRowIndex = 0;
for (Object oneRowData : data) { for (Object oneRowData : data) {
int lastRowIndex = relativeRowIndex + newRowIndex; int lastRowIndex = relativeRowIndex + newRowIndex;
addOneRowOfDataToExcel(oneRowData, lastRowIndex, relativeRowIndex, sortedAllFieldMap); addOneRowOfDataToExcel(oneRowData, lastRowIndex, relativeRowIndex);
relativeRowIndex++; relativeRowIndex++;
} }
} }
private void addOneRowOfDataToExcel(Object oneRowData, int rowIndex, int relativeRowIndex, private void addOneRowOfDataToExcel(Object oneRowData, int rowIndex, int relativeRowIndex) {
Map<Integer, Field> sortedAllFieldMap) {
if (oneRowData == null) { if (oneRowData == null) {
return; return;
} }
@ -79,7 +77,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
} else if (oneRowData instanceof Map) { } else if (oneRowData instanceof Map) {
addBasicTypeToExcel(new MapRowData((Map<Integer, ?>)oneRowData), row, rowIndex, relativeRowIndex); addBasicTypeToExcel(new MapRowData((Map<Integer, ?>)oneRowData), row, rowIndex, relativeRowIndex);
} else { } else {
addJavaObjectToExcel(oneRowData, row, rowIndex, relativeRowIndex, sortedAllFieldMap); addJavaObjectToExcel(oneRowData, row, rowIndex, relativeRowIndex);
} }
WriteHandlerUtils.afterRowDispose(rowWriteHandlerContext); WriteHandlerUtils.afterRowDispose(rowWriteHandlerContext);
@ -119,7 +117,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
int dataIndex, int columnIndex) { int dataIndex, int columnIndex) {
ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(null, ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(null,
writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(),
head == null ? null : head.getFieldName()); head == null ? null : head.getFieldName(), writeContext.currentWriteHolder());
CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext(writeContext, CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext(writeContext,
row, rowIndex, head, columnIndex, relativeRowIndex, Boolean.FALSE, excelContentProperty); row, rowIndex, head, columnIndex, relativeRowIndex, Boolean.FALSE, excelContentProperty);
@ -138,8 +136,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext); WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext);
} }
private void addJavaObjectToExcel(Object oneRowData, Row row, int rowIndex, int relativeRowIndex, private void addJavaObjectToExcel(Object oneRowData, Row row, int rowIndex, int relativeRowIndex) {
Map<Integer, Field> sortedAllFieldMap) {
WriteHolder currentWriteHolder = writeContext.currentWriteHolder(); WriteHolder currentWriteHolder = writeContext.currentWriteHolder();
BeanMap beanMap = BeanMapUtils.create(oneRowData); BeanMap beanMap = BeanMapUtils.create(oneRowData);
// Bean the contains of the Map Key method with poor performance,So to create a keySet here // 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, ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(beanMap,
currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), name); currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), name, currentWriteHolder);
CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext( CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext(
writeContext, row, rowIndex, head, columnIndex, relativeRowIndex, Boolean.FALSE, writeContext, row, rowIndex, head, columnIndex, relativeRowIndex, Boolean.FALSE,
excelContentProperty); excelContentProperty);
@ -185,19 +182,17 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
} }
maxCellIndex++; maxCellIndex++;
Map<String, Field> ignoreMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getIgnoreMap(); FieldCache fieldCache = ClassUtils.declaredFields(oneRowData.getClass(), writeContext.currentWriteHolder());
initSortedAllFieldMapFieldList(oneRowData.getClass(), sortedAllFieldMap); for (Map.Entry<Integer, Field> entry : fieldCache.getSortedFieldMap().entrySet()) {
for (Map.Entry<Integer, Field> entry : sortedAllFieldMap.entrySet()) {
Field field = entry.getValue(); Field field = entry.getValue();
String fieldName = FieldUtils.resolveCglibFieldName(field); String fieldName = FieldUtils.resolveCglibFieldName(field);
boolean uselessData = !beanKeySet.contains(fieldName) || beanMapHandledSet.contains(fieldName) boolean uselessData = !beanKeySet.contains(fieldName) || beanMapHandledSet.contains(fieldName);
|| ignoreMap.containsKey(fieldName);
if (uselessData) { if (uselessData) {
continue; continue;
} }
Object value = beanMap.get(fieldName); Object value = beanMap.get(fieldName);
ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(beanMap, ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(beanMap,
currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), fieldName); currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), fieldName, currentWriteHolder);
CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext( CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext(
writeContext, row, rowIndex, null, maxCellIndex, relativeRowIndex, Boolean.FALSE, excelContentProperty); writeContext, row, rowIndex, null, maxCellIndex, relativeRowIndex, Boolean.FALSE, excelContentProperty);
WriteHandlerUtils.beforeCellCreate(cellWriteHandlerContext); WriteHandlerUtils.beforeCellCreate(cellWriteHandlerContext);
@ -229,7 +224,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
.isEmpty(writeSheetHolder.getExcludeColumnIndexes()) || !CollectionUtils .isEmpty(writeSheetHolder.getExcludeColumnIndexes()) || !CollectionUtils
.isEmpty(writeSheetHolder.getIncludeColumnFieldNames()) || !CollectionUtils .isEmpty(writeSheetHolder.getIncludeColumnFieldNames()) || !CollectionUtils
.isEmpty(writeSheetHolder.getIncludeColumnIndexes()); .isEmpty(writeSheetHolder.getIncludeColumnIndexes());
ClassUtils.declaredFields(clazz, sortedAllFieldMap, needIgnore, writeSheetHolder); ClassUtils.declaredFields(clazz, writeSheetHolder);
} }
} }

8
easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java

@ -37,7 +37,9 @@ import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import com.alibaba.excel.util.PoiUtils; import com.alibaba.excel.util.PoiUtils;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.CellType;
@ -215,7 +217,8 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
} }
Object value = dataMap.get(variable); Object value = dataMap.get(variable);
ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(dataMap, ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(dataMap,
writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), variable); writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), variable,
writeContext.currentWriteHolder());
cellWriteHandlerContext.setExcelContentProperty(excelContentProperty); cellWriteHandlerContext.setExcelContentProperty(excelContentProperty);
createCell(analysisCell, fillConfig, cellWriteHandlerContext, rowWriteHandlerContext); createCell(analysisCell, fillConfig, cellWriteHandlerContext, rowWriteHandlerContext);
@ -249,7 +252,8 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
} }
Object value = dataMap.get(variable); Object value = dataMap.get(variable);
ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(dataMap, ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(dataMap,
writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), variable); writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), variable,
writeContext.currentWriteHolder());
cellWriteHandlerContext.setOriginalValue(value); cellWriteHandlerContext.setOriginalValue(value);
cellWriteHandlerContext.setOriginalFieldClass(FieldUtils.getFieldClass(dataMap, variable, value)); cellWriteHandlerContext.setOriginalFieldClass(FieldUtils.getFieldClass(dataMap, variable, value));
cellWriteHandlerContext.setExcelContentProperty(excelContentProperty); cellWriteHandlerContext.setExcelContentProperty(excelContentProperty);

7
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. * Only output the custom columns.
*/ */
private Collection<String> includeColumnFieldNames; private Collection<String> 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;
} }

45
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.ConverterKeyBuild;
import com.alibaba.excel.converters.DefaultConverterLoader; import com.alibaba.excel.converters.DefaultConverterLoader;
import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.enums.HeadKindEnum;
import com.alibaba.excel.enums.HolderEnum;
import com.alibaba.excel.event.NotRepeatExecutor; import com.alibaba.excel.event.NotRepeatExecutor;
import com.alibaba.excel.metadata.AbstractHolder; import com.alibaba.excel.metadata.AbstractHolder;
import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Head;
@ -94,10 +95,14 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
* Only output the custom columns. * Only output the custom columns.
*/ */
private Collection<String> includeColumnFieldNames; private Collection<String> 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 * Write handler
*/ */
@ -197,11 +202,17 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
} else { } else {
this.includeColumnFieldNames = writeBasicParameter.getIncludeColumnFieldNames(); 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 { } else {
this.forceIndex = writeBasicParameter.getForceIndex(); this.sortByIncludeColumn = parentAbstractWriteHolder.getSortByIncludeColumn();
} }
} else {
this.sortByIncludeColumn = writeBasicParameter.getSortByIncludeColumn();
}
if (writeBasicParameter.getIncludeColumnIndexes() == null && parentAbstractWriteHolder != null) { if (writeBasicParameter.getIncludeColumnIndexes() == null && parentAbstractWriteHolder != null) {
this.includeColumnIndexes = parentAbstractWriteHolder.getIncludeColumnIndexes(); this.includeColumnIndexes = parentAbstractWriteHolder.getIncludeColumnIndexes();
} else { } else {
@ -488,4 +499,28 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
return getAutomaticMergeHead(); return getAutomaticMergeHead();
} }
@Override
public boolean sortByIncludeColumn() {
return getSortByIncludeColumn();
}
@Override
public Collection<Integer> includeColumnIndexes() {
return getIncludeColumnIndexes();
}
@Override
public Collection<String> includeColumnFieldNames() {
return getIncludeColumnFieldNames();
}
@Override
public Collection<Integer> excludeColumnIndexes() {
return getExcludeColumnIndexes();
}
@Override
public Collection<String> excludeColumnFieldNames() {
return getExcludeColumnFieldNames();
}
} }

29
easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java

@ -1,10 +1,11 @@
package com.alibaba.excel.write.metadata.holder; package com.alibaba.excel.write.metadata.holder;
import java.util.Collection;
import com.alibaba.excel.metadata.ConfigurationHolder; import com.alibaba.excel.metadata.ConfigurationHolder;
import com.alibaba.excel.write.property.ExcelWriteHeadProperty; import com.alibaba.excel.write.property.ExcelWriteHeadProperty;
/** /**
*
* Get the corresponding Holder * Get the corresponding Holder
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
@ -46,4 +47,30 @@ public interface WriteHolder extends ConfigurationHolder {
* @return * @return
*/ */
int relativeHeadRowIndex(); int relativeHeadRowIndex();
/**
* Data will be sorted according to {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}.
*
* default is false.
*/
boolean sortByIncludeColumn();
/**
* Only output the custom columns.
*/
Collection<Integer> includeColumnIndexes();
/**
* Only output the custom columns.
*/
Collection<String> includeColumnFieldNames();
/**
* Ignore the custom columns.
*/
Collection<Integer> excludeColumnIndexes();
/**
* Ignore the custom columns.
*/
Collection<String> excludeColumnFieldNames();
} }

1
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.HashMap;
import java.util.Map; import java.util.Map;
import com.alibaba.excel.enums.CacheLocationEnum;
import com.alibaba.excel.enums.HolderEnum; import com.alibaba.excel.enums.HolderEnum;
import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.metadata.data.DataFormatData; import com.alibaba.excel.metadata.data.DataFormatData;

5
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.annotation.write.style.OnceAbsoluteMerge;
import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.enums.HeadKindEnum;
import com.alibaba.excel.metadata.CellRange; import com.alibaba.excel.metadata.CellRange;
import com.alibaba.excel.metadata.ConfigurationHolder;
import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.Holder; import com.alibaba.excel.metadata.Holder;
import com.alibaba.excel.metadata.property.ColumnWidthProperty; import com.alibaba.excel.metadata.property.ColumnWidthProperty;
@ -44,8 +45,8 @@ public class ExcelWriteHeadProperty extends ExcelHeadProperty {
private RowHeightProperty contentRowHeightProperty; private RowHeightProperty contentRowHeightProperty;
private OnceAbsoluteMergeProperty onceAbsoluteMergeProperty; private OnceAbsoluteMergeProperty onceAbsoluteMergeProperty;
public ExcelWriteHeadProperty(Holder holder, Class<?> headClazz, List<List<String>> head) { public ExcelWriteHeadProperty(ConfigurationHolder configurationHolder, Class<?> headClazz, List<List<String>> head) {
super(holder, headClazz, head); super(configurationHolder, headClazz, head);
if (getHeadKind() != HeadKindEnum.CLASS) { if (getHeadKind() != HeadKindEnum.CLASS) {
return; return;
} }

14
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 includeFieldName07;
private static File includeFieldName03; private static File includeFieldName03;
private static File includeFieldNameCsv; private static File includeFieldNameCsv;
private static File includeFieldNameForceIndex07; private static File includeFieldNameSort07;
private static File includeFieldNameForceIndex03; private static File includeFieldNameSort03;
private static File includeFieldNameForceIndexCsv; private static File includeFieldNameSortCsv;
@BeforeClass @BeforeClass
public static void init() { public static void init() {
@ -52,9 +52,9 @@ public class ExcludeOrIncludeDataTest {
includeFieldName07 = TestFileUtil.createNewFile("includeFieldName.xlsx"); includeFieldName07 = TestFileUtil.createNewFile("includeFieldName.xlsx");
includeFieldName03 = TestFileUtil.createNewFile("includeFieldName.xls"); includeFieldName03 = TestFileUtil.createNewFile("includeFieldName.xls");
includeFieldNameCsv = TestFileUtil.createNewFile("includeFieldName.csv"); includeFieldNameCsv = TestFileUtil.createNewFile("includeFieldName.csv");
includeFieldNameForceIndex07 = TestFileUtil.createNewFile("includeFieldNameForceIndex.xlsx"); includeFieldNameSort07 = TestFileUtil.createNewFile("includeFieldNameSort.xlsx");
includeFieldNameForceIndex03 = TestFileUtil.createNewFile("includeFieldNameForceIndex.xls"); includeFieldNameSort03 = TestFileUtil.createNewFile("includeFieldNameSort.xls");
includeFieldNameForceIndexCsv = TestFileUtil.createNewFile("includeFieldNameForceIndex.csv"); includeFieldNameSortCsv = TestFileUtil.createNewFile("includeFieldNameSort.csv");
} }
@Test @Test
@ -197,7 +197,7 @@ public class ExcludeOrIncludeDataTest {
includeColumnFieldNames.add("column3"); includeColumnFieldNames.add("column3");
includeColumnFieldNames.add("column2"); includeColumnFieldNames.add("column2");
EasyExcel.write(file, ExcludeOrIncludeData.class).includeColumnFieldNames(includeColumnFieldNames) EasyExcel.write(file, ExcludeOrIncludeData.class).includeColumnFieldNames(includeColumnFieldNames)
.forceIndex(true).sheet().doWrite(data()); .sortByIncludeColumn(true).sheet().doWrite(data());
List<Map<Integer, String>> dataMap = EasyExcel.read(file).sheet().doReadSync(); List<Map<Integer, String>> dataMap = EasyExcel.read(file).sheet().doReadSync();
Assert.assertEquals(1, dataMap.size()); Assert.assertEquals(1, dataMap.size());
Map<Integer, String> record = dataMap.get(0); Map<Integer, String> record = dataMap.get(0);

5
update.md

@ -5,7 +5,10 @@
* xlsx存在隐藏字符时需要忽略,确保和展示看到的一样 * xlsx存在隐藏字符时需要忽略,确保和展示看到的一样
* 新增`commons-io` 2.11.0 包 * 新增`commons-io` 2.11.0 包
* 在`easyexcel-parent` 包中移除测试包的`dependencyManagement` * 在`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 # 3.2.1

Loading…
Cancel
Save