[MyBatis源碼分析 - 反射器模塊 - 組件六] MetaClass

一省核、簡介

??MetaClass 通過對 ReflectorPropertyTokenizer 組合使用易迹,實(shí)現(xiàn)了對復(fù)雜的屬性表達(dá)式的解析砌们,并實(shí)現(xiàn)了獲取指定屬性描述信息的功能。


二旷赖、成員屬性

  private ReflectorFactory reflectorFactory;
  private Reflector reflector;
  • reflectorFactory:負(fù)責(zé)創(chuàng)建和緩存 Reflector 對象的工廠類。
  • reflector:傳入的 Class 對象對應(yīng)的 Reflector 對象探膊,該對象保存了該類相關(guān)的元信息杠愧。


三、構(gòu)造方法

  // 構(gòu)造方法逞壁,被靜態(tài)方法#forClass內(nèi)部調(diào)用
  private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
    this.reflectorFactory = reflectorFactory;
    this.reflector = reflectorFactory.findForClass(type);
  }

??這是一個(gè)私有構(gòu)造方法流济,構(gòu)造函數(shù)會(huì)被靜態(tài)內(nèi)部類 #forClass() 調(diào)用。


四腌闯、方法功能

1绳瘟、MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory)

【功能】創(chuàng)建指定類的 MetaClass 對象(創(chuàng)建 Reflector 對象的工廠對象可以用戶指定)。
【源碼與注解】

  // 根據(jù)傳入的Class對象和反射工廠對象創(chuàng)建對應(yīng)的MetaClass對象
  public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
    return new MetaClass(type, reflectorFactory);
  }


2姿骏、MetaClass metaClassForProperty(String name)

【功能】根據(jù)指定的類屬性名創(chuàng)建類屬性類型對應(yīng)的MataClass對象糖声。
【源碼與注解】

  // 根據(jù)類屬性名創(chuàng)建類屬性類型對應(yīng)的MataClass對象
  public MetaClass metaClassForProperty(String name) {
    // 從類的反射器對象中找到對應(yīng)的類屬性的類型
    Class<?> propType = reflector.getGetterType(name);
    // 創(chuàng)建屬性對應(yīng)的MetaClass對象
    return MetaClass.forClass(propType, reflectorFactory);
  }


3、String findProperty(String name)

【功能】根據(jù)表達(dá)式分瘦,獲得屬性
【源碼與注解】

  public String findProperty(String name) {
    // 將表達(dá)式委托給 #buildProperty 方法處理
    StringBuilder prop = buildProperty(name, new StringBuilder());
    return prop.length() > 0 ? prop.toString() : null;
  }


4蘸泻、StringBuilder buildProperty(String name, StringBuilder builder)

【功能】遞歸處理屬性表達(dá)式,并將處理結(jié)果返回給 #findProperty() 方法嘲玫。
【源碼與注解】

  private StringBuilder buildProperty(String name, StringBuilder builder) {
    PropertyTokenizer prop = new PropertyTokenizer(name);     // 解析屬性表達(dá)式
    // (1) 判斷是否有子表達(dá)式
    if (prop.hasNext()) {
      // (1.1) 查找 PropertyTokenizer.name 對應(yīng)的屬性
      String propertyName = reflector.findPropertyName(prop.getName());
      if (propertyName != null) {
        builder.append(propertyName);   // 追加屬性
        builder.append(".");
        // (1.2) 為該屬性創(chuàng)建對應(yīng)的MetaClass對象
        MetaClass metaProp = metaClassForProperty(propertyName);
        // (1.3) 遞歸解析 PropertyTokenizer.children 字段悦施,并將解析結(jié)果添加到 builder 中保存
        metaProp.buildProperty(prop.getChildren(), builder);
      }
    } else {
      // (2) 遞歸出口
      String propertyName = reflector.findPropertyName(name);
      if (propertyName != null) {
        builder.append(propertyName);
      }
    }
    return builder;
  }

??比如屬性表達(dá)式 order[0].items[0].name,第一次執(zhí)行方法時(shí)去团,解析到 propertyName 為 order抡诞,children 為 items[0].name,遞歸調(diào)用土陪;第二次調(diào)用 propertyName 為 items昼汗,children 為 name,遞歸調(diào)用鬼雀;第三次進(jìn)入遞歸出口分支顷窒,propertyName 為 name;所以最后返回的 builder 為 order.items.name取刃。


5蹋肮、String findProperty(String name, boolean useCamelCaseMapping)

【功能】在 #findProperty(String) 的基礎(chǔ)上增加了是否使用駝峰轉(zhuǎn)換的標(biāo)志位,如果屬性名是按照駝峰的規(guī)則璧疗,則先去除掉下劃線坯辩。
【源碼】

  public String findProperty(String name, boolean useCamelCaseMapping) {
    if (useCamelCaseMapping) {
      name = name.replace("_", "");
    }
    return findProperty(name);
  }


6、Class<?> getSetterType(String name)

【功能】獲取屬性表達(dá)式中的屬性對應(yīng) setter 方法的類型崩侠。
【源碼與注解】

  public Class<?> getSetterType(String name) {
    // 解析屬性表達(dá)式
    PropertyTokenizer prop = new PropertyTokenizer(name);
    // 判斷是否存在待處理的子表達(dá)式
    if (prop.hasNext()) {
      // 獲取頂層屬性對應(yīng)的 MetaClass 對象漆魔,遞歸調(diào)用
      MetaClass metaProp = metaClassForProperty(prop.getName());
      return metaProp.getSetterType(prop.getChildren());
    } else {
      // 不存在子表達(dá)式,直接根據(jù)屬性名從 reflector 中獲取屬性 setter 類型
      return reflector.getSetterType(prop.getName());
    }
  }


7、Class<?> getGetterType(String name)

【功能】獲取屬性表達(dá)式中的屬性對應(yīng) getter 方法的類型改抡。
【源碼】

  public Class<?> getGetterType(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaClass metaProp = metaClassForProperty(prop);
      return metaProp.getGetterType(prop.getChildren());
    }
    // issue #506. Resolve the type inside a Collection Object
    return getGetterType(prop);
  }

【解析】
??上面部分代碼的處理跟 #getSetterType() 方法的處理類似矢炼,當(dāng)解析到屬性表達(dá)式?jīng)]有子表達(dá)式時(shí),遞歸出口調(diào)用 #getGetterType(PropertyTokenizer) 方法處理阿纤,該方法主要用來處理要獲取的屬性是在集合中的情況如 order['id'](但這里其實(shí)我也有點(diǎn)疑惑句灌,為什么 #getSetterType() 方法就不用這樣處理呢?)


8欠拾、Class<?> getGetterType(PropertyTokenizer prop)

【功能】解析帶泛型的 getterType 的真正類型
【源碼與注解】

  private Class<?> getGetterType(PropertyTokenizer prop) {
    // 獲取表達(dá)式中頂層屬性的類型
    Class<?> type = reflector.getGetterType(prop.getName());
    // 如果表達(dá)式中使用了下標(biāo)胰锌,并且頂層屬性的是 Collection 的子類
    if (prop.getIndex() != null && Collection.class.isAssignableFrom(type)) {
      // 通過 TypeParameterResolver 工具類解析屬性的類型
      Type returnType = getGenericGetterType(prop.getName());
      // 針對 ParameterizedType 進(jìn)行處理,即針對泛型集合類型進(jìn)行處理
      if (returnType instanceof ParameterizedType) {
        Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();   // 獲取實(shí)際的類型參數(shù)
        if (actualTypeArguments != null && actualTypeArguments.length == 1) {   // 過濾掉多個(gè)實(shí)際參數(shù)類型的情況藐窄,是因?yàn)楸磉_(dá)式表示的屬性類型资昧,是對應(yīng)一個(gè)參數(shù)類型的集合
          returnType = actualTypeArguments[0];    // 獲取泛型的類型
          if (returnType instanceof Class) {
            type = (Class<?>) returnType;
          } else if (returnType instanceof ParameterizedType) {
            type = (Class<?>) ((ParameterizedType) returnType).getRawType();
          }
        }
      }
    }
    // 如果沒使用下標(biāo),或者解析完畢荆忍,返回對應(yīng)的類型
    return type;
  }

【解析】
??比如 order[0] 沒有子表達(dá)式格带,會(huì)調(diào)用本方法處理,假設(shè) order 屬性的類型為 List<String>刹枉,則 order[0] 表示列表中的第一個(gè)元素叽唱,其屬性類型應(yīng)為 String。


9微宝、Type getGenericGetterType(String propertyName)

【功能】獲取屬性對應(yīng)泛型類型中的泛型的具體類型
【源碼與注解】

  private Type getGenericGetterType(String propertyName) {
    try {
      // 獲取屬性對應(yīng)的 getInvoker
      Invoker invoker = reflector.getGetInvoker(propertyName);
      // 如果 invoker 是一個(gè) MethodInvoker尔觉,即有對應(yīng) getter,獲取對應(yīng)的 Method
      // 并調(diào)用 TypeParameterResolver.resolveReturnType 處理
      if (invoker instanceof MethodInvoker) {
        Field _method = MethodInvoker.class.getDeclaredField("method");
        _method.setAccessible(true);
        Method method = (Method) _method.get(invoker);
        return TypeParameterResolver.resolveReturnType(method, reflector.getType());
      // 如果 invoker 是一個(gè) GetFieldInvoker芥吟,即 invoker 中封裝的是一個(gè) Field,則獲取對應(yīng)的 Field
      // 并調(diào)用 TypeParameterResolver.resolveFieldType 處理
      } else if (invoker instanceof GetFieldInvoker) {
        Field _field = GetFieldInvoker.class.getDeclaredField("field");
        _field.setAccessible(true);
        Field field = (Field) _field.get(invoker);
        return TypeParameterResolver.resolveFieldType(field, reflector.getType());
      }
    } catch (NoSuchFieldException e) {
    } catch (IllegalAccessException e) {
    }
    return null;
  }


10专甩、MetaClass metaClassForProperty(PropertyTokenizer prop)

【功能】根據(jù)當(dāng)前類的一個(gè)屬性表達(dá)式钟鸵,創(chuàng)建表達(dá)式中表示的屬性對應(yīng)的 MetaClass 對象。
【源碼與注解】

  private MetaClass metaClassForProperty(PropertyTokenizer prop) {
    // 獲取表達(dá)式中屬性的真正類型
    Class<?> propType = getGetterType(prop);
    // 創(chuàng)建 MetaClass 對象
    return MetaClass.forClass(propType, reflectorFactory);
  }


11涤躲、boolean hasSetter(String name)

【功能】判斷表達(dá)式對應(yīng)的屬性是否允許設(shè)置值棺耍。
【源碼與注解】

public boolean hasSetter(String name) {
  PropertyTokenizer prop = new PropertyTokenizer(name);   // 解析屬性表達(dá)式
  if (prop.hasNext()) {   // 判斷是否有子表達(dá)式
    // 如果類中并沒有頂層屬性對應(yīng)的 setter,直接返回false种樱,若有則遞歸解析表達(dá)式并且遞歸調(diào)用本方法
    if (reflector.hasSetter(prop.getName())) {
      MetaClass metaProp = metaClassForProperty(prop.getName());
      return metaProp.hasSetter(prop.getChildren());
    } else {
      return false;
    }
  } else {
    // 如果沒有子表達(dá)式蒙袍,或者遞歸的出口,根據(jù)屬性名調(diào)用反射器的方法返回
    return reflector.hasSetter(prop.getName());
  }
}


12嫩挤、boolean hasGetter(String name)

【功能】判斷表達(dá)式對應(yīng)的屬性是否允許獲取值害幅。
【源碼與注解】跟 #hasSetter() 類型,不贅述岂昭。

  public boolean hasGetter(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      if (reflector.hasGetter(prop.getName())) {
        MetaClass metaProp = metaClassForProperty(prop);
        return metaProp.hasGetter(prop.getChildren());
      } else {
        return false;
      }
    } else {
      return reflector.hasGetter(prop.getName());
    }
  }


13以现、其他方法

【功能】代理調(diào)用 Reflector 的方法。

  • (1)#String[] getGetterNames():調(diào)用 reflector.getGetablePropertyNames() 獲取可讀屬性。
  • (2)#String[] getSetterNames():調(diào)用 reflector.getSetablePropertyNames() 獲取可寫屬性邑遏。
  • (3)#Invoker getGetInvoker(String name):調(diào)用 reflector.getGetInvoker(name) 獲取讀取屬性的調(diào)用器佣赖。
  • (4)#Invoker getSetInvoker(String name):調(diào)用 reflector.getSetInvoker(name) 獲取寫屬性的調(diào)用器。
  • (5)#boolean hasDefaultConstructor():調(diào)用 reflector.hasDefaultConstructor() 判斷類是否有默認(rèn)構(gòu)造方法记盒。


五憎蛤、測試案例

1、案例1

(1)類定義

// 泛型接口
public interface GenericInterface<K> {
  void setId(K id);
}
// 泛型抽象類
public abstract class GenericAbstract<K> {
  public abstract K getId();
}
// 繼承泛型抽象類并具體化類型的抽象類
public abstract class GenericSubclass extends GenericAbstract<Long> {
  @Override
  public abstract Long getId();
}
// 實(shí)現(xiàn)泛型接口和繼承泛型抽象類的類
public class GenericConcrete extends GenericSubclass implements GenericInterface<Long> {
  private Long id;

  @Override
  public Long getId() {
    return id;
  }

  public void setId(String id) {
    this.id = Long.valueOf(id);
  }

  @Override
  public void setId(Long id) {
    this.id = id;
  }

  public void setId(Integer id) {
    this.id = (long) id;
  }
}

(2)測試代碼

  @Test
  public void shouldTestDataTypeOfGenericMethod() {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    MetaClass meta = MetaClass.forClass(GenericConcrete.class, reflectorFactory);
    assertEquals(Long.class, meta.getGetterType("id"));
    assertEquals(Long.class, meta.getSetterType("id"));
  }

(3)執(zhí)行結(jié)果



2纪吮、案例2

(1)類定義

public class RichType {

  private RichType richType;

  private String richField;

  private String richProperty;

  private Map richMap = new HashMap();

  private List richList = new ArrayList() {
    {
      add("bar");
    }
  };

  public RichType getRichType() {
    return richType;
  }

  public void setRichType(RichType richType) {
    this.richType = richType;
  }

  public String getRichProperty() {
    return richProperty;
  }

  public void setRichProperty(String richProperty) {
    this.richProperty = richProperty;
  }

  public List getRichList() {
    return richList;
  }

  public void setRichList(List richList) {
    this.richList = richList;
  }

  public Map getRichMap() {
    return richMap;
  }

  public void setRichMap(Map richMap) {
    this.richMap = richMap;
  }
}

(2)測試代碼

  // 測試 hasGetter 方法
  @Test
  public void shouldCheckGetterExistance() {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
    assertTrue(meta.hasGetter("richField"));
    assertTrue(meta.hasGetter("richProperty"));
    assertTrue(meta.hasGetter("richList"));
    assertTrue(meta.hasGetter("richMap"));
    assertTrue(meta.hasGetter("richList[0]"));

    assertTrue(meta.hasGetter("richType"));
    assertTrue(meta.hasGetter("richType.richField"));
    assertTrue(meta.hasGetter("richType.richProperty"));
    assertTrue(meta.hasGetter("richType.richList"));
    assertTrue(meta.hasGetter("richType.richMap"));
    assertTrue(meta.hasGetter("richType.richList[0]"));

    assertFalse(meta.hasGetter("[0]"));
  }

  // 測試 hasSetter 方法
  @Test
  public void shouldCheckSetterExistance() {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
    assertTrue(meta.hasSetter("richField"));
    assertTrue(meta.hasSetter("richProperty"));
    assertTrue(meta.hasSetter("richList"));
    assertTrue(meta.hasSetter("richMap"));
    assertTrue(meta.hasSetter("richList[0]"));

    assertTrue(meta.hasSetter("richType"));
    assertTrue(meta.hasSetter("richType.richField"));
    assertTrue(meta.hasSetter("richType.richProperty"));
    assertTrue(meta.hasSetter("richType.richList"));
    assertTrue(meta.hasSetter("richType.richMap"));
    assertTrue(meta.hasSetter("richType.richList[0]"));

    assertFalse(meta.hasSetter("[0]"));
  }

  // 測試 getGetterType 方法
  @Test
  public void shouldCheckTypeForEachGetter() {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
    assertEquals(String.class, meta.getGetterType("richField"));
    assertEquals(String.class, meta.getGetterType("richProperty"));
    assertEquals(List.class, meta.getGetterType("richList"));
    assertEquals(Map.class, meta.getGetterType("richMap"));
    assertEquals(List.class, meta.getGetterType("richList[0]"));

    assertEquals(RichType.class, meta.getGetterType("richType"));
    assertEquals(String.class, meta.getGetterType("richType.richField"));
    assertEquals(String.class, meta.getGetterType("richType.richProperty"));
    assertEquals(List.class, meta.getGetterType("richType.richList"));
    assertEquals(Map.class, meta.getGetterType("richType.richMap"));
    assertEquals(List.class, meta.getGetterType("richType.richList[0]"));
  }

  // 測試 getSetterType 方法
  @Test
  public void shouldCheckTypeForEachSetter() {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
    assertEquals(String.class, meta.getSetterType("richField"));
    assertEquals(String.class, meta.getSetterType("richProperty"));
    assertEquals(List.class, meta.getSetterType("richList"));
    assertEquals(Map.class, meta.getSetterType("richMap"));
    assertEquals(List.class, meta.getSetterType("richList[0]"));

    assertEquals(RichType.class, meta.getSetterType("richType"));
    assertEquals(String.class, meta.getSetterType("richType.richField"));
    assertEquals(String.class, meta.getSetterType("richType.richProperty"));
    assertEquals(List.class, meta.getSetterType("richType.richList"));
    assertEquals(Map.class, meta.getSetterType("richType.richMap"));
    assertEquals(List.class, meta.getSetterType("richType.richList[0]"));
  }

  // 測試 getGetterNames 和 getSetterNames 方法
  @Test
  public void shouldCheckGetterAndSetterNames() {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
    assertEquals(5, meta.getGetterNames().length);
    assertEquals(5, meta.getSetterNames().length);
  }

  // 測試 findProperty 方法
  @Test
  public void shouldFindPropertyName() {
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
    assertEquals("richField", meta.findProperty("RICHfield"));
  }

(3)執(zhí)行結(jié)果

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俩檬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子彬碱,更是在濱河造成了極大的恐慌豆胸,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巷疼,死亡現(xiàn)場離奇詭異晚胡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嚼沿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門估盘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人骡尽,你說我怎么就攤上這事遣妥。” “怎么了攀细?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵箫踩,是天一觀的道長。 經(jīng)常有香客問我谭贪,道長境钟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任俭识,我火速辦了婚禮慨削,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘套媚。我一直安慰自己缚态,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布堤瘤。 她就那樣靜靜地躺著玫芦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪本辐。 梳的紋絲不亂的頭發(fā)上姨俩,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天蘸拔,我揣著相機(jī)與錄音,去河邊找鬼环葵。 笑死调窍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的张遭。 我是一名探鬼主播邓萨,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼菊卷!你這毒婦竟也來了缔恳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤洁闰,失蹤者是張志新(化名)和其女友劉穎歉甚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扑眉,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纸泄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腰素。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片聘裁。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖弓千,靈堂內(nèi)的尸體忽然破棺而出衡便,到底是詐尸還是另有隱情,我是刑警寧澤洋访,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布镣陕,位于F島的核電站,受9級特大地震影響姻政,放射性物質(zhì)發(fā)生泄漏茁彭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一扶歪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧摄闸,春花似錦善镰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至熏兄,卻和暖如春品洛,著一層夾襖步出監(jiān)牢的瞬間树姨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工桥状, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帽揪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓辅斟,卻偏偏與公主長得像转晰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子士飒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355