背景
有时候我们需要将属性名称作为方法参数进行传递,常见的方式就是定义一个string类型的形参,调用方法的时候传递实参名称,例如:
static String columnToDate(@Nonnull String column) {
return String.format("to_char(%s, 'yyyyMMdd')", column);
}
调用:
String str = CommonConstants.columnToDate("post_date");
上述方式最大的弊端就是将字段名写死在代码中,如果要修改字段名,则需要全局修改,为了便于维护,我们往往会将post_date定义为常量,调用方法时传递常量,这样修改字段名称的时候只需要修改常量值即可。
在java8之后我们可以通过lambda的方法引用来获取属性名,这样连定义常量的步骤都省了,mybatis-plus就是采用这种方式。
实现
- 定义支持序列化的Function
/**
* 支持序列化的 Function
*/
@FunctionalInterface
public interface SFunction<T, R> extends Function<T, R>, Serializable {
}
- 通过调用lambda表达式看不见的writeReplace方法获取返回值SerializedLambda继而获取到方法名称
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
/**
* @description: 获取属性名称
* @date 15:49 2024/10/8
* @param column 字段方法引用
* @return 属性名称
**/
@SneakyThrows
public static <R> String findFieldName(SFunction<R, ?> column) {
Method method = column.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(true);
SerializedLambda serializedLambda = (SerializedLambda) method.invoke(column);
String methodName = serializedLambda.getImplMethodName();
return methodToProperty(methodName);
}
/**
* @description: 从方法名获取属性名
* @date 15:48 2024/10/8
* @param methodName 方法名称
* @return 属性名称
**/
private static String methodToProperty(String methodName) {
if (methodName == null || methodName.isEmpty()) {
return "";
}
String name;
if (methodName.startsWith("is")) {
name = methodName.substring(2);
} else if (methodName.startsWith("get") || methodName.startsWith("set")) {
name = methodName.substring(3);
} else {
throw new RuntimeException("Error parsing property name '" + methodName + "'. Didn't start with getter/setter prefix!");
}
//首字母小写
if (name.length() == 1 || name.length() > 1 && Character.isLowerCase(name.charAt(1))) {
name = name.substring(0, 1).toLowerCase() + name.substring(1);
}
return name;
}
- 测试