Эта статья была опубликована Shaw в блоге команды ScalaCool.
существуетСерия Play!Framework (2)В , мы представили структуру проекта Play. Мы все ежедневно используем внедрение зависимостей при работе с бизнес-логикой.Эта статья расскажет о внедрении зависимостей в Play! и о том, как их разумно использовать.
Зачем использовать «инъекцию зависимостей»
Во многих фреймворках Java «внедрение зависимостей» больше не является незнакомой технологией, а фреймворк Play рекомендуется начиная с версии 2.4.Guiceдля внедрения зависимостей.
Самым большим преимуществом использования внедрения зависимостей является «развязка», например:
существуетПредыдущийВ примере из статьи мы реализовали EmployeeService для работы с сотрудниками компании:
package services
import models._
class EmployeeSerivce{
...
}
В предыдущей реализации мы не добавляли операции с базой данных, поэтому теперь мы хотим ввести класс подключения к базе данных: DatabaseAccessService для подключения к базе данных, чтобы мы могли работать с соответствующей таблицей базы данных, затем:
Создайте новую службу для операций подключения к базе данных:
package services
class DatabaseAccessService{}
EmployeeSerivce должен зависеть от DatabaseAccessService:
package services
import models._
class EmployeeSerivce(db: DBService){
...
}
Хорошо, теперь нам нужно использовать EmployeeSerivce в EmployeeController, если мы не используем внедрение зависимостей, то:
class EmployeeController @Inject() (
cc: ControllerComponents
) extends AbstractController(cc) {
val db = new DatabaseAccessService()
val employeeSerivce = new EmployeeSerivce(db)
def employeeList = Action { implicit request: Request[AnyContent] =>
val employees = employeeSerivce.getEmployees()
Ok(views.html.employeeList(employees))
}
}
Можно видеть, что для создания экземпляра EmployeeSerivce, DatabaseAccessService также необходимо создать экземпляр. на других вещах, что делает наш код очень избыточным и крайне сложным в обслуживании.
Чтобы решить эту проблему, мы ввели внедрение зависимостей. Play поддерживает два типа внедрения зависимостей: «внедрение зависимостей во время выполнения» и «внедрение зависимостей во время компиляции». Далее мы решим проблему с помощью этих двух типов внедрения зависимостей. вопрос мы подняли выше.
Внедрение зависимостей во время выполнения (зависимость во время выполнения)
Внедрение зависимостей во время выполнения Play использует значение по умолчаниюGuice, О Guice мы расскажем в следующей статье, просто узнайте ее здесь. Для поддержки Guice и других сред внедрения зависимостей во время выполнения Play предоставляет ряд встроенных компонентов. смотрите подробностиplay.api.inject.
Итак, как мы будем использовать эту инъекцию зависимостей в Play? Возвращаясь к каштану, о котором мы говорили в начале этой статьи, теперь мы реорганизуем наш код с помощью внедрения зависимостей:
Во-первых, EmployeeSerivce должен полагаться на DatabaseAccessService, здесь на самом деле есть «инъекция зависимостей», поэтому мы можем реализовать это следующим образом:
package services
import models._
import javax.inject._
class EmployeeSerivce @Inject() (db: DBService){
...
}
В приведенном выше коде мы ввелиimport javax.inject._
, и вы можете увидеть еще один@Inject()
Аннотация, мы используем эту аннотацию для реализации внедрения зависимостей во время выполнения.
Затем в EmployeeController наш код становится таким:
class EmployeeController @Inject() (
employeeSerivce: EmployeeSerivce,
cc: ControllerComponents
) extends AbstractController(cc) {
def employeeList = Action { implicit request: Request[AnyContent] =>
val employees = employeeSerivce.getEmployees()
Ok(views.html.employeeList(employees))
}
}
Вы можете видеть, что нам больше не нужно писать так много экземпляров, нам просто нужно объявить, какие зависимости нам нужны, где нам нужны определенные зависимости, и Play будет внедрять нужные нам зависимости в соответствующие компоненты во время выполнения.
Подсказка:@Inject
Он должен располагаться после имени класса и перед параметрами конструктора.
«Внедрение зависимостей во время выполнения», как следует из названия, заключается в выполнении внедрения зависимостей во время работы программы, но это не может быть проверено во время компиляции.Чтобы программа могла проверять внедрение зависимостей во время компиляции, Play поддерживает «компилятор». . Внедрение зависимостей".
внедрение зависимостей во время компиляции
Чтобы реализовать внедрение зависимостей во время компиляции, нам нужно знать трейт, предоставляемый Play:ApplicationLoader, метод load в этом трейте будет загружать наше приложение при старте программы, во время этого процесса будет инстанцирован сам фреймворк Play и все, от чего зависит код нашей собственной программы.
По умолчанию Play предоставляет модуль Guice, и GuiceApplicationBuilder в этом модуле свяжет все компоненты, от которых зависит программа, в соответствии с контекстом, заданным платформой Play.
Если мы хотим настроить ApplicationLoader, нам также понадобится что-то вроде GuiceApplicationBuilder, благо Play предоставляет такую штуку, а именно:BuiltInComponentsFromContext, мы можем реализовать собственный ApplicationLoader, унаследовав этот класс.
Далее мы объясним далее через соответствующий код:
import controllers._
import play.api._
import play.api.routing.Router
import services._
import router.Routes
//自定义 ApplicationLoader
class MyApplicationLoader extends ApplicationLoader {
def load(context: Context): Application = {
new MyComponents(context).application
}
}
class MyComponents(context: Context)
extends BuiltInComponentsFromContext(context)
with play.filters.HttpFiltersComponents {
lazy val databaseAccessService = new DatabaseAccessService
lazy val employeeSerivce = new EmployeeSerivce(databaseAccessService)
lazy val employeeController = new EmployeeController(employeeSerivce, controllerComponents)
lazy val router: Router = new Routes(httpErrorHandler, employeeController)
}
Унаследовав BuiltInComponentsFromContext, программа может загружать некоторые компоненты, требуемые самой платформой Play, в соответствии с контекстом, предоставленным Play.
Итак, вернемся к нашему «внедрению зависимостей во время компиляции». Мы видим, что в классе MyComponents мы создаем экземпляры всех сервисов и внедряем эти экземпляры в соответствующие модули, которые от них зависят:
//将两个 service 实例化
lazy val databaseAccessService = new DatabaseAccessService
//EmployeeSerivce 依赖 DatabaseAccessService,将实例 databaseAccessService 注入其中
lazy val employeeSerivce = new EmployeeSerivce(databaseAccessService)
//将 employeeSerivce 注入到 employeeController 中
lazy val employeeController = new EmployeeController(employeeSerivce, controllerComponents)
При использовании BuiltInComponentsFromContext нам нужно самим реализовать роутер:
lazy val router: Router = new Routes(httpErrorHandler, employeeController)
подсказка: Следует отметить, что если мы реализуем собственный ApplicationLoader, нам нужноapplication.conf
Файл объявляет:
play.application.loader = MyApplicationLoader
Настраивая ApplicationLoader, мы реализуем внедрение зависимостей во время компиляции, тогда EmployeeSerivce становится:
package services
import models._
class EmployeeSerivce (db: DBService){
...
}
Как видите, здесь он опущен@Inject()
аннотация.
Аналогично для EmployeeController:
package controllers
import play.api._
import play.api.mvc._
import models._
import services._
// 没有了 @Inject() 注解
class EmployeeController (
employeeSerivce: EmployeeSerivce,
cc: ControllerComponents
) extends AbstractController(cc) {
...
}
Используя внедрение зависимостей во время компиляции, нам нужно только один раз создать все зависимости, и таким образом мы можем найти некоторые исключения в программе во время компиляции. Кроме того, есть некоторые проблемы с использованием этого метода, заключающиеся в том, что нам нужно написать много шаблонного кода. Кроме того, внедрение зависимостей во время компиляции этой статьи полностью выполняется вручную, что выглядит громоздким и не таким интуитивно понятным.Если мы хотим использовать более элегантный способ, мы можем использоватьmacwire, о чем мы подробно расскажем в следующей статье.
Эпилог
В этой статье кратко представлены два режима внедрения зависимостей, поддерживаемые Play.Мы подробно расскажем о некоторых сторонних платформах внедрения зависимостей, упомянутых в статье, в следующих статьях. Чтобы посмотреть примеры в этой статье, нажмитеИсходная ссылка