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
: 模板在运行中需要的 ClassLoaderattributeAccessFactory
: 一个工厂类,为一个特定类的方法生成 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 构建暴露特定方法的自定义缓存管理器。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~