您可以通過創(chuàng)建使用 @ConfigurationProperties 注釋的類來創(chuàng)建類型安全的配置。
Micronaut 會產(chǎn)生一個無反射的@ConfigurationProperties bean,并且還會在編譯時計算屬性路徑進(jìn)行評估,大大提高加載@ConfigurationProperties 的速度和效率。
例如:
@ConfigurationProperties Example
Java |
Groovy |
Kotlin |
import io.micronaut.context.annotation.ConfigurationProperties;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.util.Optional;
@ConfigurationProperties("my.engine") // (1)
public class EngineConfig {
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public int getCylinders() {
return cylinders;
}
public void setCylinders(int cylinders) {
this.cylinders = cylinders;
}
public CrankShaft getCrankShaft() {
return crankShaft;
}
public void setCrankShaft(CrankShaft crankShaft) {
this.crankShaft = crankShaft;
}
@NotBlank // (2)
private String manufacturer = "Ford"; // (3)
@Min(1L)
private int cylinders;
private CrankShaft crankShaft = new CrankShaft();
@ConfigurationProperties("crank-shaft")
public static class CrankShaft { // (4)
private Optional<Double> rodLength = Optional.empty(); // (5)
public Optional<Double> getRodLength() {
return rodLength;
}
public void setRodLength(Optional<Double> rodLength) {
this.rodLength = rodLength;
}
}
}
|
import io.micronaut.context.annotation.ConfigurationProperties
import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank
@ConfigurationProperties('my.engine') // (1)
class EngineConfig {
@NotBlank // (2)
String manufacturer = "Ford" // (3)
@Min(1L)
int cylinders
CrankShaft crankShaft = new CrankShaft()
@ConfigurationProperties('crank-shaft')
static class CrankShaft { // (4)
Optional<Double> rodLength = Optional.empty() // (5)
}
}
|
import io.micronaut.context.annotation.ConfigurationProperties
import java.util.Optional
import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank
@ConfigurationProperties("my.engine") // (1)
class EngineConfig {
@NotBlank // (2)
var manufacturer = "Ford" // (3)
@Min(1L)
var cylinders: Int = 0
var crankShaft = CrankShaft()
@ConfigurationProperties("crank-shaft")
class CrankShaft { // (4)
var rodLength: Optional<Double> = Optional.empty() // (5)
}
}
|
@ConfigurationProperties 注解采用配置前綴
您可以使用 javax.validation 注釋來驗證配置
可以為屬性分配默認(rèn)值
靜態(tài)內(nèi)部類可以提供嵌套配置
可選的配置值可以包裝在 java.util.Optional 中
一旦你準(zhǔn)備好了一個類型安全的配置,它就可以像任何其他 bean 一樣注入到你的 bean 中:
@ConfigurationProperties 依賴注入
Java |
Groovy |
Kotlin |
@Singleton
public class EngineImpl implements Engine {
private final EngineConfig config;
public EngineImpl(EngineConfig config) { // (1)
this.config = config;
}
@Override
public int getCylinders() {
return config.getCylinders();
}
@Override
public String start() {// (2)
return getConfig().getManufacturer() + " Engine Starting V" + getConfig().getCylinders() +
" [rodLength=" + getConfig().getCrankShaft().getRodLength().orElse(6d) + "]";
}
public final EngineConfig getConfig() {
return config;
}
}
|
@Singleton
class EngineImpl implements Engine {
final EngineConfig config
EngineImpl(EngineConfig config) { // (1)
this.config = config
}
@Override
int getCylinders() {
config.cylinders
}
@Override
String start() { // (2)
"$config.manufacturer Engine Starting V$config.cylinders [rodLength=${config.crankShaft.rodLength.orElse(6.0d)}]"
}
}
|
@Singleton
class EngineImpl(val config: EngineConfig) : Engine {// (1)
override val cylinders: Int
get() = config.cylinders
override fun start(): String {// (2)
return "${config.manufacturer} Engine Starting V${config.cylinders} [rodLength=${config.crankShaft.rodLength.orElse(6.0)}]"
}
}
|
注入 EngineConfig bean
使用配置屬性
然后可以從 PropertySource 實例之一提供配置值。例如:
供應(yīng)配置
Java |
Groovy |
Kotlin |
Map<String, Object> map = new LinkedHashMap<>(1);
map.put("my.engine.cylinders", "8");
ApplicationContext applicationContext = ApplicationContext.run(map, "test");
Vehicle vehicle = applicationContext.getBean(Vehicle.class);
System.out.println(vehicle.start());
|
ApplicationContext applicationContext = ApplicationContext.run(
['my.engine.cylinders': '8'],
"test"
)
def vehicle = applicationContext.getBean(Vehicle)
println(vehicle.start())
|
val map = mapOf( "my.engine.cylinders" to "8")
val applicationContext = ApplicationContext.run(map, "test")
val vehicle = applicationContext.getBean(Vehicle::class.java)
println(vehicle.start())
|
上面的示例打?。骸癋ord Engine Starting V8 [rodLength=6.0]”
您可以使用以下語法直接引用@Requires 注釋中的配置屬性以有條件地加載bean:@Requires(bean=Config.class, beanProperty="property", value="true")
請注意,對于更復(fù)雜的配置,您可以通過繼承構(gòu)造 @ConfigurationProperties bean。
例如,使用 @ConfigurationProperties('bar') 創(chuàng)建 EngineConfig 的子類將解析路徑 my.engine.bar 下的所有屬性。
包括/排除
對于配置屬性類從父類繼承屬性的情況,可能需要從父類中排除屬性。 @ConfigurationProperties 注釋的包含和排除成員允許該功能。該列表適用于本地屬性和繼承屬性。
提供給包含/排除列表的名稱必須是“屬性”名稱。例如,如果注入了 setter 方法,則屬性名稱是去大寫的 setter 名稱(setConnectionTimeout → connectionTimeout)。
更改訪問器樣式
從 3.3 開始,Micronaut 支持為 getter 和 setter 定義不同的訪問器前綴,而不是為 Java Beans 定義的默認(rèn) get 和 set。使用@AccessorsStyle 注釋注釋您的POJO 或@ConfigurationProperties 類。
當(dāng)您以流暢的方式編寫 getter 和 setter 時,這很有用。例如:
使用@AccessorsStyle
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.core.annotation.AccessorsStyle;
@AccessorsStyle(readPrefixes = "", writePrefixes = "") (1)
@ConfigurationProperties("my.engine")
public class EngineConfig {
private String manufacturer;
private int cylinders;
public EngineConfig(String manufacturer, int cylinders) {
this.manufacturer = manufacturer;
this.cylinders = cylinders;
}
public String manufacturer() { (2)
return manufacturer;
}
public void manufacturer(String manufacturer) { (2)
this.manufacturer = manufacturer;
}
public int cylinders() { (2)
return cylinders;
}
public void cylinders(int cylinders) { (2)
this.cylinders = cylinders;
}
}
Micronaut 將為 getter 和 setter 使用空前綴。
使用空前綴定義 getter 和 setter。
現(xiàn)在您可以注入 EngineConfig 并將其與 engineConfig.manufacturer() 和 engineConfig.cylinders() 一起使用以從配置中檢索值。
屬性類型轉(zhuǎn)換
Micronaut 在解析屬性時使用 ConversionService bean 來轉(zhuǎn)換值。您可以通過定義實現(xiàn) TypeConverter 接口的 bean 來為 Micronaut 不支持的類型注冊額外的轉(zhuǎn)換器。
Micronaut 具有一些有用的內(nèi)置轉(zhuǎn)換,下面將詳細(xì)介紹。
持續(xù)時間轉(zhuǎn)換
可以通過在單位后面附加一個數(shù)字來指定持續(xù)時間。支持的單位有 s、ms、m 等。下表總結(jié)了示例:
表 1. 持續(xù)時間轉(zhuǎn)換
配置值 |
結(jié)果值 |
10ms
|
持續(xù)時間為 10 毫秒
|
10m
|
持續(xù)時間為 10 分鐘
|
10s
|
持續(xù)時間為 10 秒
|
10d
|
持續(xù)時間為 10 天
|
10h
|
持續(xù)時間為 10 小時
|
10ns
|
持續(xù)時間為 10 納秒
|
PT15M
|
使用ISO-8601格式,持續(xù)時間為15分鐘
|
例如配置默認(rèn)的 HTTP 客戶端讀取超時:
使用持續(xù)時間值
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
micronaut.http.client.read-timeout=15s
|
micronaut:
http:
client:
read-timeout: 15s
|
[micronaut]
[micronaut.http]
[micronaut.http.client]
read-timeout="15s"
|
micronaut {
http {
client {
readTimeout = "15s"
}
}
}
|
{
micronaut {
http {
client {
read-timeout = "15s"
}
}
}
}
|
{
"micronaut": {
"http": {
"client": {
"read-timeout": "15s"
}
}
}
}
|
列表/數(shù)組轉(zhuǎn)換
列表和數(shù)組可以在 Java 屬性文件中指定為逗號分隔值,或者在 YAML 中使用本機(jī) YAML 列表。通用類型用于轉(zhuǎn)換值。例如在 YAML 中:
在 YAML 中指定列表或數(shù)組
Properties |
Yaml |
Toml |
Groovy |
Hocon |
JSON |
my.app.integers[0]=1
my.app.integers[1]=2
my.app.urls[0]=http://foo.com
my.app.urls[1]=http://bar.com
|
my:
app:
integers:
- 1
- 2
urls:
- http://foo.com
- http://bar.com
|
[my]
[my.app]
integers=[
1,
2
]
urls=[
"http://foo.com",
"http://bar.com"
]
|
my {
app {
integers = [1, 2]
urls = ["http://foo.com", "http://bar.com"]
}
}
|
{
my {
app {
integers = [1, 2]
urls = ["http://foo.com", "http://bar.com"]
}
}
}
|
{
"my": {
"app": {
"integers": [1, 2],
"urls": ["http://foo.com", "http://bar.com"]
}
}
}
|
對于上面的示例配置,您可以定義屬性以綁定到通過泛型提供的目標(biāo)類型:
List<Integer> integers;
List<URL> urls;
可讀字節(jié)
您可以使用 @ReadableBytes 注釋任何設(shè)置器參數(shù),以允許使用用于指定字節(jié)、千字節(jié)等的簡寫語法設(shè)置值。例如,以下內(nèi)容取自 HttpClientConfiguration:
使用@ReadableBytes
public void setMaxContentLength(@ReadableBytes int maxContentLength) {
this.maxContentLength = maxContentLength;
}
準(zhǔn)備好上述內(nèi)容后,您可以使用以下值設(shè)置 micronaut.http.client.max-content-length:
表 2. @ReadableBytes 轉(zhuǎn)換
配置值 |
結(jié)果值 |
10mb
|
10 MB
|
10kb
|
10 KB
|
10gb
|
10 GB
|
1024
|
原始字節(jié)長度
|
格式化日期
@Format 注釋可用于 setter 以指定綁定 java.time 日期對象時要使用的日期格式。
對日期使用@Format
public void setMyDate(@Format("yyyy-MM-dd") LocalDate date) {
this.myDate = date;
}
配置生成器
許多框架和工具已經(jīng)使用構(gòu)建器風(fēng)格的類來構(gòu)造配置。
您可以使用 @ConfigurationBuilder 注釋來使用配置值填充構(gòu)建器樣式的類。 ConfigurationBuilder 可以應(yīng)用于用@ConfigurationProperties 注釋的類中的字段或方法。
由于在 Java 世界中沒有一致的方法來定義構(gòu)建器,因此可以在注釋中指定一個或多個方法前綴以支持構(gòu)建器方法,如 withXxx 或 setXxx。如果構(gòu)建器方法沒有前綴,則為參數(shù)分配一個空字符串。
還可以指定配置前綴來告訴 Micronaut 在哪里查找配置值。默認(rèn)情況下,構(gòu)建器方法使用在類級 @ConfigurationProperties 注釋中指定的配置前綴。
例如:
@ConfigurationBuilder 示例
Java |
Groovy |
Kotlin |
import io.micronaut.context.annotation.ConfigurationBuilder;
import io.micronaut.context.annotation.ConfigurationProperties;
@ConfigurationProperties("my.engine") // (1)
class EngineConfig {
@ConfigurationBuilder(prefixes = "with") // (2)
EngineImpl.Builder builder = EngineImpl.builder();
@ConfigurationBuilder(prefixes = "with", configurationPrefix = "crank-shaft") // (3)
CrankShaft.Builder crankShaft = CrankShaft.builder();
private SparkPlug.Builder sparkPlug = SparkPlug.builder();
SparkPlug.Builder getSparkPlug() {
return sparkPlug;
}
@ConfigurationBuilder(prefixes = "with", configurationPrefix = "spark-plug") // (4)
void setSparkPlug(SparkPlug.Builder sparkPlug) {
this.sparkPlug = sparkPlug;
}
}
|
import io.micronaut.context.annotation.ConfigurationBuilder
import io.micronaut.context.annotation.ConfigurationProperties
@ConfigurationProperties('my.engine') // (1)
class EngineConfig {
@ConfigurationBuilder(prefixes = "with") // (2)
EngineImpl.Builder builder = EngineImpl.builder()
@ConfigurationBuilder(prefixes = "with", configurationPrefix = "crank-shaft") // (3)
CrankShaft.Builder crankShaft = CrankShaft.builder()
SparkPlug.Builder sparkPlug = SparkPlug.builder()
@ConfigurationBuilder(prefixes = "with", configurationPrefix = "spark-plug") // (4)
void setSparkPlug(SparkPlug.Builder sparkPlug) {
this.sparkPlug = sparkPlug
}
}
|
import io.micronaut.context.annotation.ConfigurationBuilder
import io.micronaut.context.annotation.ConfigurationProperties
@ConfigurationProperties("my.engine") // (1)
internal class EngineConfig {
@ConfigurationBuilder(prefixes = ["with"]) // (2)
val builder = EngineImpl.builder()
@ConfigurationBuilder(prefixes = ["with"], configurationPrefix = "crank-shaft") // (3)
val crankShaft = CrankShaft.builder()
@set:ConfigurationBuilder(prefixes = ["with"], configurationPrefix = "spark-plug") // (4)
var sparkPlug = SparkPlug.builder()
}
|
@ConfigurationProperties 注解采用配置前綴
第一個構(gòu)建器可以在沒有類配置前綴的情況下進(jìn)行配置;它繼承自上面的。
第二個構(gòu)建器可以配置類配置前綴 + configurationPrefix 值。
第三個構(gòu)建器演示了注釋可以應(yīng)用于方法和屬性。
默認(rèn)情況下,僅支持單參數(shù)構(gòu)建器方法。對于沒有參數(shù)的方法,將注釋的 allowZeroArgs 參數(shù)設(shè)置為 true。
與前面的示例一樣,我們可以構(gòu)造一個 EngineImpl。由于我們使用的是構(gòu)建器,因此我們可以使用工廠類從構(gòu)建器構(gòu)建引擎。
Factory Bean
Java |
Groovy |
Kotlin |
import io.micronaut.context.annotation.Factory;
import jakarta.inject.Singleton;
@Factory
class EngineFactory {
@Singleton
EngineImpl buildEngine(EngineConfig engineConfig) {
return engineConfig.builder.build(engineConfig.crankShaft, engineConfig.getSparkPlug());
}
}
|
import io.micronaut.context.annotation.Factory
import jakarta.inject.Singleton
@Factory
class EngineFactory {
@Singleton
EngineImpl buildEngine(EngineConfig engineConfig) {
engineConfig.builder.build(engineConfig.crankShaft, engineConfig.sparkPlug)
}
}
|
import io.micronaut.context.annotation.Factory
import jakarta.inject.Singleton
@Factory
internal class EngineFactory {
@Singleton
fun buildEngine(engineConfig: EngineConfig): EngineImpl {
return engineConfig.builder.build(engineConfig.crankShaft, engineConfig.sparkPlug)
}
}
|
然后可以將返回的發(fā)動機(jī)注入需要發(fā)動機(jī)的任何地方。
可以從 PropertySource 實例之一提供配置值。例如:
供應(yīng)配置
Java |
Groovy |
Kotlin |
Map<String, Object> properties = new HashMap<>();
properties.put("my.engine.cylinders" ,"4");
properties.put("my.engine.manufacturer" , "Subaru");
properties.put("my.engine.crank-shaft.rod-length", 4);
properties.put("my.engine.spark-plug.name" , "6619 LFR6AIX");
properties.put("my.engine.spark-plug.type" , "Iridium");
properties.put("my.engine.spark-plug.companyName", "NGK");
ApplicationContext applicationContext = ApplicationContext.run(properties, "test");
Vehicle vehicle = applicationContext.getBean(Vehicle.class);
System.out.println(vehicle.start());
|
ApplicationContext applicationContext = ApplicationContext.run(
['my.engine.cylinders' : '4',
'my.engine.manufacturer' : 'Subaru',
'my.engine.crank-shaft.rod-length': 4,
'my.engine.spark-plug.name' : '6619 LFR6AIX',
'my.engine.spark-plug.type' : 'Iridium',
'my.engine.spark-plug.companyName': 'NGK'
],
"test"
)
Vehicle vehicle = applicationContext.getBean(Vehicle)
println(vehicle.start())
|
val applicationContext = ApplicationContext.run(
mapOf(
"my.engine.cylinders" to "4",
"my.engine.manufacturer" to "Subaru",
"my.engine.crank-shaft.rod-length" to 4,
"my.engine.spark-plug.name" to "6619 LFR6AIX",
"my.engine.spark-plug.type" to "Iridium",
"my.engine.spark-plug.company" to "NGK"
),
"test"
)
val vehicle = applicationContext.getBean(Vehicle::class.java)
println(vehicle.start())
|
上面的示例打?。骸癝ubaru Engine Starting V4 [rodLength=4.0, sparkPlug=Iridium(NGK 6619 LFR6AIX)]”
MapFormat
對于某些用例,可能需要接受可以提供給 bean 的任意配置屬性的映射,尤其是當(dāng) bean 代表第三方 API 時,其中并非所有可能的配置屬性都是已知的。例如,數(shù)據(jù)源可以接受特定于特定數(shù)據(jù)庫驅(qū)動程序的配置屬性映射,允許用戶在映射中指定任何所需的選項,而無需顯式編碼每個屬性。
為此,MapFormat 注釋允許您將映射綁定到單個配置屬性,并指定是接受鍵到值的平面映射,還是接受嵌套映射(其中值可能是附加映射)。
@MapFormat 示例
Java |
Groovy |
Kotlin |
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.core.convert.format.MapFormat;
import javax.validation.constraints.Min;
import java.util.Map;
@ConfigurationProperties("my.engine")
public class EngineConfig {
@Min(1L)
private int cylinders;
@MapFormat(transformation = MapFormat.MapTransformation.FLAT) //(1)
private Map<Integer, String> sensors;
public int getCylinders() {
return cylinders;
}
public void setCylinders(int cylinders) {
this.cylinders = cylinders;
}
public Map<Integer, String> getSensors() {
return sensors;
}
public void setSensors(Map<Integer, String> sensors) {
this.sensors = sensors;
}
}
|
import io.micronaut.context.annotation.ConfigurationProperties
import io.micronaut.core.convert.format.MapFormat
import javax.validation.constraints.Min
@ConfigurationProperties('my.engine')
class EngineConfig {
@Min(1L)
int cylinders
@MapFormat(transformation = MapFormat.MapTransformation.FLAT) //(1)
Map<Integer, String> sensors
}
|
import io.micronaut.context.annotation.ConfigurationProperties
import io.micronaut.core.convert.format.MapFormat
import javax.validation.constraints.Min
@ConfigurationProperties("my.engine")
class EngineConfig {
@Min(1L)
var cylinders: Int = 0
@MapFormat(transformation = MapFormat.MapTransformation.FLAT) //(1)
var sensors: Map<Int, String>? = null
}
|
注意注釋的轉(zhuǎn)換參數(shù);可能的值是 MapTransformation.FLAT(對于平面地圖)和 MapTransformation.NESTED(對于嵌套地圖)
EngineImpl
Java |
Groovy |
Kotlin |
@Singleton
public class EngineImpl implements Engine {
@Inject
EngineConfig config;
@Override
public Map getSensors() {
return config.getSensors();
}
@Override
public String start() {
return "Engine Starting V" + getConfig().getCylinders() +
" [sensors=" + getSensors().size() + "]";
}
public EngineConfig getConfig() {
return config;
}
public void setConfig(EngineConfig config) {
this.config = config;
}
}
|
@Singleton
class EngineImpl implements Engine {
@Inject EngineConfig config
@Override
Map getSensors() {
config.sensors
}
@Override
String start() {
"Engine Starting V$config.cylinders [sensors=${sensors.size()}]"
}
}
|
@Singleton
class EngineImpl : Engine {
override val sensors: Map<*, *>?
get() = config!!.sensors
@Inject
var config: EngineConfig? = null
override fun start(): String {
return "Engine Starting V${config!!.cylinders} [sensors=${sensors!!.size}]"
}
}
|
現(xiàn)在可以將屬性映射提供給 my.engine.sensors 配置屬性。
使用地圖配置
Java |
Groovy |
Kotlin |
Map<String, Object> map = new LinkedHashMap<>(2);
map.put("my.engine.cylinders", "8");
Map<Integer, String> map1 = new LinkedHashMap<>(2);
map1.put(0, "thermostat");
map1.put(1, "fuel pressure");
map.put("my.engine.sensors", map1);
ApplicationContext applicationContext = ApplicationContext.run(map, "test");
Vehicle vehicle = applicationContext.getBean(Vehicle.class);
System.out.println(vehicle.start());
|
ApplicationContext applicationContext = ApplicationContext.run(
['my.engine.cylinders': '8',
'my.engine.sensors' : [0: 'thermostat',
1: 'fuel pressure']],
"test"
)
def vehicle = applicationContext.getBean(Vehicle)
println(vehicle.start())
|
val subMap = mapOf(
0 to "thermostat",
1 to "fuel pressure"
)
val map = mapOf(
"my.engine.cylinders" to "8",
"my.engine.sensors" to subMap
)
val applicationContext = ApplicationContext.run(map, "test")
val vehicle = applicationContext.getBean(Vehicle::class.java)
println(vehicle.start())
|
上面的示例打?。骸癊ngine Starting V8 [sensors=2]”
更多建議: