Browse Source

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

pull/3168/head
Jiaju Zhuang 2 years ago
parent
commit
85d4264415
  1. 11
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractParameterBuilder.java
  2. 10
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldCache.java
  3. 43
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldWrapper.java
  4. 2
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java
  5. 19
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java
  6. 93
      easyexcel-core/src/main/java/com/alibaba/excel/util/ClassUtils.java
  7. 6
      easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java
  8. 21
      easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java
  9. 4
      easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java
  10. 17
      easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java
  11. 4
      easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java
  12. 21
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheData.java
  13. 219
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheDataTest.java
  14. 21
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheInvokeData.java
  15. 21
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheInvokeMemoryData.java
  16. 84
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeDataTest.java
  17. 5
      update.md

11
easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractParameterBuilder.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 com.alibaba.excel.util.ListUtils; import com.alibaba.excel.util.ListUtils;
/** /**
@ -73,6 +74,16 @@ public abstract class AbstractParameterBuilder<T extends AbstractParameterBuilde
return self(); return self();
} }
/**
* The cache used when parsing fields such as head.
*
* default is THREAD_LOCAL.
*/
public T filedCacheLocation(CacheLocationEnum filedCacheLocation) {
parameter().setFiledCacheLocation(filedCacheLocation);
return self();
}
/** /**
* Automatic trim includes sheet name and content * Automatic trim includes sheet name and content
* *

10
easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldCache.java

@ -2,6 +2,7 @@ package com.alibaba.excel.metadata;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Map; import java.util.Map;
import java.util.Set;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -23,15 +24,10 @@ public class FieldCache {
* A field cache that has been sorted by a class. * A field cache that has been sorted by a class.
* It will exclude fields that are not needed. * It will exclude fields that are not needed.
*/ */
private Map<Integer, Field> sortedFieldMap; private Map<Integer, FieldWrapper> sortedFieldMap;
/** /**
* Fields using the index attribute * Fields using the index attribute
*/ */
private Map<Integer, Field> indexFieldMap; private Map<Integer, FieldWrapper> indexFieldMap;
/**
* Fields to ignore
*/
private Map<String, Field> ignoreMap;
} }

43
easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldWrapper.java

@ -0,0 +1,43 @@
package com.alibaba.excel.metadata;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* filed wrapper
*
* @author Jiaju Zhuang
*/
@Getter
@Setter
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class FieldWrapper {
/**
* field
*/
private Field field;
/**
* The field name matching cglib
*/
private String fieldName;
/**
* The name of the sheet header.
*
* @see ExcelProperty
*/
private String[] heads;
}

2
easyexcel-core/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java

@ -54,6 +54,6 @@ public class GlobalConfiguration {
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; this.filedCacheLocation = CacheLocationEnum.THREAD_LOCAL;
} }
} }

19
easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java

@ -11,6 +11,7 @@ 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.ConfigurationHolder;
import com.alibaba.excel.metadata.FieldCache; import com.alibaba.excel.metadata.FieldCache;
import com.alibaba.excel.metadata.FieldWrapper;
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;
@ -107,7 +108,7 @@ public class ExcelHeadProperty {
} }
FieldCache fieldCache = ClassUtils.declaredFields(headClazz, configurationHolder); FieldCache fieldCache = ClassUtils.declaredFields(headClazz, configurationHolder);
for (Map.Entry<Integer, Field> entry : fieldCache.getSortedFieldMap().entrySet()) { for (Map.Entry<Integer, FieldWrapper> entry : fieldCache.getSortedFieldMap().entrySet()) {
initOneColumnProperty(entry.getKey(), entry.getValue(), initOneColumnProperty(entry.getKey(), entry.getValue(),
fieldCache.getIndexFieldMap().containsKey(entry.getKey())); fieldCache.getIndexFieldMap().containsKey(entry.getKey()));
} }
@ -122,22 +123,20 @@ public class ExcelHeadProperty {
* @param forceIndex * @param forceIndex
* @return Ignore current field * @return Ignore current field
*/ */
private void initOneColumnProperty(int index, Field field, Boolean forceIndex) { private void initOneColumnProperty(int index, FieldWrapper field, Boolean forceIndex) {
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); List<String> tmpHeadList = new ArrayList<>();
List<String> tmpHeadList = new ArrayList<String>(); boolean notForceName = field.getHeads() == null || field.getHeads().length == 0
String fieldName = FieldUtils.resolveCglibFieldName(field); || (field.getHeads().length == 1 && StringUtils.isEmpty(field.getHeads()[0]));
boolean notForceName = excelProperty == null || excelProperty.value().length <= 0
|| (excelProperty.value().length == 1 && StringUtils.isEmpty((excelProperty.value())[0]));
if (headMap.containsKey(index)) { if (headMap.containsKey(index)) {
tmpHeadList.addAll(headMap.get(index).getHeadNameList()); tmpHeadList.addAll(headMap.get(index).getHeadNameList());
} else { } else {
if (notForceName) { if (notForceName) {
tmpHeadList.add(fieldName); tmpHeadList.add(field.getFieldName());
} else { } else {
Collections.addAll(tmpHeadList, excelProperty.value()); Collections.addAll(tmpHeadList, field.getHeads());
} }
} }
Head head = new Head(index, field, fieldName, tmpHeadList, forceIndex, !notForceName); Head head = new Head(index, field.getField(), field.getFieldName(), tmpHeadList, forceIndex, !notForceName);
headMap.put(index, head); headMap.put(index, head);
} }

93
easyexcel-core/src/main/java/com/alibaba/excel/util/ClassUtils.java

@ -1,5 +1,6 @@
package com.alibaba.excel.util; package com.alibaba.excel.util;
import java.io.FileWriter;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
@ -7,14 +8,14 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet;
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;
@ -25,17 +26,16 @@ 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.ConfigurationHolder;
import com.alibaba.excel.metadata.FieldCache; import com.alibaba.excel.metadata.FieldCache;
import com.alibaba.excel.metadata.Holder; import com.alibaba.excel.metadata.FieldWrapper;
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;
import com.alibaba.excel.metadata.property.FontProperty; import com.alibaba.excel.metadata.property.FontProperty;
import com.alibaba.excel.metadata.property.NumberFormatProperty; import com.alibaba.excel.metadata.property.NumberFormatProperty;
import com.alibaba.excel.metadata.property.StyleProperty; import com.alibaba.excel.metadata.property.StyleProperty;
import com.alibaba.excel.write.metadata.holder.AbstractWriteHolder; import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.excel.write.metadata.holder.WriteHolder; import com.alibaba.excel.write.metadata.holder.WriteHolder;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -312,16 +312,16 @@ public class ClassUtils {
tempClass = tempClass.getSuperclass(); tempClass = tempClass.getSuperclass();
} }
// Screening of field // Screening of field
Map<Integer, List<Field>> orderFieldMap = new TreeMap<Integer, List<Field>>(); Map<Integer, List<FieldWrapper>> orderFieldMap = new TreeMap<>();
Map<Integer, Field> indexFieldMap = new TreeMap<Integer, Field>(); Map<Integer, FieldWrapper> indexFieldMap = new TreeMap<>();
Map<String, Field> ignoreMap = new HashMap<String, Field>(16); Set<String> ignoreSet = new HashSet<>();
ExcelIgnoreUnannotated excelIgnoreUnannotated = clazz.getAnnotation(ExcelIgnoreUnannotated.class); ExcelIgnoreUnannotated excelIgnoreUnannotated = clazz.getAnnotation(ExcelIgnoreUnannotated.class);
for (Field field : tempFieldList) { for (Field field : tempFieldList) {
declaredOneField(field, orderFieldMap, indexFieldMap, ignoreMap, excelIgnoreUnannotated); declaredOneField(field, orderFieldMap, indexFieldMap, ignoreSet, excelIgnoreUnannotated);
} }
Map<Integer, Field> sortedFieldMap = buildSortedAllFieldMap(orderFieldMap, indexFieldMap); Map<Integer, FieldWrapper> sortedFieldMap = buildSortedAllFieldMap(orderFieldMap, indexFieldMap);
FieldCache fieldCache = new FieldCache(sortedFieldMap, indexFieldMap, ignoreMap); FieldCache fieldCache = new FieldCache(sortedFieldMap, indexFieldMap);
if (!(configurationHolder instanceof WriteHolder)) { if (!(configurationHolder instanceof WriteHolder)) {
return fieldCache; return fieldCache;
@ -338,21 +338,21 @@ public class ClassUtils {
return fieldCache; return fieldCache;
} }
// ignore filed // ignore filed
Map<Integer, Field> tempSortedFieldMapp = MapUtils.newHashMap(); Map<Integer, FieldWrapper> tempSortedFieldMapp = MapUtils.newHashMap();
int index = 0; int index = 0;
for (Map.Entry<Integer, Field> entry : sortedFieldMap.entrySet()) { for (Map.Entry<Integer, FieldWrapper> entry : sortedFieldMap.entrySet()) {
Integer key = entry.getKey(); Integer key = entry.getKey();
Field field = entry.getValue(); FieldWrapper field = entry.getValue();
// The current field needs to be ignored // The current field needs to be ignored
if (writeHolder.ignore(entry.getValue().getName(), entry.getKey())) { if (writeHolder.ignore(field.getFieldName(), entry.getKey())) {
if (ignoreMap != null) { if (ignoreSet != null) {
ignoreMap.put(field.getName(), field); ignoreSet.add(field.getFieldName());
} }
indexFieldMap.remove(index); indexFieldMap.remove(index);
} else { } else {
// Mandatory sorted fields // Mandatory sorted fields
if (indexFieldMap.containsKey(key)) { if (ignoreSet.contains(key)) {
tempSortedFieldMapp.put(key, field); tempSortedFieldMapp.put(key, field);
} else { } else {
// Need to reorder automatically // Need to reorder automatically
@ -374,13 +374,13 @@ public class ClassUtils {
/** /**
* it only works when {@link WriteHolder#getIncludeColumnFieldNames()} or * it only works when {@link WriteHolder#getIncludeColumnFieldNames()} or
* {@link WriteHolder#getIncludeColumnIndexes()} ()} has value * {@link WriteHolder#getIncludeColumnIndexes()} ()} has value
* and {@link WriteHolder#getSortByIncludeColumn()} ()} is true * and {@link WriteHolder#getOrderByIncludeColumn()} ()} is true
**/ **/
private static void resortField(WriteHolder writeHolder, FieldCache fieldCache) { private static void resortField(WriteHolder writeHolder, FieldCache fieldCache) {
if (!writeHolder.sortByIncludeColumn()) { if (!writeHolder.orderByIncludeColumn()) {
return; return;
} }
Map<Integer, Field> indexFieldMap = fieldCache.getIndexFieldMap(); Map<Integer, FieldWrapper> indexFieldMap = fieldCache.getIndexFieldMap();
Collection<String> includeColumnFieldNames = writeHolder.includeColumnFieldNames(); Collection<String> includeColumnFieldNames = writeHolder.includeColumnFieldNames();
if (!CollectionUtils.isEmpty(includeColumnFieldNames)) { if (!CollectionUtils.isEmpty(includeColumnFieldNames)) {
@ -392,9 +392,9 @@ public class ClassUtils {
} }
// rebuild sortedFieldMap // rebuild sortedFieldMap
Map<Integer, Field> tempSortedFieldMap = MapUtils.newHashMap(); Map<Integer, FieldWrapper> tempSortedFieldMap = MapUtils.newHashMap();
fieldCache.getSortedFieldMap().forEach((index, field) -> { fieldCache.getSortedFieldMap().forEach((index, field) -> {
Integer tempFieldIndex = filedIndexMap.get(field.getName()); Integer tempFieldIndex = filedIndexMap.get(field.getFieldName());
if (tempFieldIndex != null) { if (tempFieldIndex != null) {
tempSortedFieldMap.put(tempFieldIndex, field); tempSortedFieldMap.put(tempFieldIndex, field);
@ -409,7 +409,7 @@ public class ClassUtils {
} }
Collection<Integer> includeColumnIndexes = writeHolder.includeColumnIndexes(); Collection<Integer> includeColumnIndexes = writeHolder.includeColumnIndexes();
if (!CollectionUtils.isEmpty(includeColumnFieldNames)) { if (!CollectionUtils.isEmpty(includeColumnIndexes)) {
// Index sorted map // Index sorted map
Map<Integer, Integer> filedIndexMap = MapUtils.newHashMap(); Map<Integer, Integer> filedIndexMap = MapUtils.newHashMap();
int fieldIndex = 0; int fieldIndex = 0;
@ -418,7 +418,7 @@ public class ClassUtils {
} }
// rebuild sortedFieldMap // rebuild sortedFieldMap
Map<Integer, Field> tempSortedFieldMap = MapUtils.newHashMap(); Map<Integer, FieldWrapper> tempSortedFieldMap = MapUtils.newHashMap();
fieldCache.getSortedFieldMap().forEach((index, field) -> { fieldCache.getSortedFieldMap().forEach((index, field) -> {
Integer tempFieldIndex = filedIndexMap.get(index); Integer tempFieldIndex = filedIndexMap.get(index);
@ -431,16 +431,16 @@ public class ClassUtils {
} }
} }
private static Map<Integer, Field> buildSortedAllFieldMap(Map<Integer, List<Field>> orderFieldMap, private static Map<Integer, FieldWrapper> buildSortedAllFieldMap(Map<Integer, List<FieldWrapper>> orderFieldMap,
Map<Integer, Field> indexFieldMap) { Map<Integer, FieldWrapper> indexFieldMap) {
Map<Integer, Field> sortedAllFieldMap = new HashMap<Integer, Field>( Map<Integer, FieldWrapper> sortedAllFieldMap = MapUtils.newHashMapWithExpectedSize(
(orderFieldMap.size() + indexFieldMap.size()) * 4 / 3 + 1); orderFieldMap.size() + indexFieldMap.size());
Map<Integer, Field> tempIndexFieldMap = new HashMap<Integer, Field>(indexFieldMap); Map<Integer, FieldWrapper> tempIndexFieldMap = MapUtils.newLinkedHashMapWithExpectedSize(indexFieldMap.size());
int index = 0; int index = 0;
for (List<Field> fieldList : orderFieldMap.values()) { for (List<FieldWrapper> fieldList : orderFieldMap.values()) {
for (Field field : fieldList) { for (FieldWrapper field : fieldList) {
while (tempIndexFieldMap.containsKey(index)) { while (tempIndexFieldMap.containsKey(index)) {
sortedAllFieldMap.put(index, tempIndexFieldMap.get(index)); sortedAllFieldMap.put(index, tempIndexFieldMap.get(index));
tempIndexFieldMap.remove(index); tempIndexFieldMap.remove(index);
@ -454,34 +454,45 @@ public class ClassUtils {
return sortedAllFieldMap; return sortedAllFieldMap;
} }
private static void declaredOneField(Field field, Map<Integer, List<Field>> orderFieldMap, private static void declaredOneField(Field field, Map<Integer, List<FieldWrapper>> orderFieldMap,
Map<Integer, Field> indexFieldMap, Map<String, Field> ignoreMap, Map<Integer, FieldWrapper> indexFieldMap, Set<String> ignoreSet,
ExcelIgnoreUnannotated excelIgnoreUnannotated) { ExcelIgnoreUnannotated excelIgnoreUnannotated) {
String fieldName = FieldUtils.resolveCglibFieldName(field);
FieldWrapper fieldWrapper = new FieldWrapper();
fieldWrapper.setField(field);
fieldWrapper.setFieldName(fieldName);
ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class); ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class);
if (excelIgnore != null) { if (excelIgnore != null) {
ignoreMap.put(field.getName(), field); ignoreSet.add(fieldName);
return; return;
} }
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
boolean noExcelProperty = excelProperty == null && excelIgnoreUnannotated != null; boolean noExcelProperty = excelProperty == null && excelIgnoreUnannotated != null;
if (noExcelProperty) { if (noExcelProperty) {
ignoreMap.put(field.getName(), field); ignoreSet.add(fieldName);
return; return;
} }
boolean isStaticFinalOrTransient = boolean isStaticFinalOrTransient =
(Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()))
|| Modifier.isTransient(field.getModifiers()); || Modifier.isTransient(field.getModifiers());
if (excelProperty == null && isStaticFinalOrTransient) { if (excelProperty == null && isStaticFinalOrTransient) {
ignoreMap.put(field.getName(), field); ignoreSet.add(fieldName);
return; return;
} }
// set heads
if (excelProperty != null) {
fieldWrapper.setHeads(excelProperty.value());
}
if (excelProperty != null && excelProperty.index() >= 0) { if (excelProperty != null && excelProperty.index() >= 0) {
if (indexFieldMap.containsKey(excelProperty.index())) { if (indexFieldMap.containsKey(excelProperty.index())) {
throw new ExcelCommonException("The index of '" + indexFieldMap.get(excelProperty.index()).getName() throw new ExcelCommonException(
"The index of '" + indexFieldMap.get(excelProperty.index()).getFieldName()
+ "' and '" + field.getName() + "' must be inconsistent"); + "' and '" + field.getName() + "' must be inconsistent");
} }
indexFieldMap.put(excelProperty.index(), field); indexFieldMap.put(excelProperty.index(), fieldWrapper);
return; return;
} }
@ -489,8 +500,8 @@ public class ClassUtils {
if (excelProperty != null) { if (excelProperty != null) {
order = excelProperty.order(); order = excelProperty.order();
} }
List<Field> orderFieldList = orderFieldMap.computeIfAbsent(order, key -> ListUtils.newArrayList()); List<FieldWrapper> orderFieldList = orderFieldMap.computeIfAbsent(order, key -> ListUtils.newArrayList());
orderFieldList.add(field); orderFieldList.add(fieldWrapper);
} }
/** /**

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

@ -122,12 +122,12 @@ public abstract class AbstractExcelWriterParameterBuilder<T extends AbstractExce
} }
/** /**
* Data will be sorted according to {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}. * Data will be order by {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}.
* *
* default is false. * default is false.
**/ **/
public T sortByIncludeColumn(Boolean sortByIncludeColumn) { public T orderByIncludeColumn(Boolean orderByIncludeColumn) {
parameter().setSortByIncludeColumn(sortByIncludeColumn); parameter().setOrderByIncludeColumn(orderByIncludeColumn);
return self(); return self();
} }
} }

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

@ -11,6 +11,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.FieldCache;
import com.alibaba.excel.metadata.FieldWrapper;
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;
@ -183,9 +184,9 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
maxCellIndex++; maxCellIndex++;
FieldCache fieldCache = ClassUtils.declaredFields(oneRowData.getClass(), writeContext.currentWriteHolder()); FieldCache fieldCache = ClassUtils.declaredFields(oneRowData.getClass(), writeContext.currentWriteHolder());
for (Map.Entry<Integer, Field> entry : fieldCache.getSortedFieldMap().entrySet()) { for (Map.Entry<Integer, FieldWrapper> entry : fieldCache.getSortedFieldMap().entrySet()) {
Field field = entry.getValue(); FieldWrapper field = entry.getValue();
String fieldName = FieldUtils.resolveCglibFieldName(field); String fieldName = field.getFieldName();
boolean uselessData = !beanKeySet.contains(fieldName) || beanMapHandledSet.contains(fieldName); boolean uselessData = !beanKeySet.contains(fieldName) || beanMapHandledSet.contains(fieldName);
if (uselessData) { if (uselessData) {
continue; continue;
@ -213,18 +214,4 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
} }
} }
private void initSortedAllFieldMapFieldList(Class<?> clazz, Map<Integer, Field> sortedAllFieldMap) {
if (!sortedAllFieldMap.isEmpty()) {
return;
}
WriteSheetHolder writeSheetHolder = writeContext.writeSheetHolder();
boolean needIgnore =
!CollectionUtils.isEmpty(writeSheetHolder.getExcludeColumnFieldNames()) || !CollectionUtils
.isEmpty(writeSheetHolder.getExcludeColumnIndexes()) || !CollectionUtils
.isEmpty(writeSheetHolder.getIncludeColumnFieldNames()) || !CollectionUtils
.isEmpty(writeSheetHolder.getIncludeColumnIndexes());
ClassUtils.declaredFields(clazz, writeSheetHolder);
}
} }

4
easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java

@ -58,9 +58,9 @@ public class WriteBasicParameter extends BasicParameter {
private Collection<String> includeColumnFieldNames; private Collection<String> includeColumnFieldNames;
/** /**
* Data will be sorted according to {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}. * Data will be order by {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}.
* *
* default is false. * default is false.
*/ */
private Boolean sortByIncludeColumn; private Boolean orderByIncludeColumn;
} }

17
easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java

@ -14,7 +14,6 @@ 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;
@ -97,11 +96,11 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
private Collection<String> includeColumnFieldNames; private Collection<String> includeColumnFieldNames;
/** /**
* Data will be sorted according to {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}. * Data will be order by {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}.
* *
* default is false. * default is false.
*/ */
private Boolean sortByIncludeColumn; private Boolean orderByIncludeColumn;
/** /**
* Write handler * Write handler
@ -203,14 +202,14 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
this.includeColumnFieldNames = writeBasicParameter.getIncludeColumnFieldNames(); this.includeColumnFieldNames = writeBasicParameter.getIncludeColumnFieldNames();
} }
if (writeBasicParameter.getSortByIncludeColumn() == null) { if (writeBasicParameter.getOrderByIncludeColumn() == null) {
if (parentAbstractWriteHolder == null) { if (parentAbstractWriteHolder == null) {
this.sortByIncludeColumn = Boolean.FALSE; this.orderByIncludeColumn = Boolean.FALSE;
} else { } else {
this.sortByIncludeColumn = parentAbstractWriteHolder.getSortByIncludeColumn(); this.orderByIncludeColumn = parentAbstractWriteHolder.getOrderByIncludeColumn();
} }
} else { } else {
this.sortByIncludeColumn = writeBasicParameter.getSortByIncludeColumn(); this.orderByIncludeColumn = writeBasicParameter.getOrderByIncludeColumn();
} }
if (writeBasicParameter.getIncludeColumnIndexes() == null && parentAbstractWriteHolder != null) { if (writeBasicParameter.getIncludeColumnIndexes() == null && parentAbstractWriteHolder != null) {
@ -500,8 +499,8 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
} }
@Override @Override
public boolean sortByIncludeColumn() { public boolean orderByIncludeColumn() {
return getSortByIncludeColumn(); return getOrderByIncludeColumn();
} }
@Override @Override

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

@ -49,11 +49,11 @@ public interface WriteHolder extends ConfigurationHolder {
int relativeHeadRowIndex(); int relativeHeadRowIndex();
/** /**
* Data will be sorted according to {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}. * Data will be order by {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}.
* *
* default is false. * default is false.
*/ */
boolean sortByIncludeColumn(); boolean orderByIncludeColumn();
/** /**
* Only output the custom columns. * Only output the custom columns.

21
easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheData.java vendored

@ -0,0 +1,21 @@
package com.alibaba.easyexcel.test.core.cache;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
/**
* @author Jiaju Zhuang
*/
@Getter
@Setter
@EqualsAndHashCode
public class CacheData {
@ExcelProperty("姓名")
private String name;
@ExcelProperty("年龄")
private Long age;
}

219
easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheDataTest.java vendored

@ -0,0 +1,219 @@
package com.alibaba.easyexcel.test.core.cache;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.alibaba.easyexcel.test.demo.read.DemoData;
import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CacheLocationEnum;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.FieldCache;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.util.FieldUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
/**
* @author Jiaju Zhuang
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Slf4j
public class CacheDataTest {
private static File file07;
private static File fileCacheInvoke;
private static File fileCacheInvoke2;
private static File fileCacheInvokeMemory;
private static File fileCacheInvokeMemory2;
@BeforeClass
public static void init() {
file07 = TestFileUtil.createNewFile("cache/cache.xlsx");
fileCacheInvoke = TestFileUtil.createNewFile("cache/fileCacheInvoke.xlsx");
fileCacheInvoke2 = TestFileUtil.createNewFile("cache/fileCacheInvoke2.xlsx");
fileCacheInvokeMemory = TestFileUtil.createNewFile("cache/fileCacheInvokeMemory.xlsx");
fileCacheInvokeMemory2 = TestFileUtil.createNewFile("cache/fileCacheInvokeMemory2.xlsx");
}
@Test
public void t01ReadAndWrite() throws Exception {
Field field = FieldUtils.getField(ClassUtils.class, "FIELD_THREAD_LOCAL", true);
ThreadLocal<Map<Class<?>, FieldCache>> fieldThreadLocal = (ThreadLocal<Map<Class<?>, FieldCache>>)field.get(
ClassUtils.class.newInstance());
Assert.assertNull(fieldThreadLocal.get());
EasyExcel.write(file07, CacheData.class).sheet().doWrite(data());
EasyExcel.read(file07, CacheData.class, new PageReadListener<DemoData>(dataList -> {
Assert.assertNotNull(fieldThreadLocal.get());
}))
.sheet()
.doRead();
Assert.assertNull(fieldThreadLocal.get());
}
@Test
public void t02ReadAndWriteInvoke() throws Exception {
EasyExcel.write(fileCacheInvoke, CacheInvokeData.class).sheet().doWrite(dataInvoke());
EasyExcel.read(fileCacheInvoke, CacheInvokeData.class, new AnalysisEventListener<CacheInvokeData>() {
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
Assert.assertEquals(2, headMap.size());
Assert.assertEquals("姓名", headMap.get(0));
Assert.assertEquals("年龄", headMap.get(1));
}
@Override
public void invoke(CacheInvokeData data, AnalysisContext context) {
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
}).sheet()
.doRead();
Field name = FieldUtils.getField(CacheInvokeData.class, "name", true);
ExcelProperty annotation = name.getAnnotation(ExcelProperty.class);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
memberValues.setAccessible(true);
Map map = (Map)memberValues.get(invocationHandler);
map.put("value", new String[] {"姓名2"});
EasyExcel.write(fileCacheInvoke2, CacheInvokeData.class).sheet().doWrite(dataInvoke());
EasyExcel.read(fileCacheInvoke2, CacheInvokeData.class, new AnalysisEventListener<CacheInvokeData>() {
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
Assert.assertEquals(2, headMap.size());
Assert.assertEquals("姓名2", headMap.get(0));
Assert.assertEquals("年龄", headMap.get(1));
}
@Override
public void invoke(CacheInvokeData data, AnalysisContext context) {
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
}).sheet()
.doRead();
}
@Test
public void t03ReadAndWriteInvokeMemory() throws Exception {
EasyExcel.write(fileCacheInvokeMemory, CacheInvokeMemoryData.class)
.filedCacheLocation(CacheLocationEnum.MEMORY)
.sheet()
.doWrite(dataInvokeMemory());
EasyExcel.read(fileCacheInvokeMemory, CacheInvokeMemoryData.class,
new AnalysisEventListener<CacheInvokeMemoryData>() {
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
Assert.assertEquals(2, headMap.size());
Assert.assertEquals("姓名", headMap.get(0));
Assert.assertEquals("年龄", headMap.get(1));
}
@Override
public void invoke(CacheInvokeMemoryData data, AnalysisContext context) {
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
})
.filedCacheLocation(CacheLocationEnum.MEMORY)
.sheet()
.doRead();
Field name = FieldUtils.getField(CacheInvokeMemoryData.class, "name", true);
ExcelProperty annotation = name.getAnnotation(ExcelProperty.class);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
memberValues.setAccessible(true);
Map map = (Map)memberValues.get(invocationHandler);
map.put("value", new String[] {"姓名2"});
EasyExcel.write(fileCacheInvokeMemory2, CacheInvokeMemoryData.class)
.filedCacheLocation(CacheLocationEnum.MEMORY)
.sheet()
.doWrite(dataInvokeMemory());
EasyExcel.read(fileCacheInvokeMemory2, CacheInvokeMemoryData.class,
new AnalysisEventListener<CacheInvokeMemoryData>() {
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
Assert.assertEquals(2, headMap.size());
Assert.assertEquals("姓名", headMap.get(0));
Assert.assertEquals("年龄", headMap.get(1));
}
@Override
public void invoke(CacheInvokeMemoryData data, AnalysisContext context) {
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
})
.filedCacheLocation(CacheLocationEnum.MEMORY)
.sheet()
.doRead();
}
private List<CacheData> data() {
List<CacheData> list = new ArrayList<CacheData>();
for (int i = 0; i < 10; i++) {
CacheData simpleData = new CacheData();
simpleData.setName("姓名" + i);
simpleData.setAge((long)i);
list.add(simpleData);
}
return list;
}
private List<CacheInvokeData> dataInvoke() {
List<CacheInvokeData> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CacheInvokeData simpleData = new CacheInvokeData();
simpleData.setName("姓名" + i);
simpleData.setAge((long)i);
list.add(simpleData);
}
return list;
}
private List<CacheInvokeMemoryData> dataInvokeMemory() {
List<CacheInvokeMemoryData> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CacheInvokeMemoryData simpleData = new CacheInvokeMemoryData();
simpleData.setName("姓名" + i);
simpleData.setAge((long)i);
list.add(simpleData);
}
return list;
}
}

21
easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheInvokeData.java vendored

@ -0,0 +1,21 @@
package com.alibaba.easyexcel.test.core.cache;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
/**
* @author Jiaju Zhuang
*/
@Getter
@Setter
@EqualsAndHashCode
public class CacheInvokeData {
@ExcelProperty("姓名")
private String name;
@ExcelProperty("年龄")
private Long age;
}

21
easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheInvokeMemoryData.java vendored

@ -0,0 +1,21 @@
package com.alibaba.easyexcel.test.core.cache;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
/**
* @author Jiaju Zhuang
*/
@Getter
@Setter
@EqualsAndHashCode
public class CacheInvokeMemoryData {
@ExcelProperty("姓名")
private String name;
@ExcelProperty("年龄")
private Long age;
}

84
easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeDataTest.java

@ -34,9 +34,13 @@ 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 includeFieldNameSort07; private static File includeFieldNameOrder07;
private static File includeFieldNameSort03; private static File includeFieldNameOrder03;
private static File includeFieldNameSortCsv; private static File includeFieldNameOrderCsv;
private static File includeFieldNameOrderIndex07;
private static File includeFieldNameOrderIndex03;
private static File includeFieldNameOrderIndexCsv;
@BeforeClass @BeforeClass
public static void init() { public static void init() {
@ -52,9 +56,12 @@ 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");
includeFieldNameSort07 = TestFileUtil.createNewFile("includeFieldNameSort.xlsx"); includeFieldNameOrder07 = TestFileUtil.createNewFile("includeFieldNameOrder.xlsx");
includeFieldNameSort03 = TestFileUtil.createNewFile("includeFieldNameSort.xls"); includeFieldNameOrder03 = TestFileUtil.createNewFile("includeFieldNameOrder.xls");
includeFieldNameSortCsv = TestFileUtil.createNewFile("includeFieldNameSort.csv"); includeFieldNameOrderCsv = TestFileUtil.createNewFile("includeFieldNameOrder.csv");
includeFieldNameOrderIndex07 = TestFileUtil.createNewFile("includeFieldNameOrderIndex.xlsx");
includeFieldNameOrderIndex03 = TestFileUtil.createNewFile("includeFieldNameOrderIndex.xls");
includeFieldNameOrderIndexCsv = TestFileUtil.createNewFile("includeFieldNameOrderIndex.csv");
} }
@Test @Test
@ -119,18 +126,33 @@ public class ExcludeOrIncludeDataTest {
} }
@Test @Test
public void t41IncludeFieldName07() { public void t41IncludeFieldNameOrder07() {
includeFieldNameForce(includeFieldNameForceIndex07); includeFieldNameOrder(includeFieldNameOrder07);
}
@Test
public void t42IncludeFieldNameOrder03() {
includeFieldNameOrder(includeFieldNameOrder03);
}
@Test
public void t43IncludeFieldNameOrderCsv() {
includeFieldNameOrder(includeFieldNameOrderCsv);
}
@Test
public void t41IncludeFieldNameOrderIndex07() {
includeFieldNameOrderIndex(includeFieldNameOrderIndex07);
} }
@Test @Test
public void t42IncludeFieldName03() { public void t42IncludeFieldNameOrderIndex03() {
includeFieldNameForce(includeFieldNameForceIndex03); includeFieldNameOrderIndex(includeFieldNameOrderIndex03);
} }
@Test @Test
public void t43IncludeFieldNameCsv() { public void t43IncludeFieldNameOrderIndexCsv() {
includeFieldNameForce(includeFieldNameForceIndexCsv); includeFieldNameOrderIndex(includeFieldNameOrderIndexCsv);
} }
private void excludeIndex(File file) { private void excludeIndex(File file) {
@ -192,18 +214,44 @@ public class ExcludeOrIncludeDataTest {
Assert.assertEquals("column3", record.get(1)); Assert.assertEquals("column3", record.get(1));
} }
private void includeFieldNameForce(File file) {
private void includeFieldNameOrderIndex(File file) {
List<Integer> includeColumnIndexes = new ArrayList<>();
includeColumnIndexes.add(3);
includeColumnIndexes.add(1);
includeColumnIndexes.add(2);
includeColumnIndexes.add(0);
EasyExcel.write(file, ExcludeOrIncludeData.class)
.includeColumnIndexes(includeColumnIndexes)
.orderByIncludeColumn(true).
sheet()
.doWrite(data());
List<Map<Integer, String>> dataMap = EasyExcel.read(file).sheet().doReadSync();
Assert.assertEquals(1, dataMap.size());
Map<Integer, String> record = dataMap.get(0);
Assert.assertEquals(4, record.size());
Assert.assertEquals("column4", record.get(0));
Assert.assertEquals("column2", record.get(1));
Assert.assertEquals("column3", record.get(2));
Assert.assertEquals("column1", record.get(3));
}
private void includeFieldNameOrder(File file) {
List<String> includeColumnFieldNames = new ArrayList<>(); List<String> includeColumnFieldNames = new ArrayList<>();
includeColumnFieldNames.add("column3"); includeColumnFieldNames.add("column4");
includeColumnFieldNames.add("column2"); includeColumnFieldNames.add("column2");
EasyExcel.write(file, ExcludeOrIncludeData.class).includeColumnFieldNames(includeColumnFieldNames) includeColumnFieldNames.add("column3");
.sortByIncludeColumn(true).sheet().doWrite(data()); EasyExcel.write(file, ExcludeOrIncludeData.class)
.includeColumnFieldNames(includeColumnFieldNames)
.orderByIncludeColumn(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);
Assert.assertEquals(2, record.size()); Assert.assertEquals(3, record.size());
Assert.assertEquals("column3", record.get(0)); Assert.assertEquals("column4", record.get(0));
Assert.assertEquals("column2", record.get(1)); Assert.assertEquals("column2", record.get(1));
Assert.assertEquals("column3", record.get(2));
} }
private List<ExcludeOrIncludeData> data() { private List<ExcludeOrIncludeData> data() {

5
update.md

@ -7,8 +7,9 @@
* 在`easyexcel-parent` 包中移除测试包的`dependencyManagement` * 在`easyexcel-parent` 包中移除测试包的`dependencyManagement`
* 删除`org.apache.poi.hssf.usermodel.PoiUtils.java`, * 删除`org.apache.poi.hssf.usermodel.PoiUtils.java`,
使用反射获取 [Issue #2804](https://github.com/alibaba/easyexcel/issues/2804) 使用反射获取 [Issue #2804](https://github.com/alibaba/easyexcel/issues/2804)
* 默认对象反射缓存改成`ThreadLocal`,并支持设置 [Issue #2792](https://github.com/alibaba/easyexcel/issues/2792) * 默认对象反射缓存改成`ThreadLocal`,并支持设置反射缓存类型 [Issue #2792](https://github.com/alibaba/easyexcel/issues/2792)
* 支持根据`includeColumnIndexes`和`includeColumnFieldNames`排序 [Issue #2697](https://github.com/alibaba/easyexcel/issues/2697) * 支持根据`includeColumnIndexes`和`includeColumnFieldNames`
排序 [Issue #2697](https://github.com/alibaba/easyexcel/issues/2697)
# 3.2.1 # 3.2.1

Loading…
Cancel
Save