Skip to content

Commit

Permalink
refactor(PropertyOperator): refactoring PropertyOperator based on Pro…
Browse files Browse the repository at this point in the history
…pDesc (GitHub #231)
Createsequence committed Mar 15, 2024
1 parent a6c763f commit d18606e
Showing 15 changed files with 785 additions and 555 deletions.
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
import cn.crane4j.core.parser.handler.strategy.PropertyMappingStrategy;
import cn.crane4j.core.parser.operation.AssembleOperation;
import cn.crane4j.core.support.converter.ConverterManager;
import cn.crane4j.core.support.reflect.PropDesc;
import cn.crane4j.core.support.reflect.PropertyOperator;
import cn.crane4j.core.util.StringUtils;
import lombok.NonNull;
@@ -141,20 +142,20 @@ protected Object getTheAssociatedSource(Target target, Map<Object, Object> sourc
@Override
protected void completeMapping(Object source, Target target) {
AssembleExecution execution = target.getExecution();
PropDesc sourceDesc = propertyOperator.getPropertyDescriptor(source.getClass());
PropDesc targetDesc = propertyOperator.getPropertyDescriptor(target.getOrigin().getClass());

// mapping properties
PropertyMappingStrategy propertyMappingStrategy = execution.getOperation().getPropertyMappingStrategy();
Set<PropertyMapping> mappings = execution.getOperation().getPropertyMappings();
for (PropertyMapping mapping : mappings) {
mappingProperty(target, source, mapping);
Object sourceValue = mapping.hasSource() ?
sourceDesc.readProperty(source, mapping.getSource()) : source;
Object originTarget = target.getOrigin();
propertyMappingStrategy.doMapping(
originTarget, source, sourceValue, mapping,
sv -> targetDesc.writeProperty(originTarget, mapping.getReference(), sourceValue)
);
}
}

private void mappingProperty(Target entity, Object source, PropertyMapping mapping) {
PropertyMappingStrategy propertyMappingStrategy = entity.getExecution().getOperation().getPropertyMappingStrategy();
Object sourceValue = mapping.hasSource() ?
propertyOperator.readProperty(source.getClass(), source, mapping.getSource()) : source;
Object target = entity.getOrigin();
propertyMappingStrategy.doMapping(
target, source, sourceValue, mapping,
sv -> propertyOperator.writeProperty(target.getClass(), target, mapping.getReference(), sourceValue)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package cn.crane4j.core.support.reflect;

import cn.crane4j.core.support.MethodInvoker;
import cn.crane4j.core.util.CollectionUtils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;

/**
* The abstract implementation of {@link PropDesc}.
*
* @author huangchengxing
*/
@RequiredArgsConstructor
public abstract class AbstractPropDesc implements PropDesc {

private static final MethodInvoker NULL = (target, args) -> null;
private final ConcurrentMap<String, MethodInvoker> getterCache = CollectionUtils.newWeakConcurrentMap();
private final ConcurrentMap<String, MethodInvoker> setterCache = CollectionUtils.newWeakConcurrentMap();
@Getter
protected final Class<?> beanType;

/**
* Get the getter method.
*
* @param propertyName property name
* @return property getter
*/
@Nullable
@Override
public MethodInvoker getGetter(String propertyName) {
MethodInvoker invoker = obtainInvokerFromCache(
getterCache, propertyName, this::findGetter
);
return invoker == NULL ? null : invoker;
}

/**
* Get the setter method.
*
* @param propertyName property name
* @return property setter
*/
@Nullable
@Override
public MethodInvoker getSetter(String propertyName) {
MethodInvoker invoker = obtainInvokerFromCache(
setterCache, propertyName, this::findSetter
);
return invoker == NULL ? null : invoker;
}

private MethodInvoker obtainInvokerFromCache(
ConcurrentMap<String, MethodInvoker> cache, String propertyName,
Function<String, MethodInvoker> invokerSupplier) {
return CollectionUtils.computeIfAbsent(cache, propertyName, prop -> {
MethodInvoker invoker = invokerSupplier.apply(prop);
return invoker == null ? NULL : invoker;
});
}

/**
* find getter.
*
* @param propertyName property name
* @return getter method
*/
@Nullable
protected abstract MethodInvoker findGetter(String propertyName);

/**
* find setter.
*
* @param propertyName property name
* @return setter method
*/
@Nullable
protected abstract MethodInvoker findSetter(String propertyName);
}
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
import com.esotericsoftware.reflectasm.FieldAccess;
import com.esotericsoftware.reflectasm.MethodAccess;
import lombok.RequiredArgsConstructor;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.lang.reflect.Field;
@@ -22,16 +23,6 @@
*/
public class AsmReflectivePropertyOperator extends ReflectivePropertyOperator {

/**
* method access caches.
*/
private final Map<Class<?>, MethodAccess> methodAccessCaches = new ConcurrentHashMap<>();

/**
* field access caches.
*/
private final Map<Class<?>, FieldAccess> fieldAccessCaches = new ConcurrentHashMap<>();

/**
* Create an {@link AsmReflectivePropertyOperator} instance
*
@@ -42,24 +33,81 @@ public AsmReflectivePropertyOperator(@Nullable ConverterManager converterManager
}

/**
* Create {@link MethodInvoker} according to the specified method
* Get property descriptor.
*
* @param targetType target type
* @param propertyName property name
* @param method getter method or setter method
* @return {@link MethodInvoker}
* @return property descriptor
* @since 2.7.0
*/
@Override
protected MethodInvoker createInvokerForMethod(Class<?> targetType, String propertyName, Method method) {
MethodAccess access = CollectionUtils.computeIfAbsent(methodAccessCaches, targetType, MethodAccess::get);
int methodIndex = access.getIndex(method.getName(), method.getParameterTypes());
return new ReflectAsmMethodInvoker(methodIndex, access);
public @NonNull PropDesc getPropertyDescriptor(Class<?> targetType) {
return new AsmReflectivePropDesc(targetType, converterManager, throwIfNoAnyMatched);
}

/**
* {@link PropDesc} implementation based on {@link com.esotericsoftware.reflectasm}.
*
* @author huangchengxing
* @since 2.7.0
*/
protected static class AsmReflectivePropDesc extends ReflectivePropDesc {

private final Map<Class<?>, MethodAccess> methodAccessCaches = new ConcurrentHashMap<>();
private final Map<Class<?>, FieldAccess> fieldAccessCaches = new ConcurrentHashMap<>();

public AsmReflectivePropDesc(
Class<?> beanType, @Nullable ConverterManager converterManager, boolean throwIfNoAnyMatched) {
super(beanType, converterManager, throwIfNoAnyMatched);
}

@Override
protected MethodInvoker createSetterInvokerForField(String propertyName, Field field) {
if (Modifier.isPrivate(field.getModifiers())) {
return super.createSetterInvokerForField(propertyName, field);
}
FieldAccess access = CollectionUtils.computeIfAbsent(fieldAccessCaches, beanType, FieldAccess::get);
try {
int fieldIndex = access.getIndex(field.getName());
return new ReflectAsmFieldInvoker.Setter(access, fieldIndex);
} catch (IllegalArgumentException e) {
return super.createSetterInvokerForField(propertyName, field);
}
}

@Override
protected MethodInvoker createGetterInvokerForField(String propertyName, Field field) {
if (Modifier.isPrivate(field.getModifiers())) {
return super.createGetterInvokerForField(propertyName, field);
}
FieldAccess access = CollectionUtils.computeIfAbsent(fieldAccessCaches, beanType, FieldAccess::get);
try {
int fieldIndex = access.getIndex(field.getName());
return new ReflectAsmFieldInvoker.Getter(access, fieldIndex);
} catch (IllegalArgumentException e) {
return super.createGetterInvokerForField(propertyName, field);
}
}

/**
* Create {@link MethodInvoker} according to the specified method
*
* @param propertyName property name
* @param method getter method or setter method
* @return {@link MethodInvoker}
*/
@Override
protected MethodInvoker createInvokerForMethod(String propertyName, Method method) {
MethodAccess access = CollectionUtils.computeIfAbsent(methodAccessCaches, beanType, MethodAccess::get);
int methodIndex = access.getIndex(method.getName(), method.getParameterTypes());
return new ReflectAsmMethodInvoker(methodIndex, access);
}
}

/**
* {@link MethodInvoker} implementation based on {@link MethodAccess}
*
* @author huangchengxing
* @since 2.0.0
*/
@RequiredArgsConstructor
public static class ReflectAsmMethodInvoker implements MethodInvoker {
@@ -80,34 +128,6 @@ public Object invoke(@Nullable Object target, @Nullable Object... args) {
}
}

@Override
protected MethodInvoker createSetterInvokerForField(Class<?> targetType, String propertyName, Field field) {
if (Modifier.isPrivate(field.getModifiers())) {
return super.createSetterInvokerForField(targetType, propertyName, field);
}
FieldAccess access = CollectionUtils.computeIfAbsent(fieldAccessCaches, targetType, FieldAccess::get);
try {
int fieldIndex = access.getIndex(field.getName());
return new ReflectAsmFieldAdapterSetterInvoker(access, fieldIndex);
} catch (IllegalArgumentException e) {
return super.createSetterInvokerForField(targetType, propertyName, field);
}
}

@Override
protected MethodInvoker createGetterInvokerForField(Class<?> targetType, String propertyName, Field field) {
if (Modifier.isPrivate(field.getModifiers())) {
return super.createGetterInvokerForField(targetType, propertyName, field);
}
FieldAccess access = CollectionUtils.computeIfAbsent(fieldAccessCaches, targetType, FieldAccess::get);
try {
int fieldIndex = access.getIndex(field.getName());
return new ReflectAsmFieldAdapterGetterInvoker(access, fieldIndex);
} catch (IllegalArgumentException e) {
return super.createGetterInvokerForField(targetType, propertyName, field);
}
}

/**
* {@link MethodInvoker} implementation based on {@link FieldAccess}
*
@@ -129,43 +149,49 @@ public Object invoke(@Nullable Object target, @Nullable Object... args) {
return invoke(fieldAccess, fieldIndex, target, args);
}

/**
* Invoke the field access.
*
* @param fieldAccess field access
* @param fieldIndex field index
* @param target target
* @param args args
* @return result of invoke
*/
protected abstract Object invoke(FieldAccess fieldAccess, int fieldIndex, @Nullable Object target, @Nullable Object... args);
}

/**
* An implementation of the {@link ReflectAsmFieldInvoker} for getter.
*
* @author tangcent
* @since 2.0.0
*/
public static class ReflectAsmFieldAdapterGetterInvoker extends ReflectAsmFieldInvoker {

public ReflectAsmFieldAdapterGetterInvoker(FieldAccess fieldAccess, int fieldIndex) {
super(fieldAccess, fieldIndex);
/**
* An implementation of the {@link ReflectAsmFieldInvoker} for getter.
*
* @author tangcent
* @since 2.0.0
*/
public static class Getter extends ReflectAsmFieldInvoker {
public Getter(FieldAccess fieldAccess, int fieldIndex) {
super(fieldAccess, fieldIndex);
}
@Override
protected Object invoke(FieldAccess fieldAccess, int fieldIndex, @Nullable Object target, @Nullable Object... args) {
return fieldAccess.get(target, fieldIndex);
}
}

@Override
protected Object invoke(FieldAccess fieldAccess, int fieldIndex, @Nullable Object target, @Nullable Object... args) {
return fieldAccess.get(target, fieldIndex);
/**
* An implementation of the {@link ReflectAsmFieldInvoker} for setter.
*
* @author tangcent
* @since 2.0.0
*/
public static class Setter extends ReflectAsmFieldInvoker {
public Setter(FieldAccess fieldAccess, int fieldIndex) {
super(fieldAccess, fieldIndex);
}
@Override
protected Object invoke(FieldAccess fieldAccess, int fieldIndex, @Nullable Object target, @Nullable Object... args) {
fieldAccess.set(target, fieldIndex, args[0]);
return null;
}
}
}

/**
* An implementation of the {@link ReflectAsmFieldInvoker} for setter.
*
* @author tangcent
* @since 2.0.0
*/
public static class ReflectAsmFieldAdapterSetterInvoker extends ReflectAsmFieldInvoker {

public ReflectAsmFieldAdapterSetterInvoker(FieldAccess fieldAccess, int fieldIndex) {
super(fieldAccess, fieldIndex);
}

@Override
protected Object invoke(FieldAccess fieldAccess, int fieldIndex, @Nullable Object target, @Nullable Object... args) {
fieldAccess.set(target, fieldIndex, args[0]);
return null;
}
}
}
Loading

0 comments on commit d18606e

Please sign in to comment.