SpringBoot 配置提示功能

提示

目的

配置自動提示的輔助功能可以讓配置寫起來更快,準確率大大提高性雄。

springboot jar 包含提供所有支持的配置屬性細節(jié)的元數(shù)據(jù)文件留拾。文件的目的是為了讓 IDE 開發(fā)者在用戶使用 application.propertiesapplication.yml 文件時提供上下文幫助和代碼補全。
大多數(shù)元數(shù)據(jù)文件是在編譯時通過處理用 @ConfigurationProperties 注釋的所有項自動生成的泞辐。也可以手動編寫部分元數(shù)據(jù)笔横。

版本

參考 SpringBoot 2.2.0.RELEASE 文檔

文件

jar包中的 META-INF/spring-configuration-metadata.json (自動生成)或 META-INF/additional-spring-configuration-metadata.json (手動添加)

實戰(zhàn)

<!-- 引入相關依賴 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>
@Configuration
@ConfigurationProperties(prefix = "file.upload")
public class FileUploadConfig {
  /** Maximum number of bytes per file */
  private String maxSize = "1024M";

  /** 不允許的文件后綴 */
  private String rejectSuffix;
  //注意:使用的時候必須要有getter/setter,否則不會自動生成該屬性對應的提示
  //此處因為篇幅原因省略 getter/setter
}
@Configuration
@ConfigurationProperties("map.test")
public class MapTestConfig {
  /** 測試Map類型數(shù)據(jù)的提示 */
  private Map<String, Object> data;
  //注意:使用的時候必須要有getter/setter咐吼,否則不會自動生成該屬性對應的提示
  //此處因為篇幅原因省略 getter/setter
}

中文注釋會亂碼吹缔,以上故意用中文注釋的地方,會在下面文件中指定對應的描述锯茄,看是否會覆蓋厢塘。

additional-spring-configuration-metadata.json

{
  "properties": [
    {
      "name": "file.upload.reject-suffix",
      "type": "java.lang.String",
      "defaultValue": "exe,jar",
      "description": "The file suffix is not allowed.",
      "sourceType": "com.lw.metadata.config.FileUploadConfig"
    },
    {
      "name": "map.test.data",
      "type": "java.util.Map",
      "description": "Tips for testing Map type data.",
      "sourceType": "com.lw.metadata.config.MapTestConfig"
    }
  ],
  "hints": [
    {
      "name": "map.test.data.keys",
      "values": [
        {
          "value": "name",
          "description": "The name of the person."
        },
        {
          "value": "sex",
          "description": "The sex of the person."
        }
      ]
    }
  ]
}

maven compile 之后,生成的 additional-spring-configuration-metadata.json 與源碼中的一樣肌幽,生成的 spring-configuration-metadata.json 如下:

{
  "groups": [
    {
      "name": "file.upload",
      "type": "com.lw.metadata.config.FileUploadConfig",
      "sourceType": "com.lw.metadata.config.FileUploadConfig"
    },
    {
      "name": "map.test",
      "type": "com.lw.metadata.config.MapTestConfig",
      "sourceType": "com.lw.metadata.config.MapTestConfig"
    }
  ],
  "properties": [
    {
      "name": "file.upload.max-size",
      "type": "java.lang.String",
      "description": "Maximum number of bytes per file",
      "sourceType": "com.lw.metadata.config.FileUploadConfig",
      "defaultValue": "1024M"
    },
    {
      "name": "file.upload.reject-suffix",
      "type": "java.lang.String",
      "description": "The file suffix is not allowed.",
      "sourceType": "com.lw.metadata.config.FileUploadConfig",
      "defaultValue": "exe,jar"
    },
    {
      "name": "map.test.data",
      "type": "java.util.Map<java.lang.String,java.lang.Object>",
      "description": "Tips for testing Map type data.",
      "sourceType": "com.lw.metadata.config.MapTestConfig"
    }
  ],
  "hints": [
    {
      "name": "map.test.data.keys",
      "values": [
        {
          "value": "name",
          "description": "The name of the person."
        },
        {
          "value": "sex",
          "description": "The sex of the person."
        }
      ]
    }
  ]
}

效果

SpringBoot配置提示效果

由此可以看到以下現(xiàn)象:

  • 代碼中的默認值會自動生成到提示文件中晚碾,如:FileUploadConfig#maxSize
  • 代碼中的注釋會自動生成到提示文件中,如:FileUploadConfig#maxSize
  • additional-spring-configuration-metadata.json 文件中存在的提示會覆蓋自動生成的對應屬性牍颈,若自動生成的沒有此屬性則自動增加迄薄。

手動寫提示文件

示例

{
    "groups": [
        {
            "name": "server",
            "type": "org.springframework.boot.autoconfigure.web.ServerProperties",
            "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
        },
        {
            "name": "spring.jpa.hibernate",
            "type": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties$Hibernate",
            "sourceType": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties",
            "sourceMethod": "getHibernate()"
        }
    ],
    "properties": [
        {
            "name": "server.port",
            "type": "java.lang.Integer",
            "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
        },
        {
            "name": "server.address",
            "type": "java.net.InetAddress",
            "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
        },
        {
              "name": "spring.jpa.hibernate.ddl-auto",
              "type": "java.lang.String",
              "description": "DDL mode. This is actually a shortcut for the \"hibernate.hbm2ddl.auto\" property.",
              "sourceType": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties$Hibernate"
        }
    ],
    "hints": [
        {
            "name": "spring.jpa.hibernate.ddl-auto",
            "values": [
                {
                    "value": "none",
                    "description": "Disable DDL handling."
                },
                {
                    "value": "validate",
                    "description": "Validate the schema, make no changes to the database."
                },
                {
                    "value": "update",
                    "description": "Update the schema if necessary."
                },
                {
                    "value": "create",
                    "description": "Create the schema and destroy previous data."
                },
                {
                    "value": "create-drop",
                    "description": "Create and then destroy the schema at the end of the session."
                }
            ]
        }
    ]
}

groups

分組,將配置類分組煮岁。
可以按照文件來分組讥蔽,即:將同一個配置文件的所有屬性放在同一個組

屬性 類型 是否必須 用途
name String Y 分組的完整名稱
type String N 分組數(shù)據(jù)類型的類名(如:使用@ConfigurationProperties注釋的完整類名、使用@Bean注釋的方法返回類型)
description String N 分組的簡短描述画机。
sourceType String N 提供分組來源的類名冶伞。
sourceMethod String N 提供分組的方法,包含括號和參數(shù)類型步氏。

properties

提示主體响禽,必須

屬性 類型 是否必須 用途
name String Y 屬性的完整名稱。名稱采用小寫句點分隔格式,如:server.address
type String N 屬性數(shù)據(jù)類型的完整簽名(如:java.lang.String)或完整的泛型類型(如:java.util.Map<java.util.String,acme.Myenum>)芋类。此屬性提示用戶輸入值得類型隆嗅。原生類型在此處使用其包裝類型(如:boolean使用java.lang.Boolean)。
description String N 分組的簡短描述侯繁。
sourceType String N 提供分組來源的類名胖喳。
defaultValue Object N 默認值。當屬性為指定時使用贮竟。
deprecation Deprecation N 指定屬性是否已棄用丽焊。

deprecation屬性如下:

屬性 類型 是否必須 用途
level String N 棄用級別,可以是 warning(默認值) 或 error咕别。warning:屬性應該仍然可以使用技健;error:屬性不保證可以使用
reason String N 屬性棄用的簡短原因。
replacement String N 替換此棄用屬性的新屬性全名惰拱。可為空

注意:Spring Boot 1.3 版本之前雌贱,是使用 boolean 類型的 deprecated

以下示例來源于官方文檔弓颈,展示了如何處理這種場景:

@ConfigurationProperties("app.acme")
public class AcmeProperties {

    private String name;

    public String getName() { ... }

    public void setName(String name) { ... }

    @DeprecatedConfigurationProperty(replacement = "app.acme.name")
    @Deprecated
    public String getTarget() {
        return getName();
    }

    @Deprecated
    public void setTarget(String target) {
        setName(target);
    }
}

一旦 getTargetsetTarget 方法從公共 API 中刪除帽芽,元數(shù)據(jù)中的自動棄用提示也會消失删掀。 如果要保留提示翔冀,則添加具有 error 棄用級別的手動元數(shù)據(jù)可以確保用戶仍然了解該屬性。在提供替代品時披泪,這樣做特別有用纤子。

hints

輔助提示,非必須

屬性 類型 是否必須 用途
name String Y 提示關聯(lián)的屬性的完整名稱款票。名稱是小寫句點分隔格式(如:spring.mvc.servlet.path)控硼,如果屬性關聯(lián)map類型(如:system.contexts),提示可以關聯(lián)map的鍵(system.contexts.keys)或者值(system.contexts.values)艾少。
values ValueHint[] N 有效值集合卡乾。(下表詳述)
providers ValueProvider[] N 提供者集合。(下表詳述)

values 屬性如下:

屬性 類型 是否必須 用途
value Object Y 提示引用元素的有效值缚够。如果屬性是數(shù)組幔妨,value和description也可以是數(shù)組。
description String N value 對應的簡短描述

ValueHint

對于Map類型的支持如下:

@ConfigurationProperties("sample")
public class SampleProperties {

    private Map<String,Integer> contexts;
    // getters and setters
}
{"hints": [
    {
        "name": "sample.contexts.keys",
        "values": [
            {
                "value": "sample1"
            },
            {
                "value": "sample2"
            }
        ]
    }
]}

提示是對Map內(nèi)每一對 key-value 的提示谍椅。

.keys.values 前綴必須分別關聯(lián) Map 的 keys 和 values误堡。

providers 屬性如下:

屬性 類型 是否必須 用途
name String N 用于為提示所引用的元素提供其他內(nèi)容幫助的 provider 的名稱。
parameters JSON object N provider 所支持的任何其他參數(shù)(有關詳細信息雏吭,請查看 provider 的文檔)锁施。

ValueProvider

一般用不到,建議跳過

下表總結(jié)了支持的 providers 列表:

屬性 描述
any 允許提供任何附加值。
class-reference 自動完成項目中可用的類悉抵。通常由目標參數(shù)指定的基類約束肩狂。
handle-as 處理屬性,就像它是由必須的 target 參數(shù)定義的類型定義的一樣姥饰。
logger-name 自動完成有效的記錄器名稱和記錄器組婚温。通常,當前項目中可用的包和類名可以自動完成媳否,也可以定義組栅螟。
spring-bean-reference 自動完成當前項目中可用的bean名稱。通常由 target 參數(shù)指定的基類約束篱竭。
spring-profile-name 自動完成項目中可用的 spring 配置文件名稱力图。

any

符合屬性類型的所有值。

{"hints": [
    {
        "name": "system.state",
        "values": [
            {
                "value": "on"
            },
            {
                "value": "off"
            }
        ],
        "providers": [
            {
                "name": "any"
            }
        ]
    }
]}

class-reference

提供以下參數(shù):

參數(shù) 類型 默認值 描述
target String(Class) 分配給值的類的全限定類名掺逼。通常用于篩選非候選類吃媒。
concrete boolean true 指定是否僅將具體類視為有效候選。
{"hints": [
    {
        "name": "server.servlet.jsp.class-name",
        "providers": [
            {
                "name": "class-reference",
                "parameters": {
                    "target": "javax.servlet.http.HttpServlet"
                }
            }
        ]
    }
]}

handle-as

允許您將屬性的類型替換為更高級的類型吕喘。

這通常在屬性具有 java.lang.String 類型時發(fā)生赘那,因為您不希望配置類依賴于不在類路徑上的類。

參數(shù) 類型 默認值 描述
target String(Class) Y 為屬性考慮的類型的完全限定名氯质。

可用的值如下:

  • 任何 java.lang.Enum: 列出屬性的可能值募舟。
  • java.nio.charset.Charset: 支持自動完成字符集/編碼值(如 utf-8
  • java.util.Locale:自動完成區(qū)域設置(如:en_US)
  • org.springframework.util.MimeType:支持自動完成 content-type 值(如:text/plain
  • org.springframework.core.io.Resource: 支持自動完成spring的資源抽象以引用文件系統(tǒng)或類路徑上的文件 (如:classpath:/sample.properties

注意:如果要提供多個值,用 Collection 或 數(shù)組類型

{"hints": [
    {
        "name": "spring.liquibase.change-log",
        "providers": [
            {
                "name": "handle-as",
                "parameters": {
                    "target": "org.springframework.core.io.Resource"
                }
            }
        ]
    }
]}

logger-name

logger-name provider 自動完成有效的記錄器名稱和記錄器組闻察。 通常拱礁,當前項目中可用的包和類名可以自動完成。 如果組已啟用(默認)辕漂,并且配置中標識了自定義記錄器組呢灶,則應提供該組的自動完成。

支持以下參數(shù):

參數(shù) 類型 默認值 描述
group boolean true 指定是否應考慮已知組钉嘹。

由于記錄器名稱可以是任意名稱鸯乃,此 provider 應允許任何值,但可以突出顯示項目的類路徑中不可用的有效包和類名跋涣。

以下是 logging.level 屬性缨睡。keys 是 logger 名,values 關聯(lián)標準的 log levels 或 自定義的 level仆潮,

{"hints": [
    {
        "name": "logging.level.keys",
        "values": [
            {
                "value": "root",
                "description": "Root logger used to assign the default logging level."
            },
            {
                "value": "sql",
                "description": "SQL logging group including Hibernate SQL logger."
            },
            {
                "value": "web",
                "description": "Web logging group including codecs."
            }
        ],
        "providers": [
            {
                "name": "logger-name"
            }
        ]
    },
    {
        "name": "logging.level.values",
        "values": [
            {
                "value": "trace"
            },
            {
                "value": "debug"
            },
            {
                "value": "info"
            },
            {
                "value": "warn"
            },
            {
                "value": "error"
            },
            {
                "value": "fatal"
            },
            {
                "value": "off"
            }

        ],
        "providers": [
            {
                "name": "any"
            }
        ]
    }
]}

spring-bean-reference

此 provider 自動完成在當前項目的配置中定義的bean宏蛉。 支持以下參數(shù):

參數(shù) 類型 默認值 描述
target String(Class) 應該分配給候選對象的bean類的完全限定名。通常用于篩選非候選bean性置。

以下示例表示:spring.jmx.server 屬性定義了使用 MBeanServer

{"hints": [
    {
        "name": "spring.jmx.server",
        "providers": [
            {
                "name": "spring-bean-reference",
                "parameters": {
                    "target": "javax.management.MBeanServer"
                }
            }
        ]
    }
]}

spring-profile-name

此 provider 自動完成在當前項目的配置中定義的spring配置文件拾并。

以下示例表示:spring.profiles.active屬性可啟用的配置文件名稱。

{"hints": [
    {
        "name": "spring.profiles.active",
        "providers": [
            {
                "name": "spring-profile-name"
            }
        ]
    }
]}

可重復的元數(shù)據(jù)項

具有相同“property”和“group”名稱的對象可以在元數(shù)據(jù)文件中多次出現(xiàn)。 例如嗅义,可以將兩個單獨的類綁定到同一前綴屏歹,每個類都有可能重疊的屬性名。 雖然多次出現(xiàn)在元數(shù)據(jù)中的相同名稱不應是常見的之碗,但元數(shù)據(jù)的使用者應注意確保他們支持該名稱蝙眶。

自動生成提示文件

通過使用 spring-boot-configuration-processor jar,您可以從用 @ConfigurationProperties 注釋的類中輕松生成自己的配置元數(shù)據(jù)文件褪那。 jar包含一個java注釋處理器幽纷,在編譯項目時調(diào)用它。 用此處理器博敬,需要引入 spring-boot-configuration-processor 依賴友浸。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

處理器獲取用@configurationproperties注釋的類和方法。 配置類中字段值的 javadoc 用于填充 description 屬性偏窝。

注意:僅僅只應將簡單文本與@configurationproperties字段javadoc一起使用收恢,因為在將它們添加到json之前不會對它們進行處理。

如果類有一個“至少一個參數(shù)”的構(gòu)造函數(shù)祭往,則為每個構(gòu)造函數(shù)參數(shù)創(chuàng)建一個屬性伦意。 否則,通過標準getter和setter來發(fā)現(xiàn)屬性硼补,這些getter和setter對集合類型進行了特殊處理(即使只有getter存在驮肉,也會檢測到)。

注解處理器還支持使用@data括勺、@getter和@setter 的 lombok 注解缆八。

注解處理器無法自動檢測 EnumCollections 的默認值。在集合或枚舉屬性具有非空默認值的情況下疾捍,應提供手動元數(shù)據(jù)。

@ConfigurationProperties(prefix="acme.messaging")
public class MessagingProperties {

    private List<String> addresses = new ArrayList<>(Arrays.asList("a", "b")) ;

    private ContainerType = ContainerType.SIMPLE;

    // ... getter and setters

    public enum ContainerType {
        SIMPLE,
        DIRECT
    }
}

為了提示上述屬性的默認值栏妖,應該手動添加如下元數(shù)據(jù):

{"properties": [
    {
        "name": "acme.messaging.addresses",
        "defaultValue": ["a", "b"]
    },
    {
        "name": "acme.messaging.container-type",
        "defaultValue": "simple"
    }
]}

注意: 如果在項目中使用 AspectJ乱豆,則需要確保注解處理器只運行一次。 使用 Maven 時吊趾, 可以顯式地配置 maven-apt-plugin插件宛裕,并僅在那里向注解處理器添加依賴項。 還可以讓 AspectJ 插件運行于所有的處理且在 maven-compiler-pluginconfiguration 中禁用注解處理论泛,如下:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <proc>none</proc>
    </configuration>
</plugin>

綁定屬性

注解處理器自動將內(nèi)部類視為嵌套屬性揩尸。

@ConfigurationProperties(prefix="server")
public class ServerProperties {
    private String name;
    private Host host;
    // ... getter and setters
    public static class Host {
        private String ip;
        private int port;
        // ... getter and setters
    }
}

上述示例生成 server.nameserver.host.ipserver.host.port 屬性的元數(shù)據(jù)信息屁奏。 可以在字段上使用@NestedconfigurationProperty 注解來指示應將常規(guī)(非內(nèi)部)類視為嵌套類岩榆。

注意: 這對集合和映射沒有影響,因為這些類型是自動標識的,并且為每個類型生成一個元數(shù)據(jù)屬性勇边。

添加額外的元數(shù)據(jù)

Spring Boot 的配置文件處理非常靈活犹撒,通常情況下,可能存在不綁定到 @ConfigurationProperties bean的屬性粒褒。 您還可能需要調(diào)整現(xiàn)有key的某些屬性识颊,為了支持這種情況并讓您提供自定義的“提示”,注解處理器會自動將 META-INF/additional-spring-configuration-metadata.json 中的提示項合并到主要元數(shù)據(jù)文件(spring-configuration-metadata.json)中**奕坟。

如果引用已自動檢測到的屬性祥款,則將覆蓋描述、默認值和棄用信息(如果指定)月杉。 如果當前模塊中沒有標識手動屬性中的聲明镰踏,則將其作為新屬性添加。

additional-spring-configuration-metadata.json 文件的格式與 spring-configuration-metadata.json 文件一樣沙合。 附加屬性文件是可選的奠伪。如果沒有任何其他屬性,就不要添加文件首懈。

參考資料

springboot 配置提示官方文檔
公眾號:逸飛兮(專注于 Java 領域知識的深入學習绊率,從源碼到原理,系統(tǒng)有序的學習)

逸飛兮
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末究履,一起剝皮案震驚了整個濱河市滤否,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌最仑,老刑警劉巖藐俺,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異泥彤,居然都是意外死亡欲芹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門吟吝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來菱父,“玉大人,你說我怎么就攤上這事剑逃≌阋耍” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵蛹磺,是天一觀的道長粟瞬。 經(jīng)常有香客問我,道長萤捆,這世上最難降的妖魔是什么裙品? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任俗批,我火速辦了婚禮,結(jié)果婚禮上清酥,老公的妹妹穿的比我還像新娘扶镀。我一直安慰自己,他們只是感情好焰轻,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布臭觉。 她就那樣靜靜地躺著,像睡著了一般辱志。 火紅的嫁衣襯著肌膚如雪蝠筑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天揩懒,我揣著相機與錄音什乙,去河邊找鬼。 笑死已球,一個胖子當著我的面吹牛臣镣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播智亮,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼忆某,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了阔蛉?” 一聲冷哼從身側(cè)響起弃舒,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎状原,沒想到半個月后聋呢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡颠区,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年削锰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓦呼。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡喂窟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出央串,到底是詐尸還是另有隱情,我是刑警寧澤质和,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站稚字,受9級特大地震影響饲宿,放射性物質(zhì)發(fā)生泄漏厦酬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一瘫想、第九天 我趴在偏房一處隱蔽的房頂上張望仗阅。 院中可真熱鬧,春花似錦国夜、人聲如沸减噪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筹裕。三九已至,卻和暖如春窄驹,著一層夾襖步出監(jiān)牢的瞬間朝卒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工乐埠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留抗斤,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓丈咐,卻偏偏與公主長得像瑞眼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子扯罐,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容