Play! 1.1 框架中的 Scala 模块

红薯 发布于 2011/01/11 15:02
阅读 1K+
收藏 3

Play! 1.1 包含了对 Scala 编程语言的支持,由于 Play 框架的灵活性,使得通过一个简单的模块即可支持 Scala ,要启用 Scala 支持只需要在 conf/application.conf 文件中配置:


接下来就可以在已有应用中使用 scala 来编写代码,当然也可以是 Java 和 Scala 混合编码。

目前 Play 开发团队正在进行非常密集的更新工作,因此该模块目前还是体验状态,不建议在生产环境中完全使用 Scala 来编写 Play 应用。

下面是使用 Scala 编写应用的步骤:

1. 创建支持 Scala 模块的新应用

play new myApp --with scala

一旦使用这个命令创建应用后,controllers 包中的 就变成了 Application.scala 文件:

package controllers

import play._
import play.mvc._

object Application extends Controller {

def index = render()


代码跟 Java 的非常类似,一般都可以看明白,只不过是 Scala 的语法。


接下来我们编辑 Application.scala 文件,更改其 render() 方法:

def index = "Hello scala !"

刷新页面后就看到了下面的 Play 框架提示的友好错误信息。

Scala 应用可以直接让某个方法返回值,如下所示:

def index = "<h1>Hello world</h1>"

如果返回类型像是二进制流,那么 Play 框架会自动使用 renderBinary() 方法来输出,例如输出一个验证码图片:

def index = Images.captcha


处理 Action 的参数,看起来比 Java 简单多了:

def index(name: String) = <h1>Hello {name}</h1>


def index(name: String = "Guest") = <h1>Hello {name}</h1>

如果使用了默认值,那么一旦请求中不包含指定的参数,Play 会自动用默认值替代。

Controllers composition using traits

A controller can use several traits to combine several interceptor.

Let’s define a Secure trait:

package controllers

import play.__
import play.mvc.__

trait Secure extends Controller {

def check {
session("user") match {
name: String => info("Logged as %s", name)
_ => Security.login


And you can them use it in the Application controller:

package controllers

object Application extends Controller with Secure {

def index = "Hello world"


How to define and access Models

Models can be defined not only in java but in scala as well. Unfortunately, due to the differences between the two langauges the Model API somewhat differs from the java version.

Main differences

  • fields are passed as contructor arguments
  • each and every class needs to extend Model[T]
  • helper methods should be defined in the companion object
  • companion objects need to extend Model[T]

here is an example:

class User(
var email: String,
var password: String,
var fullname: String
) extends Model[User] {
//instance methods
var isAdmin = false
override def toString = email
//finder methods
object User extends Model[User] {
def connect(email: String, password: String) = {
User.find("byEmailAndPassword", email, password).first

Running queries against Scala Models from Scala classes

The following methods are available when running queries against scala models:

def count(implicit m: M[T]) = i.count(m)
def count(q: String, ps: AnyRef*)(implicit m: M[T])
def findAll(implicit m: M[T])
def findById(id: Any)(implicit m: M[T])
def findBy(q: String, ps: AnyRef*)(implicit m: M[T])
def find(q: String, ps: AnyRef*)(implicit m: M[T])
def all(implicit m: M[T])
def delete(q: String, ps: AnyRef*)(implicit m: M[T])
def deleteAll(implicit m: M[T]) =
def findOneBy(q: String, ps: AnyRef*)(implicit m: M[T]): T
def create(name: String, ps: play.mvc.Scope.Params)(implicit m: M[T]): T

As you can see, it’s really similar to the java API, so for example to count the number of users, you can just call count on the User class:


One known limitation of the Scala Model API is that the save method is not working in a chained call fashion, so you always need to execute it on an instance, as you can see it later at the unit testing section

Running queries against Java Models from Scala classes

In certain situations it might be desirable to query models written in java from scala.
Since java models are not extending from the scala Model trait, Play needs to provide an alternative query interface which comes in the form of QueryRunner trait and the corresponding companion object. In order to utilize this feature you either need to import the query methods like

import play.db.jpa.QueryRunner._

or you can mix in the trait

class MyController extends Controller with QueryRunner {...} 

and the API is defined like this:

def count[T](implicit m: M[T]) = i.count(m)
def count[T](q: String, ps: AnyRef*)(implicit m: M[T])
def findAll[T](implicit m: M[T])
def findById[T](id: Any)(implicit m: M[T])
def findBy[T](q: String, ps: AnyRef*)(implicit m: M[T])
def find[T](q: String, ps: AnyRef*)(implicit m: M[T])
def all[T](implicit m: M[T])
def delete[T](q: String, ps: AnyRef*)(implicit m: M[T])
def deleteAll[T](implicit m: M[T]) =
def findOneBy[T <: JPASupport](q: String, ps: AnyRef*)(implicit m: M[T]): T
def create[T <: JPASupport](name: String, ps: play.mvc.Scope.Params)(implicit m: M[T]): T

Using the previous example User.count becomes count[User]

Unit Testing

ScalaTest support is integrated into Play, so one can easily write unit tests using ScalaTest, for example:

class SpecStyle extends UnitTest with FlatSpec with ShouldMatchers {
"Creating a user" should "be succesfull" in {
val user = new User("", "secret", "Bob")
bob = User.find("byEmail", "").first
bob should not be (null)
bob.fullname should be ("Bob")

我想问个问题,没弄过scala ,我刚生成项目,不知道为什么会有这种错误,我已经执行play install scala ,不知道为什么出现下面红色警告,而且,当我play run 的时候出错误Application.index action not found  ,这句代码module.scala=${play.path}/modules/scala-0.9.1添加了,这是怎么回事啊,我没有接触过scala

E:\work>play new play_scala_helloworld --with scala
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~ play! 1.2,
~ The new application will be created in E:\work\play_scala_helloworld
~ What is the application name? [play_scala_helloworld]
~ Resolving dependencies using E:\work\play_scala_helloworld\conf\dependencies.
~       play->scala 0.9.1 (from playLocalModules)
~ Some dependencies have been evicted,
~       play 1.2 is overriden by play 1.2.2
~ Installing resolved dependencies,
~       modules/scala-0.9.1 -> D:\play\modules\scala-0.9.1
~ *****************************************************************************
~ WARNING: These dependencies are missing, your application may not work proper
y (use --verbose for details),
~       play->play 1.2.2
~ *****************************************************************************
~ Some dependencies are still missing.
~ OK, the application is created.
~ Start it with : play run play_scala_helloworld
~ Have fun!