在 Spring Boot 中使用創新的 Groovy 模板引擎

工程 | Cédric Champeau | 2014 年 5 月 28 日 | ...

隨著 Spring Boot 1.1.0.M2 的發布,也開始支援 新的模板引擎,此引擎由 Groovy 2.3 提供。在這篇文章中,我們將描述使用此引擎的優點,當然還有如何在 Boot 中使用它。

這篇文章中的所有原始碼都可以在 GitHub 上找到,所以請隨意複製儲存庫並試用看看

git clone https://github.com/melix/springboot-groovytemplates.git
cd springboot-groovytemplates
./gradlew run

然後在您的瀏覽器中打開 https://127.0.0.1:8080

此應用程式完全以 Groovy 撰寫,並且也使用了 GORM for Boot,但當然可以僅將 Groovy 用於模板部分,而應用程式的其餘部分則以 Java 撰寫。從現在開始,我們將只關注此專案的模板方面。

相依性

在 Spring Boot 中整合 Groovy 2.3 模板非常容易。您只需要在您的建置檔案中新增對 groovy-templates 模組的相依性。例如,如果您使用 Gradle,只需使用這個

dependencies {
  compile "org.codehaus.groovy:groovy:${groovyVersion}"
  compile "org.codehaus.groovy:groovy-templates:${groovyVersion}"
  compile "org.springframework.boot:spring-boot-starter-web:${springBootVersion}"
  compile "org.grails:gorm-hibernate4-spring-boot:1.0.0.RELEASE"

  runtime "com.h2database:h2:1.3.173"
}

Groovy 模板

Groovy 標記模板引擎提供了一個基於建構器語法的創新模板系統。它提供各種主要功能

  • 階層式 (建構器) 語法,用於產生類似 XML 的內容 (特別是 HTML5)
  • 模板包含
  • 將模板編譯為位元組碼以實現快速渲染
  • 國際化
  • 用於共享結構模式的版面配置機制
  • 可選的型別檢查

以及更多!您可以在 文件 中找到此模板引擎功能的完整列表。模板基本上是 Groovy 程式碼,具有對模板用例的特殊支援。

讓我們從一個非常簡單的範例開始,我們想要顯示一個索引,其中包含一條簡單的訊息,其中包含目前正在使用的 Spring Boot 和 Groovy 版本號

yieldUnescaped '<!DOCTYPE html>'
html {
  head {
    title('Spring Boot - Groovy templates example')
    link(rel: 'stylesheet', href: '/css/bootstrap.min.css')
  }
  body {
    div(class: 'container') {
      div(class: 'navbar') {
        div(class: 'navbar-inner') {
          a(class: 'brand',
              href: 'http://beta.groovy-lang.org/docs/groovy-2.3.2/html/documentation/markup-template-engine.html',
              'Groovy - Template Engine docs')
          a(class: 'brand',
              href: 'hhttp://projects.spring.io/spring-boot/') {
            yield 'Spring Boot docs'
          }
        }
      }
      div("This is an application using Boot $bootVersion and Groovy templates $groovyVersion")
    }
  }
}

在第一行,您可以看到 yieldUnescaped 指令。它指示渲染器按原樣渲染參數。此指令可用於渲染任何基於文字的內容。在這裡,它用於渲染我們 HTML 檔案的 doctype 宣告,但您實際上可以使用它來渲染任何內容。模板引擎提供了許多輔助函數,例如 yield,這些函數在 文件 中有描述。

模板的其餘部分由與 HTML 輸出匹配的階層式結構組成,使其非常自然地渲染 HTML 內容。例如,程式碼:link(rel: 'stylesheet', href: '/css/bootstrap.min.css') 將被渲染為

<link rel='stylesheet' href='/css/bootstrap.min.css'/>

類似地,這個

a(class: 'brand',
  href: 'http://beta.groovy-lang.org/docs/groovy-2.3.2/html/documentation/markup-template-engine.html',
  'Groovy - Template Engine docs')

將被渲染為

<a class='brand' href='http://beta.groovy-lang.org/docs/groovy-2.3.2/html/documentation/markup-template-engine.html'>Groovy - Template Engine docs</a>

請注意模板中的屬性如何對應到渲染 HTML 中的標籤屬性。最後一個參數對應於標籤的主體。或者,可以使用 yield 指令來渲染標籤的主體

a(class: 'brand',
  href: 'http://beta.groovy-lang.org/docs/groovy-2.3.2/html/documentation/markup-template-engine.html') {
  yield 'Groovy - Template Engine docs'
}

選擇通常取決於您是否有巢狀內容要渲染。但到目前為止,我們模板產生的所有內容都是靜態的。模板的最後一部分更有趣

div("This is an application using Boot $bootVersion and Groovy templates $groovyVersion")

正如您猜到的,這將被渲染為

<div>This is an application using Boot 1.1.0.M2 and Groovy templates 2.3.2</div>

此處的模板使用了兩個在模型中找到的變數

  • bootVersion
  • groovyVersion

這些由我們的應用程式作為模板中的變數公開,所以讓我們看看我們是如何做到這一點的。

控制器

我們唯一需要做的是建立一個控制器來渲染我們的視圖,並且像往常一樣使用 Spring Boot,這只需要幾行程式碼

package sample

import org.springframework.boot.Banner
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.servlet.ModelAndView

@Controller
class SampleController {
  @RequestMapping("/")
  def home() {
    new ModelAndView(
        "views/home",
        [bootVersion: Banner.package.implementationVersion, 
         groovyVersion: GroovySystem.version])
  }
}

我們的 home 方法返回一個 ModelAndView 實例,並且模型僅包含兩個元素,即 Spring Boot 版本和 Groovy 版本。視圖會自動根據其參考 views/home 找到。Spring Boot 期望視圖在 src/main/resources/templates/views 中找到。還有比這更簡單的嗎?

真實世界資料

在真實世界中,模板不太可能如此簡單。您將擁有實體、資料庫、CRUD 操作等等... 所以下一步是向您展示如何使用新的模板引擎來渲染更複雜的模型。為此,我們使用了 GORM,所以我們將從定義一個名為 Person 的實體開始

package sample

import grails.persistence.*

@Entity
class Person {
  String firstName
  String lastName
}

我們想要做的是,例如

  • 列出資料庫中的人員
  • 新增/編輯新人員

所以我們將需要兩個模板:一個用於列出人員,另一個用於建立人員 (或編輯)。列表範例很有趣,因為它將讓我們向您展示如何在模板中迭代列表。所以在這之前,讓我們建立一個具有列表操作的控制器

@Controller
@RequestMapping("/person")
class PersonController {

  @RequestMapping("list")
  def list() {
    new ModelAndView('views/person/list', [persons: Person.list()])
  }
}

您可以看到,與我們在簡單範例中所做的類似,我們返回一個 ModelAndView 範例,但這次,模型包含一個人員列表。所以讓我們看看模板是什麼樣子的

yieldUnescaped '<!DOCTYPE html>'
html {
  head {
    title('Spring Boot - Groovy templates example')
    link(rel: 'stylesheet', href: '/css/bootstrap.min.css')
  }
  body {
    div(class: 'container') {
      div(class: 'navbar') {
        div(class: 'navbar-inner') {
          a(class: 'brand',
              href: 'http://beta.groovy-lang.org/docs/groovy-2.3.2/html/documentation/markup-template-engine.html',
              'Groovy - Template Engine docs')
          a(class: 'brand',
              href: 'hhttp://projects.spring.io/spring-boot/') {
            yield 'Spring Boot docs'
          }
        }
      }

      ul {
        persons.each { person ->
          li {
            a(href:"/person/$person.id", "$person.lastName $person.firstName")
          }
        }
      }

      div {
        a(href:'/person/add', 'Add new person')
      }
    }
  }
}

模板的大部分實際上對應於頁面的裝飾,並從原始模板複製而來。此時,您可能會想知道您可以做些什麼來改進這一點,但我們稍後會回到這一點,並專注於此模板最有趣的部分,即迭代

ul {
  persons.each { person ->
    li {
      a(href: "/person/$person.id", "$person.lastName $person.firstName")
    }
  }
}

遍歷 persons 變數是透過 Groovy 開發人員習慣使用的傳統 each 方法完成的。這是正常的,因為模板實際上是 Groovy 程式碼!所以我們可以迭代人員,我們給迭代中的當前人員一個名稱 (person),然後在 a 標籤內使用它。

如果資料庫中有幾個人員,則產生的 HTML 將如下所示

<ul>
  <li><a href='/person/1'>John Doe</a></li>
  <li><a href='/person/2'>Bob Dylan</a></li>
  <li><a href='/person/3'>Guillaume Laforge</a></li>
  <li><a href='/person/4'>Graeme Rocher</a></li>
  <li><a href='/person/5'>Dave Syer</a></li>
</ul>

所以如果您習慣使用 JSP、GSP 和任何類似 HTML 的模板系統,您可以立即看到這個模板引擎將讓您擺脫處理開啟/關閉標籤的臭名昭著的問題。而這僅僅是一個開始... 為了說明您可以如何簡化事情,我們將向您介紹版面配置機制。

如果您還記得,我們實際上有兩個模板共享一個共同的結構。它們都使用 Twitter Bootstrap,它們都共享相同的選單,最終,唯一改變的是頁面標題和主要內容。如果我們可以從我們的模板中提取出來並共享它,那會怎麼樣?

版面配置簡介

版面配置就是為此而生的。所以讓我們將我們模板的共同部分提取到一個 main.tpl 檔案中,我們將其儲存到 src/main/resources/templates/layouts

yieldUnescaped '<!DOCTYPE html>'
html {
  head {
    title(pageTitle)
    link(rel: 'stylesheet', href: '/css/bootstrap.min.css')
  }
  body {
    div(class: 'container') {
      div(class: 'navbar') {
        div(class: 'navbar-inner') {
          a(class: 'brand',
              href: 'http://beta.groovy-lang.org/docs/groovy-2.3.2/html/documentation/markup-template-engine.html',
              'Groovy - Template Engine docs')
          a(class: 'brand',
              href: 'hhttp://projects.spring.io/spring-boot/') {
            yield 'Spring Boot docs'
          }
        }
      }
      mainBody()
    }
  }
}

這看起來非常像標準模板,但您實際上可以找到兩個特殊之處

  • title(pageTitle),其中 pageTitle 預期是我們想要給出的頁面標題
  • mainBody(),這將導致渲染使用該版面配置的頁面的主要內容。請注意括號,這很重要。

現在讓我們更新首頁模板以使用此版面配置

layout 'layouts/main.tpl',
    pageTitle: 'Spring Boot - Groovy templates example with layout',
    mainBody: contents {
      div("This is an application using Boot $bootVersion and Groovy templates $groovyVersion")
    }

您可以看到我們呼叫了 layout 方法並為其提供了幾個參數

  • 要使用的版面配置檔案的名稱 (layouts/main.tpl)
  • pageTitle,一個簡單的字串
  • mainBody,使用 contents 區塊

當找到 mainBody() 指令時,使用 contents 區塊將觸發 mainBody 內容在版面配置內部的渲染。所以使用這個版面配置檔案,我們肯定在多個模板之間共享一個常見的結構模式。作為說明,讓我們看看 list.tpl 模板現在看起來如何

layout 'layouts/main.tpl',
    pageTitle: 'List persons',
    mainBody: contents {
      ul {
        persons.each { person ->
          li {
            a(href:"/person/$person.id", "$person.lastName $person.firstName")
          }
        }
      }

      div {
        a(href:'/person/add', 'Add new person')
      }
    }

當然,版面配置本身是可組合的,所以您可以在版面配置內部使用版面配置...

結論

在這篇文章中,我們向您展示了 Spring Boot 如何輕鬆使用 Groovy 在 Groovy 2.3 中引入的新模板引擎。這個模板引擎提供了一個非常自然且強大的語法來產生任何基於文字的內容。Groovy 文件中可以找到模板引擎功能的完整描述,並且在 Spring Boot 範例中可以找到使用相同技術的替代應用程式。

最後但並非最不重要的一點是,對此模板引擎的原生支援即將進入 Spring 4.1!所以期待未來更多 Groovy 的愛吧!

取得 Spring 電子報

與 Spring 電子報保持聯繫

訂閱

搶先一步

VMware 提供培訓和認證,以加速您的進展。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

查看全部