Play!Framework Series (3): Внедрение зависимостей

Java задняя часть Scala

Эта статья была опубликована 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.Мы подробно расскажем о некоторых сторонних платформах внедрения зависимостей, упомянутых в статье, в следующих статьях. Чтобы посмотреть примеры в этой статье, нажмитеИсходная ссылка