云原生-Quarkus配置參考指南

Quarkus應(yīng)用程序和Quarkus本身(核心和擴展)都通過相同的機制進行配置,該機制利用SmallRye Config API和 MicroProfile Config規(guī)范的實現(xiàn)。此外,Quarkus本身提供了一些附加功能。

1.配置源

默認情況下,Quarkus從多個來源(優(yōu)先級遞減)中讀取配置屬性:

  1. 系統(tǒng)屬性
  2. 環(huán)境變量
  3. 名為文件的文件.env放置在當前工作目錄中
  4. application.properties文件放在$PWD/config/目錄中
  5. 應(yīng)用程序配置文件,即src/main/resources/application.properties

1.1.系統(tǒng)屬性

  • 運行jar: java -Dquarkus.datasource.password=youshallnotpass -jar target/quarkus-app/quarkus-run.jar
  • native執(zhí)行文件: ./target/myapp-runner -Dquarkus.datasource.password=youshallnotpass

1.2.環(huán)境變量

    • 運行jar: export QUARKUS_DATASOURCE_PASSWORD=youshallnotpass ; java -jar target/quarkus-app/quarkus-run.jar
    • native可執(zhí)行文件: export QUARKUS_DATASOURCE_PASSWORD=youshallnotpass ; ./target/myapp-runner

1.3.文件名為.env放置在當前工作目錄中

示例.env文件

QUARKUS_DATASOURCE_PASSWORD=youshallnotpass 
QUARKUS_DATASOURCE_PASSWORD使用與環(huán)境變量相同的規(guī)則來轉(zhuǎn)換名稱。

對于開發(fā)人員模式,此文件可以放在項目的根目錄中,但建議不要將其檢入版本控制。

沒有定義的配置文件的環(huán)境變量.env文件將覆蓋其所有相關(guān)的配置文件中application.properties,例如,%test.application.value被覆蓋APPLICATION_VALUE.env文件。

1.4.application.properties文件放置在$PWD/config/

application.properties文件放置在名為config的目錄中,該目錄位于應(yīng)用程序運行的目錄中,該文件中定義的所有運行時屬性都將覆蓋默認配置。此外,添加到該文件中的任何不屬于原始application.properties文件的 運行時屬性將被考慮在內(nèi)。對于運行程序jar和本機可執(zhí)行文件,其工作方式相同。

config/application.properties功能在開發(fā)模式下也可用。要使用它,config/application.properties需要放置在構(gòu)建工具的輸出目錄中(target對于Maven和build/classes/java/mainGradle)。但是請記住,從構(gòu)建工具進行的任何清理操作(例如mvn clean或)gradle clean也會刪除該config目錄。

1.5.應(yīng)用程序配置文件

這是位于中的主要應(yīng)用程序配置文件src/main/resources/application.properties。

示例application.properties文件

greeting.message=hello 
quarkus.http.port=9090 
  • 這是用戶定義的配置屬性。
  • 這是用戶定義的配置屬性。
  • Quarkus支持在文件中使用屬性表達式application.properties。

2.將配置文件嵌入到依賴項中

可以通過向META-INF/microprofile-config.properties配置文件中添加一個配置文件來將配置文件嵌入其中(這是MicroProfile Config的標準功能)。

當將此依賴項添加到應(yīng)用程序時,其配置屬性將被合并。

可以覆蓋優(yōu)先于它的屬性來自它的屬性application.properties

3.注入配置屬性

Quarkus使用MicroProfile Config注釋在應(yīng)用程序中注入配置屬性。

@ConfigProperty(name = "greeting.message") 
String message;

可以使用@Inject @ConfigProperty@ConfigProperty。對于使用@Inject注釋的成員,注釋不是必需的@ConfigProperty。此行為不同于MicroProfile Config。

如果應(yīng)用程序嘗試注入未設(shè)置的配置屬性,則會引發(fā)錯誤,從而使您可以快速了解配置何時完成。

更多@ConfigProperty例子

@ConfigProperty(name = "greeting.message") 
String message;

@ConfigProperty(name = "greeting.suffix", defaultValue="!") 
String suffix;

@ConfigProperty(name = "greeting.name")
Optional<String> name; 

如果您不提供此屬性的值,則應(yīng)用程序啟動將失敗javax.enterprise.inject.spi.DeploymentException: No config value of type [class java.lang.String] exists for: greeting.message。

如果配置未提供的值,則會注入默認值greeting.suffix。

此屬性是可選的-Optional如果配置未提供的值,則會注入一個空值greeting.name。

4.以編程方式訪問配置

可以通過編程方式訪問配置。實現(xiàn)動態(tài)查找或從既不是CDI bean也不是JAX-RS資源的類中檢索配置的值可能很方便。

可以使用以下方式以編程方式訪問配置org.eclipse.microprofile.config.ConfigProvider.getConfig()

String databaseName = ConfigProvider.getConfig().getValue("database.name", String.class);
Optional<String> maybeDatabaseName = ConfigProvider.getConfig().getOptionalValue("database.name", String.class);

5.使用@ConfigProperties

作為以上一示例中所示的方式注入多個相關(guān)配置值的替代方法,用戶還可以使用@io.quarkus.arc.config.ConfigProperties注釋將這些屬性組合在一起。

對于上面的Greeting屬性,GreetingConfiguration可以這樣創(chuàng)建一個類:

package org.acme.config;

import io.quarkus.arc.config.ConfigProperties;
import java.util.Optional;

@ConfigProperties(prefix = "greeting") 
public class GreetingConfiguration {

    private String message;
    private String suffix = "!"; 
    private Optional<String> name;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }

    public Optional<String> getName() {
        return name;
    }

    public void setName(Optional<String> name) {
        this.name = name;
    }
}

然后可以GreetingResource使用@Inject類似的CDI注釋將此類注入到,如下所示:

@Inject
GreetingConfiguration greetingConfiguration;

Quarkus提供的另一種替代樣式是GreetingConfiguration像這樣創(chuàng)建接口:

package org.acme.config;

import io.quarkus.arc.config.ConfigProperties;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.util.Optional;

@ConfigProperties(prefix = "greeting")
public interface GreetingConfiguration {

    @ConfigProperty(name = "message") 
    String message();

    @ConfigProperty(defaultValue = "!")
    String getSuffix(); 

    Optional<String> getName(); 
}

@ConfigProperties類或接口上使用時,如果未提供其字段之一的值,則應(yīng)用程序啟動將失敗,并且將javax.enterprise.inject.spi.DeploymentException顯示指示缺少值的信息。這不適用于Optional字段和具有默認值的字段。

5.1.有關(guān)@ConfigProperties的其他說明

當使用帶有類注釋的常規(guī)類時,@ConfigProperties不一定必須聲明getter和setter。具有簡單的公共非最終字段也是有效的。

此外,配置類支持嵌套對象配置。假設(shè)需要有一個額外的問候語配置層,名稱content將包含一些字段。可以這樣實現(xiàn):

@ConfigProperties(prefix = "greeting")
public class GreetingConfiguration {

    public String message;
    public String suffix = "!";
    public Optional<String> name;
    public ContentConfig content; 

    public static class ContentConfig {
        public Integer prizeAmount;
        public List<String> recipients;
    }
}

字段名稱(不是類名稱)將確定綁定到對象的屬性的名稱。

設(shè)置屬性將以正常方式進行,例如,application.properties可能具有:

greeting.message = hello
greeting.name = quarkus
greeting.content.prize-amount=10
greeting.content.recipients=Jane,John

此外,@ConfigProperties可以使用Bean Validation批注來批注使用批注的類,類似于以下示例:

@ConfigProperties(prefix = "greeting")
public class GreetingConfiguration {

    @Size(min = 20)
    public String message;
    public String suffix = "!";

}

為了使驗證生效,quarkus-hibernate-validator需要提供擴展名

如果使用給定的配置驗證失敗,則應(yīng)用程序?qū)o法啟動,并在日志中指示相應(yīng)的驗證錯誤。

如果接口帶有注釋@ConfigProperties,則允許該接口擴展其他接口,并且使用整個接口層次結(jié)構(gòu)中的方法來綁定屬性。

5.2.使用具有不同前綴的相同ConfigProperty

Quarkus還支持使用注解@ConfigProperties為每個注入點使用帶有不同前綴的同一對象io.quarkus.arc.config.@ConfigPrefix。舉例來說,前綴和前綴GreetingConfiguration都需要使用以上代碼。在這種情況下,代碼如下所示:greetingother

GreetingConfiguration.java

@ConfigProperties(prefix = "greeting")
public class GreetingConfiguration {

    @Size(min = 20)
    public String message;
    public String suffix = "!";

}

SomeBean.java

@ApplicationScoped
public class SomeBean {

    @Inject 
    GreetingConfiguration greetingConfiguration;

    @ConfigPrefix("other") 
    GreetingConfiguration otherConfiguration;

}

5.3.使用對象列表

在某些情況下,可能有必要支持利用對象列表的復(fù)雜配置結(jié)構(gòu),如以下示例所示:

ComplexConfiguration.java

@ConfigProperties(prefix = "complex")
public class ComplexConfiguration {
    public String name;
    public String user;
    public String password;
    public List<Nested> inputs;
    public List<Nested> outputs;

    public static class Nested {
        public String user;
        public String password;
    }
}

僅當將YAML配置與quarkus-config-yaml擴展一起使用時,才對此類用例提供支持。相應(yīng)的示例YAML配置可以是:

application.yaml

complex:
  name: defaultName
  user: defaultUser
  password: defaultPassword
  inputs:
    - user: user
      password: secret
    - user: otheruser
      password: secret2
  outputs:
    - user: someuser
      password: asecret
    - user: someotheruser
      password: anothersecret
  • 這種配置的局限性在于用作列表的通用類型的類型必須是類而不是接口
  • 6.配置配置文件

    Quarkus支持配置配置文件的概念。這些允許您在同一文件中具有多個配置,并通過配置文件名稱在它們之間進行選擇。

    語法為%{profile}.config.key=value。例如,如果我有以下內(nèi)容:

    quarkus.http.port=9090
    %dev.quarkus.http.port=8181

    除非dev配置文件處于活動狀態(tài),否則Quarkus HTTP端口將為9090 ,在這種情況下,它將為8181。

    要在.env文件中使用配置文件,可以遵循一種_{PROFILE}_CONFIG_KEY=value模式。.env文件中上述示例的等效內(nèi)容為:

    QUARKUS_HTTP_PORT=9090
    _DEV_QUARKUS_HTTP_PORT=8181

    默認情況下,Quarkus具有三個配置文件,盡管可以隨意使用。默認配置文件為:

    • dev-在開發(fā)模式下(即quarkus:dev)激活
    • 測試-運行測試時激活
    • prod-未在開發(fā)或測試模式下運行時的默認配置文件

    有兩種方法可以通過quarkus.profile系統(tǒng)屬性或QUARKUS_PROFILE 環(huán)境變量來設(shè)置定制概要文件。如果兩者都設(shè)置,則系統(tǒng)屬性優(yōu)先。請注意,不必在任何地方定義這些概要文件的名稱,只需創(chuàng)建具有概要文件名稱的config屬性,然后將當前概要文件設(shè)置為該名稱即可。例如,如果我想要staging使用其他HTTP端口的配置文件,則可以將以下內(nèi)容添加到application.properties

    quarkus.http.port=9090
    %staging.quarkus.http.port=9999

    然后將QUARKUS_PROFILE環(huán)境變量設(shè)置staging為激活我的配置文件。

    以編程方式檢查活動配置文件的正確方法是使用的getActiveProfile方法io.quarkus.runtime.configuration.ProfileManager。

    使用@ConfigProperty("quarkus.profile")無法正常工作。

    6.1.默認運行時配置文件

    默認的Quarkus應(yīng)用程序運行時配置文件設(shè)置為用于構(gòu)建應(yīng)用程序的配置文件。例如:

    ./mvnw package -Pnative -Dquarkus.profile=prod-aws
    ./target/my-app-1.0-runner 

    該命令將與prod-aws配置文件一起運行??梢允褂?span>quarkus.profilesystem屬性來覆蓋它。

    7.使用屬性表達式

    Quarkus支持在application.properties文件中使用屬性表達式。

    讀取屬性后,將解析這些表達式。因此,如果您的配置屬性是構(gòu)建時配置屬性,則該屬性表達式將在構(gòu)建時解析。如果您的配置屬性在運行時可覆蓋,則該屬性表達式將在運行時解析。

    您可以將屬性表達式同時用于Quarkus配置或您自己的配置屬性。

    屬性表達式是通過以下方式定義的:${my-property-expression}。

    例如,具有以下屬性:

    remote.host=quarkus.io

    另一個屬性定義為:

    callable.url=https://${remote.host}/

    將導(dǎo)致該callable.url屬性的值設(shè)置為:

    callable.url=https://quarkus.io/

    另一個示例是根據(jù)所使用的概要文件定義不同的數(shù)據(jù)庫服務(wù)器:

    %dev.quarkus.datasource.jdbc.url=jdbc:mysql://localhost:3306/mydatabase?useSSL=false
    quarkus.datasource.jdbc.url=jdbc:mysql://remotehost:3306/mydatabase?useSSL=false

    可以通過以下方式簡化:

    %dev.application.server=localhost
    application.server=remotehost
    
    quarkus.datasource.jdbc.url=jdbc:mysql://${application.server}:3306/mydatabase?useSSL=false

    在此示例中,它的確增加了一行,但是的值application.server可以在其他屬性中重用,從而減少了輸入錯誤的可能性,并在屬性定義中提供了更大的靈活性。

    8.組合屬性表達式和環(huán)境變量

    Quarkus還支持屬性表達式和環(huán)境變量的組合。

    假設(shè)您在中定義了以下屬性application.properties

    remote.host=quarkus.io

    您可以通過如下定義屬性來組合環(huán)境變量和屬性表達式:

    application.host=${HOST:${remote.host}}

    如果未設(shè)置,這將擴展HOST環(huán)境變量并將屬性的值remote.host用作默認值HOST。

    出于本節(jié)的目的,我們使用了remote.host之前定義的屬性。必須注意,該值可以是固定值,例如:

    application.host=${HOST:localhost}

    localhost如果HOST未設(shè)置,它將作為默認值。

    9.配置Quarkus

    Quarkus本身是通過與您的應(yīng)用程序相同的機制配置的。Quarkusquarkus.為自己的配置及其所有擴展的配置保留名稱空間。例如,要配置HTTP服務(wù)器端口,可以quarkus.http.port在 中設(shè)置application.properties。所有Quarkus配置屬性均已記錄且可搜索。

    Quarkus在構(gòu)建時進行大部分配置和引導(dǎo),并且在構(gòu)建期間讀取并使用了一些配置屬性。這些屬性在構(gòu)建時固定的,無法在運行時進行更改。您始終需要重新打包應(yīng)用程序,以反映此類屬性的更改。

    • 在構(gòu)建時固定的屬性用鎖定圖標標記()在所有配置選項的列表中

    但是,某些擴展程序確實定義了可以在運行時覆蓋的屬性。一個典型的例子是數(shù)據(jù)庫URL,用戶名和密碼,這些僅在您的目標環(huán)境中才知道。這是一個權(quán)衡,因為可用的運行時屬性越多,Quarkus可以進行的構(gòu)建時間越少。因此,運行時屬性列表是精簡的。您可以使用以下機制(以降低的優(yōu)先級)覆蓋這些運行時屬性:

    1. 系統(tǒng)屬性
    2. 環(huán)境變量
    3. 名為環(huán)境文件.env放置在當前工作目錄中
    4. 放置在其中的配置文件 $PWD/config/application.properties

    有關(guān)更多詳細信息,請參見配置源。

    10.為應(yīng)用程序生成配置

    也可以生成一個application.properties具有所有已知配置屬性的示例,以使您輕松查看可用的Quarkus配置選項。為此,請運行:

    ./mvnw quarkus:generate-config

    這將創(chuàng)建一個src/main/resources/application.properties.example文件,其中包含通過您當前安裝的擴展程序公開的所有配置選項。這些選項已被注釋掉,并在適用時具有其默認值。例如,此HTTP端口配置條目將顯示為:

    #
    # The HTTP port
    #
    #quarkus.http.port=8080

    除了生成示例配置文件之外,您還可以通過設(shè)置-Dfile 參數(shù)將它們添加到實際的配置文件中:

    ./mvnw quarkus:generate-config -Dfile=application.properties

    如果配置選項已經(jīng)存在(有注釋),則不會添加,因此在添加其他擴展名以查看添加了哪些其他選項之后可以安全地運行該配置選項。

    11.清除屬性

    可以通過為屬性分配一個空字符串來明確清除運行時屬性,這些屬性是可選的,并具有在構(gòu)建時設(shè)置的值或具有默認值。請注意,這只會影響運行時屬性,并且只能與不需要其值的屬性一起使用。

    可以通過設(shè)置相應(yīng)的application.properties屬性,設(shè)置相應(yīng)的系統(tǒng)屬性或設(shè)置相應(yīng)的環(huán)境變量來清除該屬性。

    12.自定義配置

    12.1.定制配置源

    您還可以采用標準MicroProfile Config方式介紹自定義配置源。為此,您必須提供一個實現(xiàn)org.eclipse.microprofile.config.spi.ConfigSource 或的類org.eclipse.microprofile.config.spi.ConfigSourceProvider。為該類創(chuàng)建一個 服務(wù)文件,它將在應(yīng)用程序啟動時檢測并安裝。

    12.2.定制配置轉(zhuǎn)換器

    您也可以將自定義類型用于配置值。這可以通過org.eclipse.microprofile.config.spi.Converter<T>META-INF/services/org.eclipse.microprofile.config.spi.Converter文件中實現(xiàn)并添加其完全限定的類名來完成。

    讓我們假設(shè)您有一個像這樣的自定義類型:

    package org.acme.config;
    
    public class MicroProfileCustomValue {
    
        private final int number;
    
        public MicroProfileCustomValue(int number) {
            this.number = number;
        }
    
        public int getNumber() {
            return number;
        }
    }

    相應(yīng)的轉(zhuǎn)換器如下所示。請注意,您的自定義轉(zhuǎn)換器類必須是public并且必須具有public無參數(shù)構(gòu)造函數(shù)。也一定不能abstract。

    package org.acme.config;
    
    import org.eclipse.microprofile.config.spi.Converter;
    
    public class MicroProfileCustomValueConverter implements Converter<MicroProfileCustomValue> {
    
        @Override
        public MicroProfileCustomValue convert(String value) {
            return new MicroProfileCustomValue(Integer.parseInt(value));
        }
    }

    然后,您需要在服務(wù)文件中包含轉(zhuǎn)換器的標準類名META-INF/services/org.eclipse.microprofile.config.spi.Converter。如果您有更多的轉(zhuǎn)換器,也只需在其文件中添加它們的類名。每行一個全限定的類名,例如:

    org.acme.config.MicroProfileCustomValueConverter
    org.acme.config.SomeOtherConverter
    org.acme.config.YetAnotherConverter

    請注意,SomeOtherConverter并且YetAnotherConverter僅出于演示目的而添加了。如果在此文件中包含在運行時不可用的類,則轉(zhuǎn)換器加載將失敗。

    完成此操作后,您可以將自定義類型用作配置值:

    @ConfigProperty(name = "configuration.value.name")
    MicroProfileCustomValue value;

    12.2.1.轉(zhuǎn)換器優(yōu)先級

    在某些情況下,您可能希望使用自定義轉(zhuǎn)換器來轉(zhuǎn)換已經(jīng)由其他轉(zhuǎn)換器轉(zhuǎn)換的類型。在這種情況下,您可以使用javax.annotation.Priority注釋來更改轉(zhuǎn)換器的優(yōu)先級,并使自定義轉(zhuǎn)換器的優(yōu)先級高于列表中的其他轉(zhuǎn)換器。

    默認情況下,如果@Priority在轉(zhuǎn)換器上找不到No ,則其優(yōu)先級為100,所有Quarkus核心轉(zhuǎn)換器的優(yōu)先級為200,因此,根據(jù)要替換的轉(zhuǎn)換器,需要設(shè)置更高的值。

    為了演示這個想法,讓我們實現(xiàn)一個自定義轉(zhuǎn)換器,該轉(zhuǎn)換器將優(yōu)先MicroProfileCustomValueConverter于上一個示例中實現(xiàn)的轉(zhuǎn)換器 。

    package org.acme.config;
    
    import javax.annotation.Priority;
    import org.eclipse.microprofile.config.spi.Converter;
    
    @Priority(150)
    public class MyCustomConverter implements Converter<MicroProfileCustomValue> {
    
        @Override
        public MicroProfileCustomValue convert(String value) {
    
            final int secretNumber;
            if (value.startsFrom("OBF:")) {
                secretNumber = Integer.parseInt(SecretDecoder.decode(value));
            } else {
                secretNumber = Integer.parseInt(value);
            }
    
            return new MicroProfileCustomValue(secretNumber);
        }
    }

    由于它會轉(zhuǎn)換相同的值類型(即MicroProfileCustomValue),并且優(yōu)先級為150,因此將使用MicroProfileCustomValueConverter默認優(yōu)先級為100的a代替它。

    此新轉(zhuǎn)換器還需要在服務(wù)文件中列出,即META-INF/services/org.eclipse.microprofile.config.spi.Converter

    13. YAML配置

    13.1.添加YAML配置支持

    您可能要對屬性使用YAML進行配置。由于SmallRye Config帶來了對YAML配置的支持,因此Quarkus也支持這一點。

    首先,您需要將Config YAML擴展名添加到您的pom.xml

    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-config-yaml</artifactId>
    </dependency>

    或者,您也可以在包含Quarkus項目的目錄中運行以下命令:

    ./mvnw quarkus:add-extension -Dextensions="config-yaml"

    現(xiàn)在,Quarkus可以讀取YAML配置文件。配置目錄和優(yōu)先級與以前相同。

    Quarkus將選擇application.yaml一個application.properties。YAML文件只是配置應(yīng)用程序的另一種方法。您應(yīng)該確定并保留一種配置類型,以避免出現(xiàn)錯誤。

    13.1.1.配置實例

    # YAML supports comments
    quarkus:
      datasource:
        db-kind: postgresql
        jdbc:
          url: jdbc:postgresql://localhost:5432/some-database
        username: quarkus
        password: quarkus
    
    # REST Client configuration property
    org:
      acme:
        restclient:
          CountriesService/mp-rest/url: https://restcountries.eu/rest
    
    # For configuration property names that use quotes, do not split the string inside the quotes.
    quarkus:
      log:
        category:
          "io.quarkus.category":
            level: INFO

    Quarkus還支持使用application.yml作為YAML文件的名稱。該文件與的規(guī)則相同application.yaml。

    13.2.取決于配置文件的配置

    與屬性一樣,通過YAML提供與配置文件相關(guān)的配置也是如此。%profile在定義鍵/值對之前,只需添加包裝在引號中的引號即可:

    "%dev":
      quarkus:
        datasource:
          db-kind: postgresql
          jdbc:
            url: jdbc:postgresql://localhost:5432/some-database
          username: quarkus
          password: quarkus

    13.3.配置密鑰沖突

    MicroProfile Config規(guī)范將配置鍵定義為一個任意的,.分隔字符串。但是,像YAML這樣的結(jié)構(gòu)化格式天真的僅支持可能的配置名稱空間的子集。例如,考慮兩個配置屬性quarkus.http.corsquarkus.http.cors.methods。一個屬性是另一個屬性的前綴,因此如何在您的YAML配置中同時指定兩個密鑰可能并不明顯。

    這可以通過對任何YAML屬性使用null鍵(通常由表示~)來解決,該鍵是另一個前綴。這是一個例子:

    解決與前綴相關(guān)的鍵名沖突的示例YAML配置

    quarkus:
      http:
        cors:
          ~: true
          methods: GET,PUT,POST

    通常,nullYAML密鑰不包括在配置屬性名稱的匯編中,從而允許它們在任何級別上用于消除配置密鑰的歧義。

    14.有關(guān)如何配置的更多信息

    Quarkus依賴SmallRye Config并繼承其功能。

    SmallRye Config提供:

    • 其他配置源
    • 附加轉(zhuǎn)換器
    • 索引屬性
    • 家長資料
    • 用于配置值解析的攔截器
    • 重新定位配置屬性
    • 后備配置屬性
    • 記錄中
    • 隱藏秘密
    ?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
    【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
    平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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