Skip to content

Commit

Permalink
refactor: Refactor ReflectivePropertyOperator to support unencapsulat…
Browse files Browse the repository at this point in the history
…ed field
  • Loading branch information
tangcent authored and Createsequence committed Jul 30, 2023
1 parent cbfed7f commit f74da77
Show file tree
Hide file tree
Showing 8 changed files with 562 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import cn.crane4j.core.support.MethodInvoker;
import cn.crane4j.core.support.converter.ConverterManager;
import cn.crane4j.core.util.CollectionUtils;
import com.esotericsoftware.reflectasm.FieldAccess;
import com.esotericsoftware.reflectasm.MethodAccess;
import lombok.RequiredArgsConstructor;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

Expand All @@ -23,6 +26,11 @@ public class AsmReflectivePropertyOperator extends ReflectivePropertyOperator {
*/
private final Map<Class<?>, MethodAccess> methodAccessCaches = new ConcurrentHashMap<>();

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

/**
* Create an {@link AsmReflectivePropertyOperator} comparator
*
Expand Down Expand Up @@ -70,4 +78,84 @@ public Object invoke(@Nullable Object target, @Nullable Object... args) {
return methodAccess.invoke(target, methodIndex, args);
}
}

@Override
protected MethodInvoker createInvokerForSetter(Class<?> targetType, String propertyName, Field field) {
if (Modifier.isPrivate(field.getModifiers())) {
return super.createInvokerForSetter(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.createInvokerForSetter(targetType, propertyName, field);
}
}

@Override
protected MethodInvoker createInvokerForGetter(Class<?> targetType, String propertyName, Field field) {
if (Modifier.isPrivate(field.getModifiers())) {
return super.createInvokerForGetter(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.createInvokerForGetter(targetType, propertyName, field);
}
}

/**
* {@link MethodInvoker} implementation based on {@link FieldAccess}
*/
public static abstract class ReflectAsmFieldInvoker implements MethodInvoker {

private final FieldAccess fieldAccess;
private final int fieldIndex;

public ReflectAsmFieldInvoker(FieldAccess fieldAccess, int fieldIndex) {
this.fieldAccess = fieldAccess;
this.fieldIndex = fieldIndex;
}

@Override
public Object invoke(@Nullable Object target, @Nullable Object... args) {
return invoke(fieldAccess, fieldIndex, target, args);
}

protected abstract Object invoke(FieldAccess fieldAccess, int fieldIndex, @Nullable Object target, @Nullable Object... args);
}

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

public ReflectAsmFieldAdapterGetterInvoker(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);
}
}

/**
* An implementation of the {@link ReflectAsmFieldInvoker} for setter.
*/
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;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package cn.crane4j.core.support.reflect;

import cn.crane4j.core.support.MethodInvoker;
import cn.crane4j.core.util.ReflectUtils;
import lombok.RequiredArgsConstructor;

import java.lang.reflect.Field;

/**
* Adapter class that implements the {@link MethodInvoker} interface with fields
* without explicit getter and setter methods.
*
* @author tangcent
*/
@RequiredArgsConstructor
public abstract class ReflectiveFieldAdapterMethodInvoker implements MethodInvoker {

private final Field field;

/**
* Create a getter {@link ReflectiveFieldAdapterMethodInvoker} from the given field.
*
* @param field the field to access
* @return the FieldAdapterMethodInvoker for getting the field value
*/
public static ReflectiveFieldAdapterMethodInvoker createGetter(Field field) {
ReflectUtils.setAccessible(field);
return new ReflectiveFieldGetterInvoker(field);
}

/**
* Create a setter {@link ReflectiveFieldAdapterMethodInvoker} from the given field.
*
* @param field the field to access
* @return the FieldAdapterMethodInvoker for setting the field value
*/
public static ReflectiveFieldAdapterMethodInvoker createSetter(Field field) {
ReflectUtils.setAccessible(field);
return new ReflectiveFieldSetterInvoker(field);
}

@Override
public Object invoke(Object target, Object... args) {
return accessField(target, field, args);
}

/**
* Get or set the field value using reflection.
*
* @param target the object on which to invoke the method
* @param field the field to access
* @param args the arguments to pass to the method
* @return the result of the method invocation
*/
protected abstract Object accessField(Object target, Field field, Object... args);

/**
* An implementation of the {@link ReflectiveFieldAdapterMethodInvoker} for getter.
*/
private static class ReflectiveFieldGetterInvoker extends ReflectiveFieldAdapterMethodInvoker {

public ReflectiveFieldGetterInvoker(Field field) {
super(field);
}

/**
* Gets the value of the field using reflection.
*
* @param target the object on which to invoke the method
* @param field the field to access
* @param args the arguments to pass to the method (ignored)
* @return the value of the field
*/
@Override
protected Object accessField(Object target, Field field, Object... args) {
try {
return field.get(target);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Failed to access field: " + field.getName(), e);
}
}
}

/**
* An implementation of the {@link ReflectiveFieldAdapterMethodInvoker} for setter.
*/
private static class ReflectiveFieldSetterInvoker extends ReflectiveFieldAdapterMethodInvoker {

public ReflectiveFieldSetterInvoker(Field field) {
super(field);
}

/**
* Sets the value of the field using reflection.
*
* @param target the object on which to invoke the method
* @param field the field to access
* @param args the arguments to pass to the method (one argument, the value to set the field to)
* @return null (the result of the method invocation)
*/
@Override
protected Object accessField(Object target, Field field, Object... args) {
try {
field.set(target, args[0]);
return null;
} catch (IllegalAccessException e) {
throw new IllegalStateException("Failed to access field: " + field.getName(), e);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
import lombok.Setter;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;

/**
* A {@link PropertyOperator} abstract implementation class.
Expand Down Expand Up @@ -53,11 +52,19 @@ public ReflectivePropertyOperator(@Nullable ConverterManager converterManager) {
@Override
public MethodInvoker findGetter(Class<?> targetType, String propertyName) {
Method method = findGetterMethod(targetType, propertyName);
Asserts.isFalse(
Objects.isNull(method) && throwIfNoMatchedMethod,
"No getter method found for property [{}] in [{}] ", propertyName, targetType.getName()
);
return resolveInvoker(targetType, propertyName, method);
MethodInvoker methodInvoker = resolveInvoker(targetType, propertyName, method);
if (methodInvoker != null) {
return methodInvoker;
}

Field field = ReflectUtils.getField(targetType, propertyName);
MethodInvoker methodInvokerForGetter = resolveInvokerForGetter(targetType, propertyName, field);
if (methodInvokerForGetter != null) {
return methodInvokerForGetter;
}

Asserts.isFalse(throwIfNoMatchedMethod, "No getter method found for property [{}] in [{}] ", propertyName, targetType.getName());
return null;
}

/**
Expand All @@ -71,11 +78,19 @@ public MethodInvoker findGetter(Class<?> targetType, String propertyName) {
@Override
public MethodInvoker findSetter(Class<?> targetType, String propertyName) {
Method method = findSetterMethod(targetType, propertyName);
Asserts.isFalse(
Objects.isNull(method) && throwIfNoMatchedMethod,
"No setter method found for property [{}] in [{}] ", propertyName, targetType.getName()
);
return resolveInvoker(targetType, propertyName, method);
MethodInvoker methodInvoker = resolveInvoker(targetType, propertyName, method);
if (methodInvoker != null) {
return methodInvoker;
}

Field field = ReflectUtils.getField(targetType, propertyName);
MethodInvoker methodInvokerForSetter = resolveInvokerForSetter(targetType, propertyName, field);
if (methodInvokerForSetter != null) {
return methodInvokerForSetter;
}

Asserts.isFalse(throwIfNoMatchedMethod, "No setter method found for property [{}] in [{}] ", propertyName, targetType.getName());
return null;
}

/**
Expand All @@ -87,10 +102,14 @@ public MethodInvoker findSetter(Class<?> targetType, String propertyName) {
*/
@Nullable
protected Method findSetterMethod(Class<?> targetType, String propertyName) {
return Optional.ofNullable(ReflectUtils.getField(targetType, propertyName))
.map(field -> ReflectUtils.findSetterMethod(targetType, field))
.flatMap(Function.identity())
Field field = ReflectUtils.getField(targetType, propertyName);
if (Objects.isNull(field)) {
return ReflectUtils.findSetterMethod(targetType, propertyName)
.orElse(null);
} else {
return ReflectUtils.findSetterMethod(targetType, field)
.orElse(null);
}
}

/**
Expand All @@ -102,10 +121,14 @@ protected Method findSetterMethod(Class<?> targetType, String propertyName) {
*/
@Nullable
protected Method findGetterMethod(Class<?> targetType, String propertyName) {
return Optional.ofNullable(ReflectUtils.getField(targetType, propertyName))
.map(field -> ReflectUtils.findGetterMethod(targetType, field))
.flatMap(Function.identity())
Field field = ReflectUtils.getField(targetType, propertyName);
if (Objects.isNull(field)) {
return ReflectUtils.findGetterMethod(targetType, propertyName)
.orElse(null);
} else {
return ReflectUtils.findGetterMethod(targetType, field)
.orElse(null);
}
}

/**
Expand All @@ -121,6 +144,30 @@ protected MethodInvoker createInvoker(Class<?> targetType, String propertyName,
return ReflectiveMethodInvoker.create(null, method, false);
}

/**
* Creates a {@link MethodInvoker} for setting the value of the specified field.
*
* @param targetType target type
* @param propertyName property name
* @param field field to be set.
* @return The {@link MethodInvoker} instance for setting the value of the specified field.
*/
protected MethodInvoker createInvokerForSetter(Class<?> targetType, String propertyName, Field field) {
return ReflectiveFieldAdapterMethodInvoker.createSetter(field);
}

/**
* Creates a {@link MethodInvoker} for getting the value of the specified field.
*
* @param targetType target type
* @param propertyName property name
* @param field field to be got.
* @return The {@link MethodInvoker} instance for getting the value of the specified field.
*/
protected MethodInvoker createInvokerForGetter(Class<?> targetType, String propertyName, Field field) {
return ReflectiveFieldAdapterMethodInvoker.createGetter(field);
}

/**
* Resolve the invoker which finally to used.
*
Expand All @@ -145,4 +192,42 @@ protected final MethodInvoker resolveInvoker(Class<?> targetType, String propert
}
return ParameterConvertibleMethodInvoker.create(invoker, converterManager, method.getParameterTypes());
}

/**
* Resolve invoker which finally be used to set the value of the specified field.
*
* @param targetType target type
* @param propertyName property name
* @param field field to be set.
* @return the {@link MethodInvoker} instance for setting the value of the specified field.
*/
@Nullable
protected final MethodInvoker resolveInvokerForSetter(Class<?> targetType, String propertyName, @Nullable Field field) {
if (Objects.isNull(field)) {
return null;
}
MethodInvoker invoker = createInvokerForSetter(targetType, propertyName, field);
if (Objects.isNull(invoker)) {
return null;
}
if (Objects.isNull(converterManager)) {
return invoker;
}
return ParameterConvertibleMethodInvoker.create(invoker, converterManager, new Class[] {field.getType()});
}

/**
* Resolve invoker which finally be used to get the value of the specified field.
*
* @param targetType target type
* @param propertyName property name
* @param field field to be got.
* @return the {@link MethodInvoker} instance for getting the value of the specified field.
*/
protected final MethodInvoker resolveInvokerForGetter(Class<?> targetType, String propertyName, @Nullable Field field) {
if (Objects.isNull(field)) {
return null;
}
return createInvokerForGetter(targetType, propertyName, field);
}
}
Loading

0 comments on commit f74da77

Please sign in to comment.