zoukankan      html  css  js  c++  java
  • 视频《一起学Beetl》记录

    以一个博客项目,实战使用

    项目git地址:https://gitee.com/gavink/beetl-blog

    视频《一起学Beetl》百度网盘下载: https://pan.baidu.com/s/1LyxAxlKpVXgVjwSXIbzBuA 提取码: 68im

    视频《一起学Beetl》在线播放地址:bilibili (可以调节清晰度): https://www.bilibili.com/video/av36278644/?p=1

    视频《一起学Beetl》作者博客:https://my.oschina.net/u/1590490?tab=newest&catalogId=6214598

    beetl官方文档:https://www.kancloud.cn/xiandafu/beetl3_guide/1992542

    本文目录:

    1、Beetl介绍
    2、集成SpringBoot2.x
    3、Beetl模板的基础用法 [变量、循环、条件]
        3.1、集成BeetlSql,用来查询数据库
        3.2、BeetlSQL demo
        3.3、修改模板文件为html结尾
        3.4、for-in循环的使用
        3.5、时间格式化
        3.6、条件语句 if 和 虚拟属性 size
    4、共享变量和自定义模板配置
        4.1、session域
        4.2、自定义模板配置
        4.3、ctxPath
    5、内置函数和安全输出
    6、定界符、占位符
    7、Beetl作用阶段
    8、标签函数 layout
    9、标签函数 include
    10、自定义HTML标签
    11、Beetl自定义方法以及直接访问java类方法
    12、ajax局部渲染
    13、重定向

    1、Beetl介绍    <--返回目录

      Beetl是什么:Beetl( 发音同Beetle ) 目前版本是3.2,相对于其他java模板引擎,具有功能齐全,语法直观,性能超高,以及编写的模板容易维护等特点。使得开发和维护模板有很好的体验。同时,Beetl具备引擎可定制性,可以打造自己的模板引擎。

      模板是一种设计模式,只要这种设计模式存在,就有Beetl的用武之地。Beetl可以用于

    • 动态页面生成,网站,后台管理系统等

    • 静态内容生成,比如互联网世界的大部分静态网页的生成

    • 短信,微信,邮件内容等生成

    • 二进制文档生成,比如PDF,Word (需要通过模板生成中间文本格式,如markdown,xml,然后通过工具转成二进制)

      Vue,React等JS框架符合现在前后端分离,但不代表所有项目,所有需求都是这样。除非你只定位自己是搞前后端分离的。一个项目,既可以用上Beetl,也可以用上Vue,比如Beetl+Vue,或者使用Beetl完成项目中的其他需求。

    2、集成SpringBoot2.x    <--返回目录

      项目结构

      pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.5.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.oy</groupId>
        <artifactId>boot-beetl</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>boot-beetl</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            
            <dependency>
                <groupId>com.ibeetl</groupId>
                <artifactId>beetl-framework-starter</artifactId>
                <version>1.1.68.RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>

      IndexController

    @Controller
    public class IndexController {
    
        @GetMapping("/")
        public String index(HttpServletRequest request){
            request.setAttribute("title","一起学Beetl");
            request.setAttribute("test","springboot 集成 beetl 一起来学呀");
            return "index1.btl";
        }
    }

      模板文件 index1.btl

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>${title}</title>
    </head>
    <body>
    ${test}
    </body>
    </html>

    3、Beetl模板的基础用法 [变量、循环、条件]    <--返回目录

    3.1、集成BeetlSql,用来查询数据库    <--返回目录

      创建数据库 create database beetl_test_blog;

      引入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.40</version>
    </dependency>

      application.properties中增加数据库配置

    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/beetl_test_blog?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&useInformationSchema=true
    spring.datasource.username=root
    spring.datasource.password=

      添加数据源配置类 DBConfig

    package com.oy;
    
    import javax.sql.DataSource;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.env.Environment;
    
    import com.zaxxer.hikari.HikariDataSource;
    
    @Configuration
    public class DBConfig {
    
        @Bean(name = "datasource")
        public DataSource datasource(Environment env) {
            HikariDataSource ds = new HikariDataSource();
            ds.setJdbcUrl(env.getProperty("spring.datasource.url"));
            ds.setUsername(env.getProperty("spring.datasource.username"));
            ds.setPassword(env.getProperty("spring.datasource.password"));
            ds.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
            return ds;
        }
            
    }

      测试数据源配置是否成功

    @Controller
    public class IndexController {
        
        @Autowired
        private DataSource dataSource;
    
        @GetMapping("/")
        public String index(HttpServletRequest request) throws SQLException{
            System.out.println(dataSource.getConnection());
            
            request.setAttribute("title","一起学Beetl");
            request.setAttribute("test","springboot 集成 beetl 一起来学呀");
            request.setAttribute("dataSource", dataSource);
            return "index1.btl";
        }
    }

      index1.btl

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>${title}</title>
    </head>
    <body>
    ${test}
    <br/>
    dataSource:${dataSource}
    </body>
    </html>

      启动项目,访问 http://localhost:8080/  。

    3.2、BeetlSQL demo    <--返回目录

       实体类 Blog

    /*
    CREATE TABLE `blog` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `content` text,
      `deleteFlag` bit(1) DEFAULT NULL,
      `img` varchar(100) DEFAULT NULL,
      `category` varchar(100) DEFAULT NULL,
      `title` varchar(100) DEFAULT NULL,
      `createTime` datetime DEFAULT NULL,
      `updateTime` datetime DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
     */
    package com.oy.model;
    
    import java.util.Date;
    
    import org.beetl.sql.core.annotatoin.Table;
    
    /**
     * @author oy
     * @version 1.0
     * @date 2020年11月5日
     * @time 下午9:28:29
     */
    @Table(name = "beetl_test_blog.blog")
    public class Blog {
        private Long id;
        private String content;
        private Boolean deleteFlag;
        private String img;
        private String category;
        private String title;
        private Date createTime;
        private Date updateTime;
    
        public Blog() {
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    
        public Boolean getDeleteFlag() {
            return deleteFlag;
        }
    
        public void setDeleteFlag(Boolean deleteFlag) {
            this.deleteFlag = deleteFlag;
        }
    
        public String getImg() {
            return img;
        }
    
        public void setImg(String img) {
            this.img = img;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public Date getCreateTime() {
            return createTime;
        }
    
        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }
    
        public Date getUpdateTime() {
            return updateTime;
        }
    
        public void setUpdateTime(Date updateTime) {
            this.updateTime = updateTime;
        }
    
        public String getCategory() {
            return category;
        }
    
        public void setCategory(String category) {
            this.category = category;
        }
    
        @Override
        public String toString() {
            return "Blog [id=" + id + ", content=" + content + ", deleteFlag=" + deleteFlag + ", img=" + img + ", category="
                    + category + ", title=" + title + ", createTime=" + createTime + ", updateTime=" + updateTime + "]";
        }
        
        
    }
    View Code

      BlogDao

    package com.oy.dao;
    import org.beetl.sql.test.BaseDao;
    import com.oy.model.Blog;
    
    public interface BlogDao extends BaseDao<Blog> {
    }

      BlogServiceImpl

    package com.oy.service.impl;
    import org.beetl.sql.core.engine.PageQuery;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import com.oy.dao.BlogDao;
    import com.oy.model.Blog;
    import com.oy.service.BlogService;
    
    @Service
    public class BlogServiceImpl implements BlogService {
    
        @Autowired
        private BlogDao blogDao;
    
        @Override
        public PageQuery<Blog> pageBlog(long pageNumber, long pageSize) {
            return blogDao.createLambdaQuery().
                    andEq(Blog::getDeleteFlag, false)
                    .page(pageNumber, pageSize);
        }
    
    }

      测试 IndexController

    package com.oy.controller;
    import java.sql.SQLException;
    import java.util.List;
    import javax.servlet.http.HttpServletRequest;
    import javax.sql.DataSource;
    import org.beetl.sql.core.engine.PageQuery;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import com.oy.model.Blog;
    import com.oy.service.BlogService;
    
    @Controller
    public class IndexController {
        
        @Autowired
        private DataSource dataSource;
        @Autowired
        private BlogService blogService;
    
        @GetMapping("/")
        public String index(HttpServletRequest request) throws SQLException{
            request.setAttribute("title","一起学Beetl");
            request.setAttribute("test","springboot 集成 beetl 一起来学呀");
            
            // 测试数据源配置是否成功
            System.out.println(dataSource.getConnection());
            request.setAttribute("dataSource", dataSource);
            
            // 测试 BeetlSQL集成是否成功
            PageQuery<Blog> pageQuery = blogService.pageBlog(1, 10);
            List<Blog> blogList = pageQuery.getList();
            blogList.stream().forEach(blog -> System.out.print(blog));
            
            return "index1.btl";
        }
        
    }

    3.3、修改模板文件为html结尾    <--返回目录

      在application.properties中增加配置

    beetl.suffix=html

    3.4、for-in循环的使用    <--返回目录

      往blog表中添加一些测试数据

    INSERT INTO `beetl_test_blog`.`blog`(`id`, `content`, `delete_flag`, `img`, `category`, `title`, `create_time`, `update_time`) VALUES (1, '博客1', b'0', 'img1', NULL, '标题1', '2020-11-05 21:44:29', '2020-11-05 21:44:34');
    
    INSERT INTO `beetl_test_blog`.`blog`(`id`, `content`, `delete_flag`, `img`, `category`, `title`, `create_time`, `update_time`) VALUES (2, '博客2', b'0', 'img2', NULL, '标题2', '2020-11-05 21:44:29', '2020-11-05 21:44:34');
    
    INSERT INTO `beetl_test_blog`.`blog`(`id`, `content`, `delete_flag`, `img`, `category`, `title`, `create_time`, `update_time`) VALUES (3, '博客3', b'0', 'img3', NULL, '标题3', '2020-11-05 21:44:29', '2020-11-05 21:44:34');
    View Code

      IndexController

    @Controller
    public class IndexController {
        
        @Autowired
        private DataSource dataSource;
        @Autowired
        private BlogService blogService;
    
        @GetMapping("/")
        public String index(HttpServletRequest request) throws SQLException{
            request.setAttribute("title","一起学Beetl");
            request.setAttribute("test","springboot 集成 beetl 一起来学呀");
            
            // 测试数据源配置是否成功
            System.out.println(dataSource.getConnection());
            request.setAttribute("dataSource", dataSource);
            
            // 测试 BeetlSQL集成是否成功
            PageQuery<Blog> pageQuery = blogService.pageBlog(1, 10);
            List<Blog> blogList = pageQuery.getList();
            blogList.stream().forEach(blog -> System.out.print(blog));
            
            request.setAttribute("page",pageQuery);
            
            return "index1.html";
        }
        
    }

      模板页面使用 for-in 渲染 List<Blog>数据

      index1.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>${title}</title>
    </head>
    <body>
    ${test}
    <br/>
    dataSource:${dataSource}
    
    <br/><br/>
    <!-- 循环取出page中的List<Blog> -->
    <% for (blog in page.list) { %>
    博客标题:${blog.title}<br/>
    博客内容:${blog.content}<br/>
    创建时间:${blog.createTime}<br/><br/> <% } %> </body> </html>

      效果:

    3.5、时间格式化    <--返回目录

      ${blog.createTime, "yyyy-MM-dd HH:mm:ss.SSS"}

    <!-- 循环取出page中的List<Blog> -->
    <% for (blog in page.list) { %>
    博客标题:${blog.title}<br/>
    博客内容:${blog.content}<br/>
    创建时间:${blog.createTime, "yyyy-MM-dd"}<br/><br/>
    <% } %>

    3.6、条件语句 if 和 虚拟属性 size    <--返回目录

      虚拟属性 for (var i = 0; i < page.list.~size; i = i + 2)

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    
    <style type="text/css">
       .even {
            background-color:#aaa;       
       }    
       .odd {
            background-color:#eee;       
       }  
    </style>
    
    <title>${title}</title>
    </head>
    <body>
    ${test}
    <br/>
    dataSource:${dataSource}
    
    <br/><br/>
    <!-- 循环取出page中的List<Blog> -->
    <% for (var i = 0; i < page.list.~size; i = i + 2) { %>
    
        <ul type="disc">
            <li class="even">博客标题:${page.list[i].title}</li>
            <li class="even">博客内容:${page.list[i].content}</li>
            <li class="even">创建时间:${page.list[i].createTime, "yyyy-MM-dd HH:mm:ss.SSS"}</li>
        </ul>
        <% if (i+1 < page.list.~size) { %>
            <ul type="disc">
                <li class="odd">博客标题:${page.list[i+1].title}</li>
                <li class="odd">博客内容:${page.list[i+1].content}</li>
                <li class="odd">创建时间:${page.list[i+1].createTime, "yyyy-MM-dd HH:mm:ss.SSS"}</li>
            </ul>  
        <% } %>
        
    <br/>
    
    <% } %>
    </body>
    </html>

    4、共享变量和自定义模板配置    <--返回目录

    4.1、session域    <--返回目录

    public class IndexController {
        
        @GetMapping("/")
        public String index(HttpServletRequest request) {
            request.getSession().setAttribute("title","我的博客");
            return "index1.html";
        }
        
        @GetMapping("/page/index2")
        public String index2() {
            return "index2.html";
        }
        
    }

      index2.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>${session.title}</title>
    </head>
    <body>
        index2
    </body>
    </html>

      注意:先访问 "/"才在session中添加变量title,如果先访问"/page/index2"则此时没有title变量。

    4.2、自定义模板配置    <--返回目录

      共享变量:所有模板都可以使用的变量。

      BeetlConfig

    package com.oy;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.beetl.core.GroupTemplate;
    import org.beetl.core.resource.ClasspathResourceLoader;
    import org.beetl.ext.spring.BeetlGroupUtilConfiguration;
    import org.beetl.ext.spring.BeetlSpringViewResolver;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class BeetlConfig {
    
        //模板根目录 ,比如 "templates"
        @Value("${beetl.templatesPath}") String templatesPath;
        @Value("${blog.title}") String title;
    
        @Bean
        public GroupTemplate getGroupTemplate(BeetlGroupUtilConfiguration beetlGroupUtilConfiguration) {
            GroupTemplate gt = beetlGroupUtilConfiguration.getGroupTemplate();
            Map<String,Object> shared = new HashMap<>();
            // 设置共享变量
            shared.put("blogSiteTitle", title);
            shared.put("blogCreateUser", "oy");
            gt.setSharedVars(shared);
            return gt;
        }
    
    
        @Bean
        public BeetlGroupUtilConfiguration getBeetlGroupUtilConfiguration() {
            BeetlGroupUtilConfiguration beetlGroupUtilConfiguration = new BeetlGroupUtilConfiguration();
            //获取Spring Boot 的ClassLoader
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            if(loader==null){
                loader = BeetlConfig.class.getClassLoader();
            }
            ClasspathResourceLoader cploder = new ClasspathResourceLoader(loader,
                    templatesPath);
            beetlGroupUtilConfiguration.setResourceLoader(cploder);
            beetlGroupUtilConfiguration.init();
            //如果使用了优化编译器,涉及到字节码操作,需要添加ClassLoader
            beetlGroupUtilConfiguration.getGroupTemplate().setClassLoader(loader);
            return beetlGroupUtilConfiguration;
    
        }
    
        @Bean(name = "beetlViewResolver")
        public BeetlSpringViewResolver getBeetlSpringViewResolver(BeetlGroupUtilConfiguration beetlGroupUtilConfiguration) {
            BeetlSpringViewResolver beetlSpringViewResolver = new BeetlSpringViewResolver();
            beetlSpringViewResolver.setContentType("text/html;charset=UTF-8");
            beetlSpringViewResolver.setOrder(0);
            beetlSpringViewResolver.setConfig(beetlGroupUtilConfiguration);
            return beetlSpringViewResolver;
        }
    }

    4.3、ctxPath    <--返回目录

    <#footer style=”simple”/>
    <#richeditor id=”rid” path="${ctxPath}/upload" name=”rname”  maxlength=”${maxlength}”> ${html} …其他模板内容   </#richdeitor>
    <#html:input  id=’aaa

    5、内置函数和安全输出    <--返回目录

      内置函数:https://www.kancloud.cn/xiandafu/beetl3_guide/1992563

      安全输出(重要)

    安全输出是任何一个模板引擎必须重视的问题,否则,将极大困扰模板开发者。Beetl中,如果要输出的模板变量为null,则beetl将不做输出,这点不同于JSP,JSP输出null,也不同于Freemarker,如果没有用!,它会报错.

    模板中还有俩种情况会导致模板输出异常

    • 有时候模板变量并不存在,这时候必须报错,如果简单忽略不输出(Velocity就这样),很容易留坑
    • 模板变量为null,但输出的是此变量的一个属性,如${user.wife.name}

    针对前俩种情况,可以在变量引用后加上!以提醒beetl这是一个安全输出的变量,变量确实有可能不存在

    如${user.wife.name! },即使user不存在,或者user为null,或者user.wife为null,或者user.wife.name为null beetl都不将输出

    可以在!后增加一个常量(字符串,数字类型等),或者另外一个变量,方法,本地调用,作为默认输出,譬如:

    ${user.wife.name!”单身”},如果user为null,或者user.wife为null,或者user.wife.name为null,输出”单身”

    譬如

    ${user.birthday!@System.constants.DefaultBir}, 表示如果user为null,或者user. birthday为null,输出System.constants.DefaultBir

    还有一种情况很少发生,但也有可能,输出模板变量发生的任何异常,如变量内部抛出的一个异常

    这需要使用格式${!(变量)},这样,在变量引用发生任何异常情况下,都不作输出,譬如

    ${!(user.name)},,beetl将会调用user.getName()方法,如果发生异常,beetl将会忽略此异常,继续渲染

    值得注意的是,在变量后加上!不仅仅可以应用于占位符输出(但主要是应用于占位符输出),也可以用于表达式中,如:

    <%
    var k = user.name!'N/A'+user.age!;
    %>
    <%
    ${k}
    %>

    如果user为null,则k值将为N/A

    在有些模板里,可能整个模板都需要安全输出,也可能模板的部分需要安全输出,使用者不必为每一个表达式使用!,可以使用beetl的安全指示符号来完成安全输出 如:

    <%
    DIRECTIVE SAFE_OUTPUT_OPEN;
    %>
    ${user.wife.name}
    模板其他内容,均能安全输出……
    <%
    //关闭安全输出。
    DIRECTIVE SAFE_OUTPUT_CLOSE;
    %>

    Beetl不建议每一个页面都使用DIRECTIVE SAFE_OUTPUT_OPEN,这样,如果真有不期望的错误,不容易及时发现,其次,安全输出意味着beetl会有额外的代码检测值是否存在或者是否为null,性能会略差点。所以建议及时关闭安全输出(这不是必须的,但页面所有地方是安全输出,可能不容易发现错误)

    如果你的所有模板都想安全输出,可以配置,但不推荐。严格了错误,就像try catch吃掉异常一样不容易发现这是个错误

    SAFE_OUTPUT=true
    

    在for-in 循环中 ,也可以为集合变量增加安全输出指示符号,这样,如果集合变量为null,也可以不进入循环体,如:

    <%
    var list = null;
    for(item in list!){
    
    }elsefor{
      print("no data");
    }
    %>
    变量是否存在
    <%
    if(has(flag)){
            print("flag变量存在,可以访问")
    }
    %>

    如果需要判断变量是否存在,如果存在,还有其他判断条件,通常都这么写

    <%
    if(has(flag)&&flag==0){
            //code
    }
    %>

    如果flag存在,而且值是0,都将执行if语句

    但是,有更为简便的方法是直接用安全输出,如

    <%
    if(flag!0==0){
      //code
    }
    %>

    flag!0 取值是这样的,如果flag不存在,则为0,如果存在,则取值flag的值,类似三元表达式 if((has(flag)?flag:0)==0)

     

    安全输出表达式可以包括

    • 字符串常量,如 ${user.count!"无结果"}
    • boolean常量 ${user.count!false}
    • 数字常量,仅限于正数,因为如果是负数,则类似减号,容易误用,因此,如果需要表示负数,请用括号,如${user.count!(-1)}
    • class直接调用,如${user.count!@User.DEFAULT_NUM}
    • 方法调用,如 ${user.count!getDefault() }
    • 属性引用,如 ${user.count!user.maxCount }
    • 任何表达式,需要用括号

    6、定界符、占位符    <--返回目录

    通俗易懂的说:

    定界符就是 界定动态beetl语言 与 html静态代码之间的符号。 比如,在<%%>中间的代码,是beetl代码(被包裹的代码会被Beetl模板引擎编译), 而定界符之外的代码就是html静态代码(beetl语法不会生效)。

    <%if(a==1){%>
    <a href="xxxx">跳转</a>
    <%}%>

    占位符,就是在静态代码中占一个位置。占位符中可以使用表达式,以及函数,占位符包裹住的代码会被Beetl引擎编译。

    比如下面的url:

    <a href="${url}">跳转</a>

    因为都是beetl代码,所以在定界符中,不需要在使用占位符包裹变量,可以直接使用变量名!

    定界符与占位符的默认配置如下:

    #占位符开始符号
    DELIMITER_PLACEHOLDER_START=${
    #占位符的结束符号
    DELIMITER_PLACEHOLDER_END=}
    #定界符开始符号
    DELIMITER_STATEMENT_START=<%
    #定界符结束符号
    DELIMITER_STATEMENT_END=%>

    7、Beetl作用阶段    <--返回目录

    Beetl的主要作用是把 数据(变量)+ 模板 => 编译成 静态代码。

    静态代码产生之后就没有 Beetl 什么事情了,浏览器打开静态代码才开始执行JS脚本。

    8、标签函数 layout    <--返回目录

    所谓标签函数,即允许处理模板文件里的一块内容,功能等于同jsp tag。

    如Beetl内置的layout标签

    index.html

    <%
    layout("/inc/layout.html",{title:'主题'}){
    %>
    Hello,this is main part
    <% } %>

    layout.html

    title is ${title}
    body content ${layoutContent}
    footer

    第1行变量title来自于layout标签函数的参数

    第2行layoutContent 是layout标签体{}渲染后的结果

    关于layout标签,参考高级主题布局

    layout标签函数,相当于把公共部分抽取出来,包裹主单个页面的个性化内容。

    9、标签函数 include    <--返回目录

    include 标签与JSP、freemark等其他的模板引擎类似。

    在一个模板中包含另一个模板,

    第一个参数是模板路径,

    第二个参数是一个json对象,用来向include的模板传递参数。

    <%include("/common/page.html",{page:msgPage,action:"detail",condition:"&id="+blog.id!}){}%>

    以下是page.html的模板内容

    <div class="paging">
        <%if(page.pageNumber <= 1){%>
        <a href="#">首页</i></a>
        <%}else{%>
        <a href="${ctxPath}/${action!}?pageNumber=${page.pageNumber-1}${condition!}">上一页</i></a>
        <%}%>
    
        <%if(page.pageNumber >= page.totalPage){%>
        <a href="#">末尾页</i></a>
        <%}else{%>
        <a href="${ctxPath}/${action!}?pageNumber=${page.pageNumber+1}${condition!}">下一页</i></a>
        <%}%>
    </div>

    10、自定义HTML标签    <--返回目录

    Beetl 也支持HTML tag形式的标签,个人认为,这是一种引用模板更加优雅的实现,能和静态HTML标签融为一体。

    比如在一个模板中引用另外一个模板,我们可以使用include

    <%include("/common/page.html",{page:msgPage,action:"detail",condition:"&id="+blog.id!}){}%>

    我们也可以为 page.html定义一个HTML标签,那他的调用方法就变成了下面这种。

    <#page page="${msgPage}" condition='${"&id="+blog.id!}' action="detail"/>

    是不是更加符合,静态HTML标签的习惯。

    一、配置 HTML标签默认配置

    #支持HTML标签
    HTML_TAG_SUPPORT = true
    
    #标签以#号开头识别为HTML标签
    HTML_TAG_FLAG = #
    
    #自定义标签文件Root目录和后缀
    RESOURCE.tagRoot = htmltag
    RESOURCE.tagSuffix = tag

    如果想修改配置,直接在根目录下的 beetl.properties 当中修改覆盖就行

    二、使用 在templates(你定义的模板目录)下新建htmltag,并且新建标签page.tag,定义标签内容:

    <div class="paging">
        <%if(page.pageNumber <= 1){%>
        <a href="#">首页</i></a>
        <%}else{%>
        <a href="${ctxPath}/${action!}?pageNumber=${page.pageNumber-1}${condition!}">上一页</i></a>
        <%}%>
    
        <%if(page.pageNumber >= page.totalPage){%>
        <a href="#">末尾页</i></a>
        <%}else{%>
        <a href="${ctxPath}/${action!}?pageNumber=${page.pageNumber+1}${condition!}">下一页</i></a>
        <%}%>
    </div>

    在需要调用标签的模板中写入:

    <#page page="${msgPage}" condition='${"&id="+blog.id!}' action="detail"/>

    Beetl自定义标签的传参形式,与HTML习惯保持一致,采用“属性=值”的方式,值必须使用双引号或者单引号,引起来。

    三、其他的注意事项

    • 可以在自定义标签里引用标签体的内容,标签体可以是普通文本,beetl模板,以及嵌套的自定义标签等。如上<#richeditor 标签体里,可用“tagBody”来引用
    • HTML自定义标签 的属性值均为字符串 如<#input value="123" />,在input.tag文件里 变量value的类型是字符串
    • 可以在属性标签里引用beetl变量,如<#input value="${user.age}" />,此时在input.tag里,value的类型取决于user.age
    • 在属性里引用beetl变量,不支持格式化,如<#input value="${user.date,'yyyy-MM-dd'}"/>,如果需要格式化,需要在input.tag文件里自行格式化
    • 在标签属性里传json变量需要谨慎,因为json包含了"}",容易与占位符混合导致解析出错,因此得使用""符号,如<#input value="${ {age:25} }" />
    • html tag 属性名将作为 其对应模板的变量名。如果属性名包含“-”,则将转为驼峰命名的变量,如data-name,转为dataName
    • 默认机制下,HTMLTagSupportWrapper2 实现了标签(2.8.x以前使用HTMLTagSupportWrapper)

    11、Beetl自定义方法以及直接访问java类方法    <--返回目录

    11.1、自定义方法

    我们想要实现类似于${ strutil.subString ("hello",1)} 这样的方法,在Beetl模板中直接使用函数。

    一、集成Function 接口,实现call方法

    call方法存在两个参数,一个是模板传输过来的参数列表Object[] objects,另外一个是beetl模板的上下文Context

    上下文中包含以下信息,需要用到可以自取。

    • byteWriter 输出流
    • template 模板本身
    • gt GroupTemplate
    • globalVar 该模板对应的全局变量
    • byteOutputMode 模板的输出模式,是字节还是字符
    • safeOutput 模板当前是否处于安全输出模式
    • 其他属性建议不熟悉的开发人员不要乱动

    有几个注意事项,需要注意下:

    • call方法要求返回一个Object,如果无返回,返回null即可
    • 为了便于类型判断,call方法最好返回一个具体的类,如date函数返回的就是java.util.Date
    • call方法里的任何异常应该抛出成Runtime异常

    实现代码如下:

    package com.ibeetl.blog.function;
    
    import org.beetl.core.Context;
    import org.beetl.core.Function;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @author GavinKing
     * @ClassName: PrintTime
     * @Description:
     * @date 2018/12/11
     */
    public class PrintTime implements Function {
        @Override
        public String call(Object[] objects, Context context) {
            Date date = (Date) objects[0];
    
            Date now = new Date();
            Long fiveM = date.getTime() + (5*60*1000);
            Long thM = date.getTime() + (30*60*1000);
            Long oneH = date.getTime() + (60*60*1000);
            if(now.getTime() < fiveM){
                return "刚刚";
            }
            if(now.getTime() < thM){
                return "半小时前";
            }
            if(now.getTime() < oneH){
                return "一小时前";
            }
    
            SimpleDateFormat sdf = new SimpleDateFormat(objects[1].toString());
    
            return sdf.format(date);
        }
    }

    二、注册function函数

    在beetl.properties 中,增加 FN.printTime = com.ibeetl.blog.function.PrintTime 进行注册。

    三、在模板中使用

    ${printTime(msg.createTime!,"yyyy-MM-dd HH:mm:ss")}

    这就可以使用我们自定义的方法了。

    11.2、直接访问java类与方法

    直接使用java方法,步骤没有那么复杂。

    可以通过符号@来表明后面表达式调用是java风格,可以调用对象的方法,属性,比如:

    ${@user.getMaxFriend(“lucy”)}
    ${@user.maxFriend[0].getName()}
    ${@com.xxxx.constants.Order.getMaxNum()}
    ${@com.xxxx.User$Gender.MAN}
    <%
    var max = @com.xxxx.constants.Order.MAX_NUM;
    var c =1;
    var d = @user.getAge(c);
    %>

    可以调用instance的public方法和属性,也可以调用静态类的属性和方法 ,需要加一个 @指示此调用是直接调用class,其后的表达式是java风格的。

     我们在代码中新建一个类

    package com.ibeetl.blog.function;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @author GavinKing
     * @ClassName: PrintTimeUtil
     * @Description:
     * @date 2018/12/11
     */
    public class PrintTimeUtil {
    
        public static String printTime(Date date,String format){
    
            Date now = new Date();
            Long fiveM = date.getTime() + (5*60*1000);
            Long thM = date.getTime() + (30*60*1000);
            Long oneH = date.getTime() + (60*60*1000);
            if(now.getTime() < fiveM){
                return "刚刚";
            }
            if(now.getTime() < thM){
                return "半小时前";
            }
            if(now.getTime() < oneH){
                return "一小时前";
            }
    
            SimpleDateFormat sdf = new SimpleDateFormat(format);
    
            return sdf.format(date);
        }
    }

    在模板中直接使用

    ${@com.ibeetl.blog.function.PrintTimeUtil.printTime(page.list[i+1].createTime,"yyyy-MM-dd HH:mm:ss")}

    不用注册函数,直接使用类的static公共方法。

    12、ajax局部渲染    <--返回目录

    Beetl满足了更加流行的方式,研发了ajax局部渲染技术。

    后台处理后返回一个json,浏览器端将json数据拆开,拼成一条一条的行数据,然后生成dom节点,追加到表格里。 作为另外一种可选技术,beetl支持局部渲染技术,允许后台处理返回的是一个完成的html片段,这样,前端浏览器可以直接将这个html片段追加到表格里。在性能测试里,俩种方式性能差别不大(http://bbs.ibeetl.com/ajax//)

    在beetl模板中,使用#ajax 局部命名:{ .... }包裹起来的代码,就是ajax需要渲染的局部代码。

    如果一个模板返回正常的渲染整个模板文件,将会忽略这一标记,比如:return "index.html"

    如果返回的是 模板名称#局部命名,Beetl将会只渲染这一小段的代码。比如return "index.html#局部命名"

    举个例子,在项目中layout.html模板中,增加了一段ajax标记

    <div class="widewrapper main">
        <div class="container">
            <div class="row">
                <div class="col-md-8 blog-main">
                    ${layoutContent}
                </div>
                <%if(isEmpty(notShow)){%>
                <aside class="col-md-4 blog-aside" id="tagsDiv">
                    <%#ajax tags :{%>
                    <div class="aside-widget">
                        <header>
                            <h3>Tags</h3>
                        </header>
                        <div class="body clearfix">
                            <ul class="tags">
                                <%for(tag in categorys!){%>
                                <li><a href="/?category=${tag}">${tag}</a></li>
                                <%}%>
                            </ul>
                        </div>
                    </div>
                    <%} }%>
                </aside>
            </div>
        </div>
    </div>

    大家可以看到有一段使用 #ajax tags{}包裹起来的代码

    <%#ajax tags :{%>
        .....
    <%}%>

    当controller中直接 return "layout.html"时,将会渲染整个页面。

    我们在controller中增加一个方法。

    @GetMapping("/tags")
    public String tags(HttpServletRequest request) {
        request.setAttribute("categorys", blogService.listCategory());
        return "common/layout.html#tags";
    }

    告诉beetl模板引擎,只渲染tags中间的代码。

    可以看到我们渲染的代码为:

    <div class="aside-widget">
        <header>
            <h3>Tags</h3>
        </header>
        <div class="body clearfix">
            <ul class="tags">
                <li><a href="/?category=beetl">beetl</a></li>
                <li><a href="/?category=beetlsql">beetlsql</a></li>
                <li><a href="/?category=java">java</a></li>
                <li><a href="/?category=一起学Beetl">一起学Beetl</a></li>
            </ul>
        </div>
    </div>

    仅仅渲染了我们中间这一段代码,能让我们更加优雅使用。

    我们在前端只要使用js加入这段渲染的html代码即可。

    <script type="text/javascript">
        $(function(){
            $.get("${ctxPath}/tags",function (data) {
                $("#tagsDiv").html(data);
                console.info(data);
            });
        });
    </script>

    13、重定向    <--返回目录

    @PostMapping("/saveBlog")
    public String saveBlog(
            Blog blog,
            HttpServletRequest request) {
        blogService.saveBlog(blog);
        return "redirect:/";
    }

    ---

  • 相关阅读:
    linux内核的配置
    数据库常用SQL语句(二):多表连接查询
    数据库常用SQL语句(三):子查询
    数据库常用SQL语句(一):常用的数据库、表操作及单表查询语句
    数据库管理系统软件的软件架构
    Java对象的序列化
    关于重写对象equals方法的问题
    对Spring的理解
    Myeclipse项目工程目录中各种Libraries的含义
    对JDBC的使用理解
  • 原文地址:https://www.cnblogs.com/xy-ouyang/p/13926860.html
Copyright © 2011-2022 走看看