close
Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
perf(bean): 优化FastBeanCopier复制性能
  • Loading branch information
zhou-hao committed May 28, 2026
commit c4452f1d7f1e772c1d1feb534935cc20bc6de11f
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ public Copier createCopier(Class<?> source, Class<?> target) {
static final class AccessorCopier implements Copier {
private final NamedPropertyTransfer[] transfers;
private final ExtensionTransfer[] extensionTransfers;
private final Map<String, NamedPropertyTransfer> sourceMapTransfers;
private final boolean sourceIsMap;
private final PropertyTransfer[] fastAccessorTransfers;
private final boolean fastAccessorMode;

AccessorCopier(Class<?> source, Class<?> target, PropertyAccessor propertyAccessor) {
Map<String, FastBeanCopierPropertySupport.ClassProperty> sourceProperties = null;
Expand Down Expand Up @@ -90,6 +94,8 @@ static final class AccessorCopier implements Copier {

List<NamedPropertyTransfer> mappings = new ArrayList<>(sourceProperties.size());
List<ExtensionTransfer> extensionMappings = new ArrayList<>();
List<PropertyTransfer> fastTransfers = new ArrayList<>(sourceProperties.size());
boolean accessorOnly = !sourceIsMap && !targetIsMap && !targetIsExtendable;
for (FastBeanCopierPropertySupport.ClassProperty sourceProperty : sourceProperties.values()) {
FastBeanCopierPropertySupport.ClassProperty targetProperty = targetProperties.get(sourceProperty.getName());
if (targetProperty == null) {
Expand All @@ -100,15 +106,40 @@ static final class AccessorCopier implements Copier {
}
NamedPropertyTransfer transfer = NamedPropertyTransfer.of(target, sourceProperty, targetProperty, propertyAccessor);
mappings.add(transfer);
if (accessorOnly && transfer instanceof AccessorPropertyTransfer) {
fastTransfers.add(((AccessorPropertyTransfer) transfer).transfer);
} else {
accessorOnly = false;
}
}
this.transfers = mappings.toArray(new NamedPropertyTransfer[0]);
this.extensionTransfers = extensionMappings.toArray(new ExtensionTransfer[0]);
this.sourceIsMap = sourceIsMap;
this.fastAccessorMode = accessorOnly && !fastTransfers.isEmpty() && fastTransfers.size() == mappings.size();
this.fastAccessorTransfers = fastAccessorMode
? fastTransfers.toArray(new PropertyTransfer[0])
: null;
if (sourceIsMap) {
Map<String, NamedPropertyTransfer> transferIndex = new HashMap<>(mappings.size());
for (NamedPropertyTransfer transfer : mappings) {
transferIndex.put(transfer.name, transfer);
}
this.sourceMapTransfers = transferIndex;
} else {
this.sourceMapTransfers = Collections.emptyMap();
}
}

@Override
public void copy(Object source, Object target, Set<String> ignore, Converter converter) {
try {
if (ignore.isEmpty()) {
if (sourceIsMap) {
if (ignore.isEmpty()) {
copyMapWithoutIgnore((Map<?, ?>) source, target, converter);
} else {
copyMapWithIgnore((Map<?, ?>) source, target, ignore, converter);
}
} else if (ignore.isEmpty()) {
copyWithoutIgnore(source, target, converter);
} else {
copyWithIgnore(source, target, ignore, converter);
Expand All @@ -121,6 +152,12 @@ public void copy(Object source, Object target, Set<String> ignore, Converter con
}

private void copyWithoutIgnore(Object source, Object target, Converter converter) throws Throwable {
if (fastAccessorMode) {
for (PropertyTransfer transfer : fastAccessorTransfers) {
transfer.transfer(source, target);
}
return;
}
for (NamedPropertyTransfer transfer : transfers) {
transfer.transfer(source, target, converter);
}
Expand All @@ -143,6 +180,28 @@ private void copyWithIgnore(Object source, Object target, Set<String> ignore, Co
}
}
}

private void copyMapWithoutIgnore(Map<?, ?> source, Object target, Converter converter) throws Throwable {
for (Map.Entry<?, ?> entry : source.entrySet()) {
NamedPropertyTransfer transfer = sourceMapTransfers.get(String.valueOf(entry.getKey()));
if (transfer != null) {
transfer.transferValue(entry.getValue(), target, converter);
}
}
}

private void copyMapWithIgnore(Map<?, ?> source, Object target, Set<String> ignore, Converter converter) throws Throwable {
for (Map.Entry<?, ?> entry : source.entrySet()) {
String name = String.valueOf(entry.getKey());
if (ignore.contains(name)) {
continue;
}
NamedPropertyTransfer transfer = sourceMapTransfers.get(name);
if (transfer != null) {
transfer.transferValue(entry.getValue(), target, converter);
}
}
}
}

abstract static class NamedPropertyTransfer {
Expand All @@ -160,6 +219,10 @@ final void transfer(Object source, Object target, Set<String> ignore, Converter

abstract void transfer(Object source, Object target, Converter converter) throws Throwable;

void transferValue(Object value, Object target, Converter converter) throws Throwable {
throw new UnsupportedOperationException("transferValue is only supported for map source transfers");
}

static NamedPropertyTransfer of(Class<?> target,
FastBeanCopierPropertySupport.ClassProperty sourceProperty,
FastBeanCopierPropertySupport.ClassProperty targetProperty,
Expand All @@ -170,6 +233,29 @@ static NamedPropertyTransfer of(Class<?> target,
boolean targetPrimitive = targetProperty.isPrimitive();
Class<?>[] genericTypes = resolveGenericTypes(target, targetProperty);

if (Map.class.isAssignableFrom(sourceProperty.getBeanType())) {
ValueWriter writer = createWriter(targetProperty, propertyAccessor);
if (targetType == Object.class) {
return new MapDirectPropertyTransfer(sourceProperty.getName(), writer, targetPrimitive);
}
if (requiresGenericConversion(targetType, genericTypes)) {
return new MapConvertingPropertyTransfer(sourceProperty.getName(),
writer,
targetPrimitive,
targetType,
genericTypes,
isNumberType(targetType),
true);
}
return new MapConvertingPropertyTransfer(sourceProperty.getName(),
writer,
targetPrimitive,
targetType,
genericTypes,
isNumberType(targetType),
true);
}

PropertyTransfer accessorTransfer = createAccessorTransfer(sourceProperty,
targetProperty,
propertyAccessor,
Expand Down Expand Up @@ -394,6 +480,93 @@ void transfer(Object source, Object target, Converter converter) throws Throwabl
}
}

static final class MapDirectPropertyTransfer extends NamedPropertyTransfer {
private final ValueWriter writer;
private final boolean targetPrimitive;

MapDirectPropertyTransfer(String name, ValueWriter writer, boolean targetPrimitive) {
super(name);
this.writer = writer;
this.targetPrimitive = targetPrimitive;
}

@Override
void transfer(Object source, Object target, Converter converter) {
throw new UnsupportedOperationException();
}

@Override
void transferValue(Object value, Object target, Converter converter) throws Throwable {
if (value == null && !targetPrimitive) {
return;
}
writer.write(target, value);
}
}

static final class MapConvertingPropertyTransfer extends NamedPropertyTransfer {
private final ValueWriter writer;
private final boolean targetPrimitive;
private final Class<?> targetType;
private final Class<?>[] genericTypes;
private final boolean unwrapEnumDictNumber;
private final boolean allowDirectAssignable;
private final boolean beanLikeTarget;

MapConvertingPropertyTransfer(String name,
ValueWriter writer,
boolean targetPrimitive,
Class<?> targetType,
Class<?>[] genericTypes,
boolean unwrapEnumDictNumber,
boolean allowDirectAssignable) {
super(name);
this.writer = writer;
this.targetPrimitive = targetPrimitive;
this.targetType = targetType;
this.genericTypes = genericTypes;
this.unwrapEnumDictNumber = unwrapEnumDictNumber;
this.allowDirectAssignable = allowDirectAssignable;
this.beanLikeTarget = isBeanLikeTarget(targetType);
}

@Override
void transfer(Object source, Object target, Converter converter) {
throw new UnsupportedOperationException();
}

@Override
@SuppressWarnings("unchecked")
void transferValue(Object value, Object target, Converter converter) throws Throwable {
if (value == null && !targetPrimitive) {
return;
}
if (unwrapEnumDictNumber && value instanceof EnumDict) {
Object enumValue = ((EnumDict<?>) value).getValue();
if (enumValue != null) {
value = enumValue;
}
}
if (allowDirectAssignable && isDirectAssignable(targetType, value)) {
writer.write(target, value);
return;
}
if (beanLikeTarget && value instanceof Map) {
Object nested = FastBeanCopierSupport.copy(value,
FastBeanCopierSupport.getBeanFactory().newInstance(targetType),
converter,
Collections.emptySet());
writer.write(target, nested);
return;
}
Object converted = converter.convert(value, (Class) targetType, genericTypes);
if (converted == null && !targetPrimitive) {
return;
}
writer.write(target, converted);
}
}

static final class CloneablePropertyTransfer extends ReaderWriterPropertyTransfer {
private final Class<?> targetType;
private final Class<?>[] genericTypes;
Expand Down Expand Up @@ -438,6 +611,7 @@ static final class ConvertingPropertyTransfer extends ReaderWriterPropertyTransf
private final Class<?>[] genericTypes;
private final boolean unwrapEnumDictNumber;
private final boolean allowDirectAssignable;
private final boolean beanLikeTarget;

ConvertingPropertyTransfer(String name,
ValueReader reader,
Expand All @@ -454,6 +628,7 @@ static final class ConvertingPropertyTransfer extends ReaderWriterPropertyTransf
this.genericTypes = genericTypes;
this.unwrapEnumDictNumber = unwrapEnumDictNumber;
this.allowDirectAssignable = allowDirectAssignable;
this.beanLikeTarget = isBeanLikeTarget(targetType);
}

@Override
Expand All @@ -473,6 +648,14 @@ void transfer(Object source, Object target, Converter converter) throws Throwabl
writer.write(target, value);
return;
}
if (beanLikeTarget && value instanceof Map) {
Object nested = FastBeanCopierSupport.copy(value,
FastBeanCopierSupport.getBeanFactory().newInstance(targetType),
converter,
Collections.emptySet());
writer.write(target, nested);
return;
}
Object converted = converter.convert(value, (Class) targetType, genericTypes);
if (converted == null && !targetPrimitive) {
return;
Expand Down Expand Up @@ -544,6 +727,22 @@ private static boolean isNumberType(Class<?> targetType) {
&& targetType != char.class;
}

private static boolean isBeanLikeTarget(Class<?> targetType) {
return targetType != Object.class
&& targetType != String.class
&& targetType != CharSequence.class
&& targetType != Date.class
&& targetType != Boolean.class
&& targetType != Character.class
&& targetType != boolean.class
&& targetType != char.class
&& !targetType.isEnum()
&& !targetType.isArray()
&& !Collection.class.isAssignableFrom(targetType)
&& !Map.class.isAssignableFrom(targetType)
&& !isNumberType(targetType);
}

private static Object tryClone(Object value) {
try {
if (value.getClass().isArray()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
package org.hswebframework.web.bean;

import org.hswebframework.ezorm.core.Extendable;
import org.hswebframework.web.bean.accessor.AsmBeanAccessor;

import java.util.Map;

final class AsmAccessorFastBeanCopierBackend extends AccessorFastBeanCopierBackend {
private final AsmBeanAccessor asmBeanAccessor;

AsmAccessorFastBeanCopierBackend() {
super(new AsmBeanAccessor());
this(new AsmBeanAccessor());
}

private AsmAccessorFastBeanCopierBackend(AsmBeanAccessor accessor) {
super(accessor);
this.asmBeanAccessor = accessor;
}

@Override
public Copier createCopier(Class<?> source, Class<?> target) {
if (!Map.class.isAssignableFrom(source)
&& !Map.class.isAssignableFrom(target)
&& !Extendable.class.isAssignableFrom(source)
&& !Extendable.class.isAssignableFrom(target)) {
Copier copier = asmBeanAccessor.createDirectCopier(source, target);
if (copier != null) {
return copier;
}
}
return super.createCopier(source, target);
}
}
Loading