最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java8中如何通過方法引用獲取屬性名詳解

 更新時間:2020年09月10日 08:39:17   作者:brucelwl  
這篇文章主要給大家介紹了關(guān)于Java8中如何通過方法引用獲取屬性名的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

在我們開發(fā)過程中常常有一個需求,就是要知道實(shí)體類中Getter方法對應(yīng)的屬性名稱(Field Name),例如實(shí)體類屬性到數(shù)據(jù)庫字段的映射,我們常常是硬編碼指定 屬性名,這種硬編碼有兩個缺點(diǎn)。

1、編碼效率低:因?yàn)橐簿幋a寫屬性名,很可能寫錯,需要非常小心,時間浪費(fèi)在了不必要的檢查上。

2、容易讓開發(fā)人員踩坑:例如有一天發(fā)現(xiàn)實(shí)體類中Field Name定義的不夠明確,希望換一個Field Name,那么代碼所有硬編碼的Field Name都要跟著變更,對于未并更的地方,是無法在編譯期發(fā)現(xiàn)的。只要有未變更的地方都可能導(dǎo)致bug的出現(xiàn)。

而使用了方法引用后,如果Field Name變更及其對應(yīng)的Getter/Setter方法變更,編譯器便可以實(shí)時的幫助我們檢查變更的代碼,在編譯器給出錯誤信息。

那么如何通過方法引用獲取Getter方法對應(yīng)的Field Name呢?

Java8中給我們提供了實(shí)現(xiàn)方式,首先要做的就是定義一個可序列化的函數(shù)式接口(實(shí)現(xiàn)Serializable),實(shí)現(xiàn)如下:

/**
 * Created by bruce on 2020/4/10 14:16
 */
@FunctionalInterface
public interface SerializableFunction<T, R> extends Function<T, R>, Serializable {
 
}

而在使用時,我們需要傳遞Getter方法引用

 //方法引用
SerializableFunction<People, String> getName1 = People::getName;
Field field = ReflectionUtil.getField(getName1);

下面看具體怎么解析這個SerializableFunction,完整實(shí)現(xiàn)如下ReflectionUtil

public class ReflectionUtil {
 
  private static Map<SerializableFunction<?, ?>, Field> cache = new ConcurrentHashMap<>();
 
  public static <T, R> String getFieldName(SerializableFunction<T, R> function) {
    Field field = ReflectionUtil.getField(function);
    return field.getName();
  }
  public static Field getField(SerializableFunction<?, ?> function) {
    return cache.computeIfAbsent(function, ReflectionUtil::findField);
  }
  public static Field findField(SerializableFunction<?, ?> function) {
    Field field = null;
    String fieldName = null;
    try {
      // 第1步 獲取SerializedLambda
      Method method = function.getClass().getDeclaredMethod("writeReplace");
      method.setAccessible(Boolean.TRUE);
      SerializedLambda serializedLambda = (SerializedLambda) method.invoke(function);
      // 第2步 implMethodName 即為Field對應(yīng)的Getter方法名
      String implMethodName = serializedLambda.getImplMethodName();
      if (implMethodName.startsWith("get") && implMethodName.length() > 3) {
        fieldName = Introspector.decapitalize(implMethodName.substring(3));
 
      } else if (implMethodName.startsWith("is") && implMethodName.length() > 2) {
        fieldName = Introspector.decapitalize(implMethodName.substring(2));
      } else if (implMethodName.startsWith("lambda$")) {
        throw new IllegalArgumentException("SerializableFunction不能傳遞lambda表達(dá)式,只能使用方法引用");
        
      } else {
        throw new IllegalArgumentException(implMethodName + "不是Getter方法引用");
      }
      // 第3步 獲取的Class是字符串,并且包名是“/”分割,需要替換成“.”,才能獲取到對應(yīng)的Class對象
      String declaredClass = serializedLambda.getImplClass().replace("/", ".");
      Class<?> aClass = Class.forName(declaredClass, false, ClassUtils.getDefaultClassLoader());
 
      // 第4步 Spring 中的反射工具類獲取Class中定義的Field
      field = ReflectionUtils.findField(aClass, fieldName);
 
    } catch (Exception e) {
      e.printStackTrace();
    }
    // 第5步 如果沒有找到對應(yīng)的字段應(yīng)該拋出異常
    if (field != null) {
      return field;
    }
    throw new NoSuchFieldError(fieldName);
  }
}

該類中主要有如下三個方法

  • String getFieldName(SerializableFunction<T, R> function) 獲取Field的字符串name
  • Field getField(SerializableFunction<?, ?> function)從緩存中查詢方法引用對應(yīng)的Field,如果沒有則通過findField(SerializableFunction<?, ?> function)方法反射獲取
  • Field findField(SerializableFunction<?, ?> function) 反射獲取方法應(yīng)用對應(yīng)的Field

實(shí)現(xiàn)原理

1、首先我們看最后一個方法Field findField(SerializableFunction<?, ?> function),該方法中第一步是通過SerializableFunction對象獲取Class,即傳遞的方法引用,然后反射獲取writeReplace()方法,并調(diào)用該方法獲取導(dǎo)SerializedLambda對象。

2、SerializedLambda是Java8中提供,主要就是用于封裝方法引用所對應(yīng)的信息,主要的就是方法名、定義方法的類名、創(chuàng)建方法引用所在類。

3、拿到這些信息后,便可以通過反射獲取對應(yīng)的Field。

4、而在方法Field getField(SerializableFunction<?, ?> function)中對獲取到的Field進(jìn)行緩存,避免每次都反射獲取,造成資源浪費(fèi)。

除此之外似乎還有一些值得思考的問題

writeReplace()方法是哪來的呢?

首先簡單了解一下java.io.Serializable接口,該接口很常見,我們在持久化一個對象或者在RPC框架之間通信使用JDK序列化時都會讓傳輸?shù)膶?shí)體類實(shí)現(xiàn)該接口,該接口是一個標(biāo)記接口沒有定義任何方法,但是該接口文檔中有這么一段描述:

Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:
   ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
  
This writeReplace method is invoked by serialization if the method exists and it would be accessible from a method defined within the class of the object being serialized. Thus, the method can have private, protected and package-private access. Subclass access to this method follows java accessibility rules.

概要意思就是說,如果想在序列化時改變序列化的對象,可以通過在實(shí)體類中定義任意訪問權(quán)限的Object writeReplace()來改變默認(rèn)序列化的對象。

那么我們的定義的SerializableFunction中并沒有定義writeReplace()方法,這個方法是哪來的呢?
代碼中SerializableFunction,Function只是一個接口,但是其在最后必定也是一個實(shí)現(xiàn)類的實(shí)例對象,而方法引用其實(shí)是在運(yùn)行時動態(tài)創(chuàng)建的,當(dāng)代碼執(zhí)行到方法引用時,如People::getName,最后會經(jīng)過
java.lang.invoke.LambdaMetafactory
java.lang.invoke.InnerClassLambdaMetafactory
去動態(tài)的創(chuàng)建實(shí)現(xiàn)類。而在動態(tài)創(chuàng)建實(shí)現(xiàn)類時則會判斷函數(shù)式接口是否實(shí)現(xiàn)了Serializable,如果實(shí)現(xiàn)了,則添加writeReplace()

值得注意的是,代碼中多次編寫的同一個方法引用,他們創(chuàng)建的是不同F(xiàn)unction實(shí)現(xiàn)類,即他們的Function實(shí)例對象也并不是同一個
我們可以通過如下屬性配置將 動態(tài)生成的Class保存到 磁盤上

java8中可以通過硬編碼

 System.setProperty("jdk.internal.lambda.dumpProxyClasses", ".");

jdk11 中只能使用jvm參數(shù)指定,硬編碼無效,原因是模塊化導(dǎo)致的

-Djdk.internal.lambda.dumpProxyClasses=.

示例代碼如下:

動態(tài)生成的Class如下:

一個方法引用創(chuàng)建一個實(shí)現(xiàn)類,他們是不同的對象,那么ReflectionUtil中將SerializableFunction最為緩存key還有意義嗎?

答案是肯定有意義的!??!因?yàn)橥环椒ㄖ械亩x的Function只會動態(tài)的創(chuàng)建一次實(shí)現(xiàn)類并只實(shí)例化一次,當(dāng)該方法被多次調(diào)用時即可走緩存中查詢該方法引用對應(yīng)的Field

這里的緩存Key應(yīng)該選用SerializableFunction#Class還是SerializableFunction實(shí)例對象好呢?

看到有些實(shí)現(xiàn)使用SerializableFunction的Class作為緩存key,代碼如下:

public static Field getField(SerializableFunction<?, ?> function) {
   //使用SerializableFunction的Class作為緩存key,導(dǎo)致每次都調(diào)用function.getClass()
   return cache.computeIfAbsent(function.getClass(), ReflectionUtil::findField);
}

但是個人建議采用SerializableFunction對象,因?yàn)闊o論方法被調(diào)用多少次,方法代碼塊內(nèi)的方法引用對象始終是同一個,如果采用其Class做為緩存key,每次查詢緩存時都需要調(diào)用native方法function.getClass()獲取其Class,也是一種資源損耗。

總結(jié):Java如何通過方法引用獲取屬性名實(shí)現(xiàn)及思考至此結(jié)束。直接使用ReflectionUtil即可

到此這篇關(guān)于Java8中如何通過方法引用獲取屬性名的文章就介紹到這了,更多相關(guān)Java8通過方法引用獲取屬性名內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyBatis-Plus更新對象時將字段值更新為null的四種常見方法

    MyBatis-Plus更新對象時將字段值更新為null的四種常見方法

    MyBatis-Plus 是一個 MyBatis 的增強(qiáng)工具,在簡化開發(fā)、提高效率方面表現(xiàn)非常出色,而,在使用 MyBatis-Plus 更新對象時,默認(rèn)情況下是不會將字段值更新為 null 的,如果你需要將某些字段的值更新為 null,有幾種方法可以實(shí)現(xiàn),本文將介紹幾種常見的方法
    2024-11-11
  • springboot如何添加task任務(wù)執(zhí)行隊(duì)列

    springboot如何添加task任務(wù)執(zhí)行隊(duì)列

    這篇文章主要介紹了springboot如何添加task任務(wù)執(zhí)行隊(duì)列問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • springboot redis使用lettuce配置多數(shù)據(jù)源的實(shí)現(xiàn)

    springboot redis使用lettuce配置多數(shù)據(jù)源的實(shí)現(xiàn)

    這篇文章主要介紹了springboot redis使用lettuce配置多數(shù)據(jù)源的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Java解析XML格式數(shù)據(jù)的方法詳解

    Java解析XML格式數(shù)據(jù)的方法詳解

    這篇文章主要介紹了Java解析XML格式數(shù)據(jù)的方法,并展示了較為常用的Java dom來解析XML的例子,需要的朋友可以參考下
    2015-10-10
  • SpringBoot自定義MessageConvert詳細(xì)講解

    SpringBoot自定義MessageConvert詳細(xì)講解

    正在學(xué)習(xí)SpringBoot,在自定義MessageConverter時發(fā)現(xiàn):為同一個返回值類型配置多個MessageConverter時,可能會發(fā)生響應(yīng)數(shù)據(jù)格式錯誤,或406異常(客戶端無法接收相應(yīng)數(shù)據(jù))。在此記錄一下解決問題以及追蹤源碼的過程
    2023-01-01
  • java Long==Long有趣的現(xiàn)象詳解

    java Long==Long有趣的現(xiàn)象詳解

    這篇文章主要給大家介紹了關(guān)于java Long==Long有趣的現(xiàn)象的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • itextpdf提取PDF文件中的任意頁碼實(shí)現(xiàn)示例

    itextpdf提取PDF文件中的任意頁碼實(shí)現(xiàn)示例

    這篇文章主要為大家介紹了itextpdf提取PDF文件中的任意頁碼實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Javaweb會話跟蹤技術(shù)Cookie和Session的具體使用

    Javaweb會話跟蹤技術(shù)Cookie和Session的具體使用

    本文主要介紹了Javaweb會話跟蹤技術(shù)Cookie&Session的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • 如何用java編寫微信小程序消息提醒推送

    如何用java編寫微信小程序消息提醒推送

    最近參與開發(fā)的項(xiàng)目有用到微信模板消息推送,在這離記錄一下,下面這篇文章主要給大家介紹了關(guān)于如何用java編寫微信小程序消息提醒推送的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • Java性能優(yōu)化之?dāng)?shù)據(jù)結(jié)構(gòu)實(shí)例代碼

    Java性能優(yōu)化之?dāng)?shù)據(jù)結(jié)構(gòu)實(shí)例代碼

    這篇文章主要介紹了Java性能優(yōu)化之?dāng)?shù)據(jù)結(jié)構(gòu)實(shí)例代碼,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01

最新評論

镇原县| 抚宁县| 上饶县| 昭觉县| 紫金县| 肇州县| 韩城市| 武陟县| 宾阳县| 建瓯市| 马鞍山市| 兴业县| 出国| 昌江| 金塔县| 巴彦县| 高邮市| 天镇县| 区。| 濉溪县| 宿州市| 维西| 桐梓县| 阳原县| 漳州市| 宾川县| 海兴县| 定陶县| 华容县| 察雅县| 扶风县| 平遥县| 北宁市| 新余市| 昌宁县| 襄垣县| 石景山区| 舞阳县| 封丘县| 长葛市| 溧阳市|