【问题标题】:How do I perform an action on server startup in the Scala Play Framework?如何在 Scala Play 框架中对服务器启动执行操作?
【发布时间】:2016-07-27 00:32:58
【问题描述】:

我的conf/ 目录中有一个配置文件servers.conf,只要路由/servers 被命中,我的ServerController 就会读取该配置文件。这不是高性能的,因为当文件不会更改时,它需要在每次连续命中时重新读取配置文件。此外,如果配置文件有问题,我可以尽快告诉用户,而不是在页面点击时抛出异常。

目前我的ServerController.scala 中有这个:

case class Server(ip: String, port: String)

/**
  * This controller creates an `Action` to handle HTTP requests to the
  * application's server page.
  */
@Singleton
class ServerController @Inject() extends Controller {

  /**
    * Create an Action to render an HTML page with a the list of servers.
    * The configuration in the `routes` file means that this method
    * will be called when the application receives a `GET` request with
    * a path of `/servers`.
    */
  def index = Action {

    val serverList = ConfigFactory.load().getConfigList("servers")
    val servers: List[Server] = serverList match {
      case null => Nil
      case _ => serverList map { s =>
        Server(s.getString("ip"), s.getString("port"))
      } filter { s =>
        s.ip != null && s.port != null
      }.toList
    }

    Ok(views.html.servers(servers))
  }
}

我的目标是让服务器在启动时读取配置文件,如果在配置文件中读取没有问题,则在路由命中时将服务器列表传递给 ServerController。如果有问题,我希望立即抛出异常。

不过,我似乎找不到应用程序的入口点,所以我不知道如何在启动时执行操作。

有人知道怎么做吗?我正在使用 Play 2.5.x。

【问题讨论】:

  • 您使用的是哪个版本的 Play?
  • @Anton 对不起。编辑了问题。
  • 您是否考虑过将整个代码块放在索引函数之外? (它只在控制器启动一次时执行,即对该控制器发出的任何 HTTP 请求)

标签: scala playframework startup playframework-2.5


【解决方案1】:

如果您使用的是最新版本的 Play,它会在启动时查找根包中名为 Module 的任何类(也就是说,文件顶部没有 package 定义)。以下是 Play 2.5.x 的最新 Activator 模板的示例,我已对其进行了修改,以演示在应用程序启动和关闭时运行代码:

services/Say.scala 中,这将是一个简单的服务,可以说“你好!”在启动和“再见!”当应用程序关闭时:

package services

import javax.inject._
import play.api.inject.ApplicationLifecycle
import scala.concurrent.Future

trait Say {
  def hello(): Unit
  def goodbye(): Unit
}

@Singleton
class SayImpl @Inject() (appLifecycle: ApplicationLifecycle) extends Say {  
    override def hello(): Unit = println("Hello!")
    override def goodbye(): Unit = println("Goodbye!")

    // You can do this, or just explicitly call `hello()` at the end
    def start(): Unit = hello()

    // When the application starts, register a stop hook with the
    // ApplicationLifecycle object. The code inside the stop hook will
    // be run when the application stops.
    appLifecycle.addStopHook { () =>
        goodbye()
        Future.successful(())
    }

    // Called when this singleton is constructed (could be replaced by `hello()`)
    start()
}

在 Module.scala 中,

import com.google.inject.AbstractModule
import services._

/**
 * This class is a Guice module that tells Guice how to bind several
 * different types. This Guice module is created when the Play
 * application starts.

 * Play will automatically use any class called `Module` that is in
 * the root package. You can create modules in other locations by
 * adding `play.modules.enabled` settings to the `application.conf`
 * configuration file.
 */
class Module extends AbstractModule {

  override def configure() = {
    // We bind the implementation to the interface (trait) as an eager singleton,
    // which means it is bound immediately when the application starts.
    bind(classOf[Say]).to(classOf[SayImpl]).asEagerSingleton()
  }
}

您可能会发现有用的其他资源是the Scala dependency injection (DI) documentationthe Guice documentation。 Guice 是 Play 使用的默认 DI 框架。

【讨论】:

  • 嗯,它不是一个名为root 的目录。它只是一个存在于根包中的文件,也就是说它的顶部没有package 声明。或者,您可以在其他地方定义模块,例如在 modules 目录中,并在您的 application.conf 中启用它,如下所示:play.modules.enabled += "com.example.modules",其中 com.example.modules 应该是您的模块所属的包。
  • 我更新了我之前的评论,因为我误解了被问到的内容。默认情况下,它在模板中的app/ 中,但并非必须如此。只是不要将package 声明添加到Module.scala,它应该工作。
  • 这似乎只有在用户第一次点击/ 时才有效。我可以忍受这个,但是以前没有办法做到这一点吗?
  • 顺便说一句,这是一个很棒的答案。
  • @erip 我对 DI 还很陌生,但编译时 DI 可能正是您要找的:playframework.com/documentation/2.5.x/…
猜你喜欢
  • 1970-01-01
  • 2013-01-15
  • 2014-12-01
  • 2015-11-08
  • 2012-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多