19

I have a config file servers.conf in my conf/ directory that is read by my ServerController whenever the route /servers is hit. This isn't performant because it requires a re-read of the configuration file on each successive hit when the file won't change. Further if there are problems with the config file, I can tell the user ASAP rather than throw an exception on a page hit.

Currently I have this in my 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))
  }
}

My goal is to have the server read the config file at startup and pass the list of servers to the ServerController when the route is hit if there are no problems reading in the config file. If there are problems, I want an exception to be thrown immediately.

I can't seem to find an entry point for my application, though, so I don't know how to perform actions on startup.

Does anyone know how to do this? I'm using Play 2.5.x.

4

1 回答 1

21

如果您使用的是最新版本的 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()
  }
}

Scala 依赖注入 (DI) 文档Guice 文档是您可能会发现有用的其他一些资源。Guice 是 Play 使用的默认 DI 框架。

于 2016-04-06T15:14:06.057 回答