Beetl 源码解析:GroupTemplate 类
本文首发于公众号:腐烂的橘子
前言
Beetl 是一款 Java 模板引擎,在公司的项目中大量运用,它的作用是写通用代码时,有一些差异化的逻辑需要处理,这时可以把这些差异化的逻辑写在模板里,程序直接调用,实现了代码的低耦合。
有人问差异化的东西为什么不能通过配置实现?原因是配置只能将一些差异化的值抽离出来,一些复杂的逻辑很难做到。比如有一个类似计算器的界面,里面可以对一些业务字段进行公式计算:
- 分润 = 利息 * 0.2
- 分润 = (利息 + 罚息) * 0.1
程序在计算这个表达式前,并不知道表达式的具体内容,只是想由业务传入利息、罚息这些字段上下文信息后,能自动计算出结果。这时使用 Beetl 模板,我们将上面的公式用 Beetl 表达式表示:
- <%print(interest * 0.2);%>
- <%print((interest + penalty) * 0.2);%>
代码中就不用感知具体公式内容了,直接写通用逻辑即可:
public static void main(String[] args) throws IOException {
    // 用户输入的公式
    String formula = "xxxx";
    // 计算结果的上下文参数
    HashMap param = new HashMap();
    // 核心代码
    GroupTemplate gt = new GroupTemplate(new StringTemplateResourceLoader(), Configuration.defaultConfiguration(););
    Template template = gt.getTemplate(formula);
    template.binding(new HashMap());
    // ans 就是计算的结果
    String ans = template.render();
}
以上就是 Beetl 的基本使用方法,具体可以参考文档,接下来的几篇文章,将对其源码进行解析,如有不合理的地方,欢迎各位大佬批评指正。
核心类 GroupTemplate 源码解析
核心字段定义
GroupTemplate 类核心字段定义如下:
public class GroupTemplate {
    /* 模板在运行过程中,class方法,accessory调用等需要的classLoader */
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader() != null
            ? Thread.currentThread().getContextClassLoader()
            : GroupTemplate.class.getClassLoader();
    AABuilder attributeAccessFactory = new AABuilder();
    ResourceLoader resourceLoader = null;
    Configuration conf = null;
    TemplateEngine engine = null;
    Cache programCache = ProgramCacheFactory.defaulCache();
    List<Listener> ls = new ArrayList<Listener>();
    // 所有注册的方法
    Map<String, Function> fnMap = new HashMap<String, Function>();
    // 格式化函数
    Map<String, Format> formatMap = new HashMap<String, Format>();
    Map<Class, Format> defaultFormatMap = new HashMap<Class, Format>(0);
    // 虚拟函数
    List<VirtualAttributeEval> virtualAttributeList = new ArrayList<VirtualAttributeEval>();
    Map<Class, VirtualClassAttribute> virtualClass = new HashMap<Class, VirtualClassAttribute>();
    // 标签函数
    Map<String, TagFactory> tagFactoryMap = new HashMap<String, TagFactory>();
    ClassSearch classSearch = null;
    // java调用安全管理器
    NativeSecurityManager nativeSecurity = null;
    ErrorHandler errorHandler = null;
    Map<String, Object> sharedVars = null;
    ContextLocalBuffers buffers = null;
    // 用于解析html tag得属性,转化为符合js变量名字
    AttributeNameConvert htmlTagAttrNameConvert = null;
    //...
}
核心字段解释如下:
- classLoader: 模板在运行中需要的 ClassLoader
- attributeAccessFactory: 一个工厂类,为一个特定类的方法生成 AttributeAccess,如果类是 Map,则生成 MapAA;如果类是 List, 则生成 ListAA 等
- resourceLoader: 资源加载器,根据 GroupTemplate 的 key 获取对应资源的加载器,可以是文件、字符串等
- conf: 模板配置,核心类,与 Beetl 相关的所有配置
- engine: 模板引擎,只有一个- createProgram方法,这个方法可以生成一个用来执行模板的程序
- programCache: 本地缓存,基于 ConcurrentHashMap 实现
- ls: 事件的监听器列表,暂时没用
GroupTemplate 核心方法
构造方法
构造方法主要分为三步:
- 初始化默认配置
- 执行 init() 初始化方法,后面会讲
- 初始化资源加载器,实际上就是初始化 resourceLoader
public GroupTemplate() {
    try {
        this.conf = Configuration.defaultConfiguration();
        init();
        initResourceLoader();
    } catch (Exception ex) {
        throw new RuntimeException("初始化失败", ex);
    }
}
init() 方法内部包含很多初始化行为,具体包括:
- 构建配置
- 初始化模板引擎
- 初始化自定义方法,即 beetl.properties中配置的方法
- 初始化拓展资源,包括 formatMap、defaultFormatmap等
- 初始化 tag,即配置里的tagFactoryMap
- 初始化虚拟函数
- 初始化缓冲区
init() 方法
protected void init() {
    conf.build();
    engine = TemplateEngineFactory.getEngine(conf.getEngine());
    this.initFunction();
    this.initFormatter();
    this.initTag();
    this.initVirtual();
    this.initBuffers();
    classSearch = new ClassSearch(conf.getPkgList(), this);
    nativeSecurity = (NativeSecurityManager) ObjectUtil.instance(conf.getNativeSecurity(), this.classLoader);
    if (conf.errorHandlerClass == null) {
        errorHandler = null;
    } else {
        errorHandler = (ErrorHandler) ObjectUtil.instance(conf.errorHandlerClass, classLoader);
    }
    htmlTagAttrNameConvert = (AttributeNameConvert)ObjectUtil.instance(conf.htmlTagAttributeConvert, classLoader);
}
总结
GroupTemplate 是 Beetl 模板的核心类,我们看到这个类非常重,里面包含了大量属性,这些属性都为后续模板计算提供相关资源。类中的大部分属性都使用了 HashMap 来存放上下文信息,这为我们设计一些类时提供了一定参考。其中缓存管理比较简单,基本就是封装了 ConcurrentHashMap 的方法,只对外暴露 4 个方法,这也是我们构建本地缓存管理的常用方式:基于 ConcurrentHashMap 构建暴露特定方法的自定义缓存管理器。
Beetl 源码解析:GroupTemplate 类的更多相关文章
- [Java源码解析] -- String类的compareTo(String otherString)方法的源码解析
		String类下的compareTo(String otherString)方法的源码解析 一. 前言 近日研究了一下String类的一些方法, 通过查看源码, 对一些常用的方法也有了更透彻的认识, ... 
- lucene原理及源码解析--核心类
		马云说:大家还没搞清PC时代的时候,移动互联网来了,还没搞清移动互联网的时候,大数据时代来了. 然而,我看到的是:在PC时代搞PC的,移动互联网时代搞移动互联网的,大数据时代搞大数据的,都是同一伙儿人 ... 
- Log4j源码解析--Layout类解析
		本文转载上善若水的博客,原文出处:http://www.blogjava.net/DLevin/archive/2012/07/04/382131.html.感谢作者的分享. Layout负责将Log ... 
- junit源码解析--核心类
		JUnit 的概念及用途 JUnit 是由 Erich Gamma 和 Kent Beck 编写的一个开源的单元测试框架.它属于白盒测试,只要将待测类继承 TestCase 类,就可以利用 JUnit ... 
- java源码解析——Stack类
		在java中,Stack类继承了Vector类.Vector类和我们经常使用的ArrayList是类似的,底层也是使用了数组来实现,只不过Vector是线程安全的.因此可以知道Stack也是线程安全的 ... 
- Spring源码解析——核心类介绍
		前言: Spring用了这么久,虽然Spring的两大核心:IOC和AOP一直在用,但是始终没有搞懂Spring内部是怎么去实现的,于是决定撸一把Spring源码,前前后后也看了有两边,很多东西看了就 ... 
- muduo源码解析11-logger类
		logger: class logger { }; 在说这个logger类之前,先看1个关键的内部类 Impl private: //logger内部数据实现类Impl,内部含有以下成员变量 //时间 ... 
- muduo源码解析10-logstream类
		FixedBuffer和logstream class FixedBuffer:noncopyable { }: class logstream:noncopyable { }: 先说一下包含的头文件 ... 
- Log4j源码解析--核心类解析
		原文出处:http://www.blogjava.net/DLevin/archive/2012/06/28/381667.html.感谢上善若水的无私分享. 在简单的介绍了Log4J各个模块类的作用 ... 
- volley源码解析-Throwable类源码解析
		前提知识点: 1.Serializable接口 作用:表示可序列化的语义.就是Java提供的通用数据保存和读取接口.任何类型实现了Serializeable接口,就可以被保存到文件中,或者作为数据流通 ... 
随机推荐
- 向TreeView添加自定义信息
			可在 Windows 窗体 TreeView 控件中创建派生节点,或在 ListView 控件中创建派生项. 通过派生可添加任何所需字段,以及添加处理这些字段的自定义方法和构造函数. 此功能的用途之一 ... 
- 关于初始化page入参的设计思路
			最近在重构老的代码,在写的过程中发现之前的逻辑如果遇到没有入参pageNo会Npe,于是乎我想找找公司项目有啥方式处理page入参的有两种如下 使用三元表达式直接判断是否null,然后赋值 使用map ... 
- Client请求外部接口标准处理方式
			简单记录下client外部请求代码的我认为比较标准的书写格式 public List<GdtDailyBalanceReportAO> getGdtDailyReportList2(Lon ... 
- 安装libevent
			1.在libevent官网(http://libevent.org/)上下载压缩包(我下载的是libevent-2.1.8-stable.tar.gz) 2.解压压缩包:tar -zxvf libev ... 
- 基于quartus的FPGA学习系列
			基于quartus学习 1.学习目标 quartus是altera的FPGA设计软件,用起来的感觉要比xilinx快.这里可以使用其完成各种基本的设计(就是不使用非必须IP核),一些基础的实验都可以在 ... 
- #长链剖分#CF208E Blood Cousins
			题目 给你一片森林,每次询问一个点与多少个点拥有共同的 \(K\) 级祖先 分析 设\(dp[x][d]\)表示以\(x\)为根节点时深度为\(d\)的个数, 那么\(dp[x][d]=\sum\{d ... 
- 使用Apache POI和Jsoup将Word文档转换为HTML
			简介 在现代办公环境中,Word文档和HTML页面都是常见的信息表达方式.有时,我们需要将Word文档转换为HTML格式,以便在网页上展示或进行进一步的处理.本文将介绍如何使用Apache POI库和 ... 
- mybatis 查询一对多子表只能查出一条数据
			mybatis 插叙一对多子表只能查出一条数据 环境 ssm 持久层 mybatis 关联查询一对多<collection> 原因 主表id 和子表id 一样 处理方式: select ... 
- 异步、多线程、Java爬取某网站图片
			一.网页图片爬取类 package com.yhyl.utils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import or ... 
- CDH5.15.1集群安装部署
			CDH集群安装部署 大数据平台软件清单 本文部署的大数据基础平台为CDH,操作系统的版本为CentOS6.8,JDK的版本为1.8,Cloudera Manager与CDH的版本为5.15.1,数据库 ... 
