Lift是Scala编程语言领域中最早也是目前最为完善的原生Scala Web应用框架,具有独特的编程理念,本教程结合作者的实践以简洁易懂快速入门为目标,使开发者能快速进行基于Lift框架的Web应用和网站开发。
关于教程
作者:tanshuai,电邮: i@tanshuai.com
为了更好的服务于开发者,本教程将会持续更新,勘误并完善内容。若发现本文的错误,建议或意见均可联系本文作者。
本文主要对基于Scala 2.9的Lift当前的稳定版(Lift 2.4 release)进行讲解。
Lift Web 框架简介
Lift Web Framework是一个以高度安全性为特点的开源Web应用框架,使用Scala语言编写。Play Framework遵循传统的MVC(Model-View-Controller: 模型、视图和控制器)模式,这一点Lift与其有所不同。
安装和配置 Lift Web Framework 开发环境
安装 JDK
Scala作为Java虚拟机语言(JVM Language),与Java一样需要Java虚拟机才能编译和运行,因此开发者须首先安装Java开发工具包(JDK),亦可使用OpenJDK。
安装完JDK后,一定要检查环境变量是否设置正确,检验方法:
在指令行程序中任意路径位置,分别输入java -version和javac -version若输出正确的java和javac版本信息,即表示安装和环境变量设置正确;若输出的是"command not found"或"不是内部或外部指令,也不是可运行的程序"等错误提示,即表示没有安装成功或环境变量设置有误。
下载地址: http://www.oracle.com/technetwork/java/javase/downloads/
下载 Lift 入门工程项目
下载地址: http://../go/gslw-v1.zip
Lift同样使用Scala开发生态中的SBT(Simple Build Tool)作为编译、运行、测试、部署和配置管理系统,类似Java的Maven。
SBT通常以一个sbt-launch.jar文件通过Java指令启动,根据项目的SBT配置文件,自动下载该项目所需的全部工具和类库。SBT可由开发者自行安装在计算机中,作为各种项目的统一工具,也可放入工程项目中以便快速部署。
本文所提供的用于快速入门的工程项目内置了SBT,开发者下载解包后,根据不同的操作系统运行下列指令即可初始化和启动开发环境:
-
Window:
cd gslw sbt
-
Unix (Linux / Mac OS X ...)
$ cd gslw$ ./sbt
使用 Lift Web 开发环境
初始化 Lift Web 发环境
首次使用SBT,启动和执行run指令时,SBT会自动下载所需的工具和类库,如:Scala编译器、Scala基础库、Lift框架等相应开发和运行所必需的类库,这个过程根据网速快慢可能需要数十分钟至数个小时,开发者此时可以继续阅读本文下面的内容。
运行 Lift Web 应用
Scala和Lift的开发和测试均在SBT指令行状态下进行。进入项目所在路径启动SBT。
当出现>
时,表示开发环境准备就绪,可以进行开发和测试工作。
输入指令 s
并回车 :
> s
即可运行本文提供的工程项目Web应用,指令执行后输出以下类似信息时证明启动成功:
[info] Started SelectChannelConnector@0.0.0.0:8080 STARTING
[success] Total time: 5 s, completed Apr 25, 2013 5:50:52 PM
"Started" 表示Web应用已经运行成功,"SelectChannelConnector@0.0.0.0:8080" 指的是Web应用使用了本机8080端口作为Web服务器监听端口。
此时在浏览器中输入:http://localhost:8080,即可浏览该Web应用的效果。
此时SBT指令行仍然处于待命状态,可以输入新的SBT指令如:输入指令 k
并回车。
k
[info] stopped o.e.j.w.WebAppContext{/,[file:/gslw/src/main/webapp/]}
[success] Total time: 0 s, completed Apr 25, 2013 5:54:13 PM
输出上述信息即可证明Web应用停止成功,此时Web应用将不再运行。
代码修改和手动编译
在输入s
指令后,修改任何Scala代码文件都不会被编译和反映在网页上,需要使用 c
指令编译被修改的文件后再使用r
指令重新启动Web应用,修改的代码才会反映在浏览器的网页上。
运行时修改和自动编译
为了增加开发效率,Lift可以借助SBT动态检测代码的修改、自动编译并重新启动Web应用,使用如下指令达到启动运行时修改和自动编译的功能:
> ~;s;r
输出下面的信息即可证明具有自动编译功能的Web应用已经运行成功已经启动:
1. Waiting for source changes... (press enter to interrupt)
此时SBT命令行处于锁定状态,若想输入新的SBT命令,必须按下回车,重新回到命令行待命状态并取消自动编译功能。
在自动编译功能的的Web应用运行模式下,开发者可以随时修改代码,只需要对代码文件文件进行保存,SBT会自动编译该代码文件比重新启动Web应用。
当SBT发现任何代码文件被修改后,将立即对该文件进行编译并输出以下类似信息:
[info] Compiling 1 Scala source to /Getting-Started-with-Lift-Web/gslw/target/scala-2.9.1/classes...[success] Total time: 1 s, completed Apr 25, 2013 10:11:34 PM
此时为首次见到[success]
提示,表明编译成功,但尚不能在浏览器看到Web应用的任何变化,需要继续等待SBT重新启动Web应用,当第二次看到[success]
提示并再次显示Waiting for source changes... (press enter to interrupt)
时,方可以刷新浏览器看到修改代码后的网页最新效果。
清理编译后的文件
有时需要清空所有编译后的文件如各种.class文件以恢复全新的状态,在待命状态下输入:
> clean
可以尝试执行该清理指令再重新编译来解决开发过程中遇到一些难以理解的问题。
只编译(不运行)
有时在进行程序编码为了校验语法等目的,不需要运行程序,以提高开发效率只需要执行编译指令 c
:
> c
退出 SBT
在待命状态下输入:
> q
Scala 下的Web开发
Scala 工程目录结构
gslw/ 根目录
├── sbt SBT Unix 批处理脚本用于启动sbt-launch.jar
├── sbt.bat SBT Windows 批处理脚本用于启动sbt-launch.jar
├── sbt-launch.jar SBT 启动的Java可执行类库
├── build.sbt 主要的工程配置文件
├── project SBT工程文件所在的文件夹
│ ├── build.properties 保存所需的SBT版本信息,通常无需更改
│ └── plugins.sbt 告知SBT本工程所需要的插件以及下载位置
├── src Lift Web应用全部代码所在文件夹
│ └── main Lift Web应用主要代码所在文件夹,同级别的还可以有test文件夹
│ ├── resources Lift Web应用资源文件所在文件夹,用于存放配置信息等数据
│ ├── scala Scala语言代码所在的文件夹
│ │ ├── bootstrap 启动代码所在的文件夹
│ │ │ └── liftweb
│ │ │ └── Boot.scala 启动代码
│ │ └── code 主体代码所在的文件夹
│ │ ├── model 数据模型代码所在的文件夹
│ │ │ └── User.scala 用户模型
│ │ └── snippet 片段代码所在的文件夹
│ │ └── HelloWorld.scala 显示时间的代码
│ └── webapp HTML相关资源文件所在文件夹
│ ├── WEB-INF WAR(Web application ARchive) WEB应用的资源目录
│ │ └── web.xml Lift Web应用部署配置文件
│ ├── images 图像所在文件夹
│ ├── index.html 首页Lift HTML模板文件
│ ├── static 静态文件所在文件夹
│ │ └── index.html 静态index.html文件(非模板文件,直接输出不被解析)
│ └── templates-hidden 隐藏模板文件所在文件夹
│ ├── default.html 默认模板文件
└── target Web应用编译后的Java目标执行代码.class文件所在文件夹
从 Boot.scala 开始
Lift Web应用的入口为 src/main/scala/bootstrap/liftweb/Boot.scala
文件,该文件定义了Lift应用的运行环境,具体解读如下:
class Boot {
def boot {
// 连接H2数据库(无用户名和密码)
if (!DB.jndiJdbcConnAvailable_?) {
val user = Empty
val password = Empty
val vendor = new StandardDBVendor("org.h2.Driver","jdbc:h2:lift_proto.db;AUTO_SERVER=TRUE", user, password)
LiftRules.unloadHooks.append(vendor.closeAllConnections_! _)
DB.defineConnectionManager(DefaultConnectionIdentifier, vendor)
}
// 采用Lift的对象关系映射(ORM)功能构建数据库
// 此处将根据User对象定义在首次启动Lift时建立数据库中的表格和字段等信息
Schemifier.schemify(true, Schemifier.infoF _, User)
// 告知Lift从那些包中搜索代码片段(Snippet)
LiftRules.addToPackages("code")
// 构建网站地图(SiteMap)
def sitemap = SiteMap(
Menu.i("Home") / "index" >> User.AddUserMenusAfter // 定义index路径及其菜单
)
def sitemapMutators = User.sitemapMutator
// 设置网站地图
LiftRules.setSiteMapFunc(() => sitemapMutators(sitemap))
// 使用 jQuery 1.4 插件
LiftRules.jsArtifacts = net.liftweb.http.js.jquery.JQuery14Artifacts
// 强制请求为 UTF-8 编码
LiftRules.early.append(_.setCharacterEncoding("UTF-8"))
// 告知Lift判断用户是否登陆方法
LiftRules.loggedInTest = Full(() => User.loggedIn_?)
// 使用HTML5进行渲染
LiftRules.htmlProperties.default.set((r: Req) =>
new Html5Properties(r.userAgent))
// 将全部数据库查询(queries)绑定为一个数据库事务(transaction)
S.addAround(DB.buildLoanWrapper)
}
}
代码片段(Snippet)与网页模板
根据Boot.scala文件中sitemap设定的index,当访问 http://localhost:8080 时Lift会首先加载/Getting Started with Lift Web/src/main/webapp/index.html
模板文件。
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" />
<title>Home</title>
</head>
<body class="lift:content_id=main">
<div id="main" class="lift:surround?with=default;at=content">
<h2>Welcome to your project!</h2>
<p>
<span class="lift:HelloWorld.howdy">
Welcome to your Lift app at <span id="time">Time goes here</span>
</span>
</p>
</div>
</body>
</html>
<body class="lift:content_id=main">
表示定义从<body>
到 </body>
之间的HTML代码实际内容id为'main'的标签,只有标签id为main的内容会输出到页面上。
<div id="main" class="lift:surround?with=default;at=content">
定义该段<div>
标签id为main,这段标签中的全部内容将会输出到浏览器,lift:surround?with=default;at=content
表示为该端标签需要被另外的名为default
模板进行包围(surround),并将该段内容替换掉该外部default
模板的具有id为content
的标签中,此为调用和复用外部模板,该模板文件位于/Getting-Started-with-Lift-Web/gslw/src/main/webapp/templates-hidden/default.html
。
<span class="lift:HelloWorld.howdy">
表示此段标签的内容将处于HelloWorld类的howdy方法管理范围,Lift会将此段标签内容发送给howdy方法,并将方法处理后的内容输出到浏览器。