Вы можете сделать это автоматически с PureConfig .
Добавить Pure Config к вам build.sbt
с:
libraryDependencies += "com.github.pureconfig" %% "pureconfig" % "0.11.0"
и перезагрузите оболочку sbt и обновите ваши зависимости.
Теперь предположим, что у вас есть следующий resource.conf
файл:
host: example.com
port: 80
user: admin
password: admin_password
Вы можете определить класс дел с именем AppConfig
:
case class AppConfig(
host: String,
port: Int,
user: String,
password: String
)
И создайте его экземпляр, заполненный конфигурацией приложения, используя метод loadConfig
:
import pureconfig.generic.auto._
val errorsOrConfig: Either[ConfigReaderFailures, AppConfig] = pureconfig.loadConfig[AppConfig]
Возвращает либо ошибку, либо ваш AppConfig, в зависимости от значений в самой конфигурации.
Например, если значение port
выше будет eighty
, вместо 80, вы получите подробную ошибку, сообщающую, что вторая строка конфигурации (с port: eighty
) содержала строку, но единственное допустимое ожидание тип это число:
ConfigReaderFailures(
ConvertFailure(
reason = WrongType(
foundType = STRING,
expectedTypes = Set(NUMBER)
),
location = Some(
ConfigValueLocation(
new URL("file:~/repos/example-project/target/scala-2.12/classes/application.conf"),
lineNumber = 2
)
),
path = "port"
)
)
Вы можете использовать loadConfigOrThrow
, если хотите получить AppConfig
вместо Either.
После того, как вы загрузите этот конфиг один раз в начале вашего приложения (как можно ближе к вашей основной функции), вы можете использовать внедрение зависимостей, чтобы передать его всем остальным классам, просто передав AppConfig в конструктор.
Если вы хотите подключить свой класс логики (и другие сервисы) к классу конфигурации с использованием MacWire , как предложил Кшиштоф в одном из своих вариантов, вы можете увидеть мой ответ здесь .
Простой пример (без MacWire ) выглядит следующим образом:
package com.example
import com.example.config.AppConfig
object HelloWorld extends App {
val config: AppConfig = pureconfig.loadConfigOrThrow[AppConfig]
val logic = new Logic(config)
}
class Logic(config: AppConfig) {
// do something with config
}
Где AppConfig определен в AppConfig.scala
package com.example.config
case class AppConfig(
host: String,
port: Int,
user: String,
password: String
)
В качестве бонуса, когда вы используете эту переменную конфигурации в вашей IDE, вы получите завершение кода.
Кроме того, ваша конфигурация может быть построена из поддерживаемых типов , таких как String, Boolean, Int и т. Д., А также из других классов case, которые построены из поддерживаемых типов (это так как case класс представляет объект значения, который содержит данные), а также списки и параметры поддерживаемых типов.
Это позволяет вам «классифицировать» сложный файл конфигурации и получить завершение кода. Например, в application.conf
:
name: hotels_best_dishes
host: "https://example.com"
port: 80
hotels: [
"Club Hotel Lutraky Greece",
"Four Seasons",
"Ritz",
"Waldorf Astoria"
]
min-duration: 2 days
currency-by-location {
us = usd
england = gbp
il = nis
}
accepted-currency: [usd, gbp, nis]
application-id: 00112233-4455-6677-8899-aabbccddeeff
ssh-directory: /home/whoever/.ssh
developer: {
name: alice,
age: 20
}
Затем определите класс конфигурации в вашем коде:
import java.net.URL
import java.util.UUID
import scala.concurrent.duration.FiniteDuration
import pureconfig.generic.EnumCoproductHint
import pureconfig.generic.auto._
case class Person(name: String, age: Int)
sealed trait Currency
case object Usd extends Currency
case object Gbp extends Currency
case object Nis extends Currency
object Currency {
implicit val currencyHint: EnumCoproductHint[Currency] = new EnumCoproductHint[Currency]
}
case class Config(
name: String,
host: URL,
port: Int,
hotels: List[String],
minDuration: FiniteDuration,
currencyByLocation: Map[String, Currency],
acceptedCurrency: List[Currency],
applicationId: UUID,
sshDirectory: java.nio.file.Path,
developer: Person
)
И загрузите его:
val config: Config = pureconfig.loadConfigOrThrow[Config]