Spring Boot 2.0 讀書筆記_05:Beetl

3. Beetl

寫在開頭,Beetl是由《Spring Boot 2精髓》作者所開發並維護的後端模板引擎,主要用於渲染視圖模板。

關於模板引擎,博主瞭解過的主要是JSP 和 FreeMarker,視圖渲染技術的瞭解並不多。

這裏談一下自己對於Web開發的理解:基於現在的Web開發環境,前後端分離開發的思想,相對後端來講,很多時候是面向接口編程,拿當下火熱的前端漸進式組件框架Vue來講,於後端的交互無異於請求數據並返回,前端框架進行數據解析和視圖渲染,後端進行接口數據的獲取以及返回。

藉此閱讀技術書籍以及結合案例Demo的機會,來比較系統的瞭解一下這個後端模板引擎:Beetl

關於模板介紹,可以移步:http://ibeetl.com/ ,這裏就不贅述了,直接上手實操。

  • 依賴引入
    在Spring Boot中,beetl-framework-starter 將自動配置以 btl 結尾的所有視圖,將自動使用 Beetl 渲染響應的 resources/templates 目錄下的視圖文件

      <dependency>
      	<groupId>com.ibeetl</groupId>
      	<artifactId>beetl-framework-starter</artifactId>
      	<version>1.1.57.RELEASE</version>
      </dependency>
    
  • 設置定界符和佔位符
    記得當初使用JSP、ftl在頁面上寫腳本的時候,定界符和佔位符的概念就瞭解過了。
    有圖有真相:

    JavaWeb時期用過的JSP
    在這裏插入圖片描述

    SSM時期用過的FreeMarker
    在這裏插入圖片描述

    默認配置:
    <% %> 作爲定界符,${ }作爲佔位符

    可以通過配置文件來設置定界符和佔位符,需要在 resources 目錄下創建 beetl.properties 文件
    案例中給出了一種配置方式:佔位符使用:’${’ 和 ‘}’,定界符使用:’@’ 和 ‘回車換行’。

      DELIMITER_PLACEHOLDER_START=${
      DELIMITER_PLACEHOLDER_END=}
      DELIMITER_STATEMENT_START=@
      DELIMITER_STATEMENT_END=
    
  • 配置Beetl
    Beetl爲了提高渲染性能,會在模板渲染之後對語法解析結果進行緩存,每次渲染前都會對模板文件進行更新監測,因此這個地方會存在一次I/O操作,線上系統可以取消這個更新監測。application.properties文件中添加:

      beetl-beetlsql.dev = false 
    

    關於Web項目中 target目錄 沒有同步更新最新目錄文件以及資源的問題:IDEA不像Eclipse會自動將新保存的文件或目錄及其他資源更新到target目錄中,必須在pom.xml中設置

      <build>    
      <resources>
          <resource>
              <directory>src/main/java</directory>
              <includes>
                  <include>**/*.*</include>
              </includes>
          </resource>
          <resource>
              <directory>src/main/resources</directory>
              <includes>
                  <include>**/*.*</include>
              </includes>
          </resource>
      </resources>
      </build>
    

    接下來,maven clean、build就可以了,這個問題和mapper.xml文件無法加載的問題一致,原因都是在resource文件夾不被加入build行列。

  • GroupTemplate
    Beetl的核心是GroupTemplate,是一個重量級對象,實際使用的時候建議使用單例模式創建,創建GroupTemplate需要兩個參數,一個是模板資源加載器,一個是配置類。

    模板資源加載器Beetl內置了6種,分別是:

    模板資源加載器 說明
    StringTemplateResourceLoader 字符串模板加載器,用於加載字符串模板,如本例所示
    FileResourceLoader 文件模板加載器,需要一個根目錄作爲參數構造,傳入getTemplate方法的String是模板文件相對於Root目錄的相對路徑
    ClasspathResourceLoader 文件模板加載器,模板文件位於Classpath裏
    WebAppResourceLoader 用於webapp集成,假定模板根目錄就是WebRoot目錄,參考web集成章
    MapResourceLoader 可以動態存入模板
    CompositeResourceLoader 混合使用多種加載方式

    GroupTemplate創建過程:

      // 模板資源加載器
      StringTemplateResourceLoader resourceLoader = new StringTemplateResourceLoader();
      // 配置類
      Configuration cfg = Configuration.defaultConfiguration();
      // 單例模式創建
      GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
    

    配置類:通過自動注入的方式注入GroupTemplate,@PostConstruct作用於Config方法上,會在Spring啓動階段調用此方法,完成對GroupTemplate的拓展。

      @Configuration
      public class BeetlExtConfig {
      	@Autowired
      	GroupTemplate groupTemplate;
    
      	@Autowired
      	ApplicationContext applicationContext;
    
      	@PostConstruct
      	public void config() {
      			
      	}
      }
    

以下內容爲Beetl的基礎語法相關內容,該部分內容會進行提綱式的筆記整理,不會詳細講述語法相關內容。

  • 變量

    • 全局變量:在模板或者子模板中使用
      • request內置對象通過attribute在模板中均可直接通過name來引用。
      • Session提供了Session會話,這裏指的是HTTPSession。
      • parameter,讀取用戶提交參數。
      • ctxPath,Web應用中的ContextPath。在Spring Boot中,應用默認是 “/”。
      • servlet,WebVariable的實例,包含 HTTPSession、HTTPServletRequest、HTTPServletResponse三個屬性。模板中可以通過 request、response、session 來引用。
      • 所有groupTemplate的共享變量
    • 局部變量:只能在當前模板中使用
      • 變量類型同JavaScript類型一致,在Beetl中,任何與高精度數據進行算數運算後的結果會轉換爲BIgDecimal類型,對應Java中的BigDecimal類型。
    • 共享變量
      • 類似全局變量,可以在任何模板中使用,需要通過groupTemplate的API進行添加。

          定義:在注入GroupTemplate對象的配置類中,config方法中進行設置:
          groupTemplate.getSharedVars().put("key", value);
          使用:在任意末班中,通過佔位符進行使用:
          ${key}
        
    • 模板變量
      • 定義:變量的內容是模板對應的輸出

          @var a = 1 ; 
          @var template = { 
          <span>${a}</span〉
          @}
          輸出:${template}
        

        template是個變量,內容位於${ }中,內容是1。
        模板變量可以用在後面的任何地方,Beetl使用模板變量來完成繼承佈局方式。

  • 表達式

    • 算數表達式
      • 支持 +、-、*、/、% 等表達式,變量類型與相應的Java類型一致。

      • 需要用到高精度數代替Double時,需要在數字後面添加 “h”,對應Java中BigDecimal類型。

          例如:@var a = 123.25785236h;
        
      • 爲保證浮點計算準確,含有高精度數表達式的計算過程全部按照高精度進行計算

    • 邏輯表達式
      • 支持 >、<、==、!=、>=、<=、!、&&、||、?等條件表達式

      • 三元表達式只考慮true情況時,可以進行簡化

          例如:
          $var a = 1;
          ${a == 1? "bingo" : ""}
          ${a == 1? "bingo"}
          上述兩行代碼效果一致,在簡化的三元表達式中,false情況時,自動賦值爲null。
          Beetl佔位符對null值不做輸出。
        
  • 控制語句

    • 循環語句
      • for…in [elsefor]、for(exp; exp; exp)、while(exp)
        • for…in 循環中可以得到循環的上下文信息,會自動創建一個變量名+LP後綴的變量,提供循環的上下文信息

            例如:
            @for(user in userList!){
              <span>${userLP.index}</span>
            @}
          

          上面的例子中在表達式:user in userList 後添加了 “!”,來進行安全輸出。即若user不存在,或者爲空,不要報錯,不進入循環體。此外上下文對象userLP還有其他相關信息:

    屬性/方法 說明
    userLP.index 當前索引值,從1開始
    userLP.size 集合長度
    userLP.first 是否是第一個
    userLP.last 是否是最後一個
    userLP.even 索引是否是偶數
    userLP.odd 索引是奇數
    • 條件語句
      • if…else [else if]
      • 加強版switch case:select-case
        • 允許case中使用邏輯表達式

        • 不需要每個case都break,默認遇到符合條件的case執行後退出

          <%
            var b = 1;
            select {
             case b < 1, b > 10:
               print("out of range");
             default:
               print("error") 
            }
          %>
          
    • try…catch
      • 捕獲模板異常

          <%
          try{
            callOtherSystemView();
          } catch (error) {
            print(err.message);
          }
          %>
        
  • 函數調用

    常用函數 說明 舉例
    print/pringln 輸出對象,若對象爲空,則不輸出
    has 判斷是否具有這個全局變量 if(has(userList)){…}
    isEmpty 判斷變量或者表達式是否爲空
    debug 在控制檯打印變量或者表達式,附帶所在文件模板路徑信息
    date 日期函數,獲得當前日期
    trim 截取一個日期或者數字類型並返回字符串 trim(126.18, 1) -> 126.18
    trim(date(),‘yyyy’) -> 2018
    parseInt 將字符串或者number轉換爲對應類型 parseLong、parseDouble
    global 返回全局變量值,參數是字符串 var user = global(“user_” + i)
    cookie 返回指定的cookie對象 var userCookie = cookie(“user”)
    strutil.* 字符串系列函數 詳情參考Beetl使用手冊
    array.* 集合相關函數
    shiro.* 安全框架shiro的相關方法封裝 shiroExt類中有說明,並非內置函數
    spring.* Spring框架中可以使用的函數 spel表達式
    reg.* 正則表達式相關函數

    這裏說一下用到過的Shiro.hasPermission()方法吧,首先Shiro安全框架是集成進項目中來的,並配置了ShiroConfig配置類。權限控制的方式上採用基於URL的形式進行控制,在視圖層,使用Beetl提供的ShiroExt中的hasPermission方法,對模板中的URL字符串進行權限認證,ShiroExt在這一過程中只是做了封裝操作,獲取到權限URL參數、和當前Subject,並將參數交給Shiro框架進行處理。

      /**
      * 驗證當前用戶是否擁有指定權限,使用時與lacksPermission 搭配使用
      *
      * @param permission 權限名
      * @return 擁有權限:true,否則false
      */
      public boolean hasPermission(String permission) {
      	return getSubject() != null && permission != null
      	&& permission.length() > 0
      	&& getSubject().isPermitted(permission);
      }
    
      /**
      * 獲取當前 Subject
      *
      * @return Subject
      */
      protected static Subject getSubject() {
      	return SecurityUtils.getSubject();
      }
    
      // isPermitted是接口Subject下的一個實現方法
      boolean isPermitted(String permission);
    
      // 視圖層使用
      @if(shiro.hasPermission("/cta/add")){
      	<#button name="添加" icon="fa-plus" clickFun="Cta.openAddCta()"/>
      @}
    
  • 格式化函數
    允許在佔位符輸出的時候指定格式化函數來格式化輸出
    格式化格式:${exp, formatName=“可選參數”}

  • 以Java方式進行調用
    可以在模板中以Java方式調用表達式,使用時必須在表達式前用"@"符號標識

    • 定界符中使用

      <%
      	var size = @a.size();
      %>
      
    • 佔位符中使用

      ${ @user.getUserName() }
      
    • 需要注意的是:

      • 對於直接Java調用,GroupTemplate是可以配置進制此功能的。

      • 可以通過安全管理器配置Beetl不允許調用那些類。默認java.lang.Runtime 和 java.lang.Process不允許被調用。

      • 按照Java規範書寫類名、方法名以及屬性名

      • 內部類訪問機制:outerClass$innerClass

      • 表達式是Java風格,參數依然是Beetl表達式。

          ${@user.sayHello(user.name)}
          user.sayHello 是Java調用
          user.name 依然是Beetl表達式
        
  • 標籤函數

    • 功能等同於 jsp tag

    • layout:佈局標籤函數

        @layout("/inc/layout.html", {title:'主題'}){
          <p>Hello Beetl</p>
        @}
      

      layout標籤會把標籤體{ }部分內容渲染出來之後,傳給layout指定的模板頁面,默認接收變量名爲layoutContent,同時攜帶title變量進行傳遞。

        layout.html
        <title>${title}</title>
        <div>
          ${layoutContent}
        </div>
      
    • include:包含標籤函數

      @include("/inc/header.html", {title:"page header"}){...}
      <div>
      // main content
      </div>
      @include("/inc/footer.html"){}
      

      include標籤第一個參數是公共模板的路徑,其後可以接受一個map參數,map中的,每一項值都會傳遞給子模板作爲子模板的全局變量。

  • 安全輸出
    Beetl在變量表達式後面使用符號 “!” 來提醒Beetl此變量可能不存在,表達式將返回 “!” 後的表達式值,若沒有表達式,則返回null。

      @ var user = null;
      ${user.name!"無此人姓名信息"}
    

    當user爲null時,或者user.name爲null時,返回 “無此人姓名信息”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章