使用SerializedLambda獲取到方法引用的方法名

引子:MyBatisPlus的lambdaQuery,可以在構(gòu)造查詢條件時傳遞方法的引用,MyBatis能夠?qū)⒎椒ㄒ媒馕龀蔀橐樵兊腄B字段名,如下:

Wrappers.<Member>lambdaQuery().eq(Member::getMemberId, memberId);

解釋:構(gòu)建出的sql條件:member_id=1000L

上述方法傳入的是lambda表達式,拿到的卻是方法引用的方法名,并將駝峰命名法轉(zhuǎn)化為蛇形命名法。

借鑒

lambda表達式實現(xiàn)了Serializable接口:

@FunctionalInterface
public interface IGetter<T> extends Serializable {
    Object get(T source);
}
@FunctionalInterface
public interface ISetter<T, U> extends Serializable {
    void set(T t, U u);
}

工具類:

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.google.common.base.CaseFormat;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BeanUtils {
    private static Map<Class, SerializedLambda> CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>();

    /***
     * 轉(zhuǎn)換方法引用為屬性名
     * @param fn
     * @return
     */
    public static <T> String convertToFieldName(IGetter<T> fn) {
        SerializedLambda lambda = getSerializedLambda(fn);
        String methodName = lambda.getImplMethodName();
        String prefix = null;
        if (methodName.startsWith("get")) {
            prefix = "get";
        } else if (methodName.startsWith("is")) {
            prefix = "is";
        }
        if (prefix == null) {
            log.warn("無效的getter方法: " + methodName);
        }
        return toSnake(methodName.replace(prefix, ""));
    }

    public static <T, U> String convertToFieldName(ISetter<T, U> fn) {
        SerializedLambda lambda = getSerializedLambda(fn);
        String methodName = lambda.getImplMethodName();
        if (!methodName.startsWith("set")) {
            log.warn("無效的setter方法:" + methodName);
        }
        return toSnake(methodName.replace("set", ""));
    }

    /**
     * 關(guān)鍵在于這個方法
     */
    public static SerializedLambda getSerializedLambda(Serializable fn) {
        SerializedLambda lambda = CLASS_LAMDBA_CACHE.get(fn.getClass());
        if (lambda == null) {
            try {
                Method method = fn.getClass().getDeclaredMethod("writeReplace");
                method.setAccessible(Boolean.TRUE);
                lambda = (SerializedLambda) method.invoke(fn);
                CLASS_LAMDBA_CACHE.put(fn.getClass(), lambda);
            } catch (Exception e) {
                log.error("", e);
            }
        }
        return lambda;
    }

    private static String toSnake(String fieldName) {
        return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName);
    }
}

測試方法:

@Data
public class Person {
    private int id;
    private String name;
}
public class Test {

    public static void main(String[] args) {
        String getName = BeanUtils.convertToFieldName(Person::getId);
        System.out.println(getName);
        String setName = BeanUtils.convertToFieldName(Person::setName);
        System.out.println(setName);
    }
}

原理

如果一個函數(shù)式接口實現(xiàn)了Serializable接口,那么它的實例就會自動生成了一個返回SerializedLambda實例的 writeReplace方法,可以從SerializedLambda實例中獲取到這個函數(shù)式接口的運行時信息。這些運行時的信息就是 SerializedLambda的屬性:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容