java 如何把vm模板渲染成并执行

场景:
1、告警模板:采用vm定义模板内容,传入告警上下文动态执行

实现:
直接调用 SelfTemplateEngine#generateByObj(java.lang.String, java.util.Map)

  1. public final class SelfTemplateEngine extends VelocityEngine {
  2. /** logger*/
  3. private static final Logger logger = LoggerFactory.getLogger(SelfTemplateEngine.class);
  4. /** 模板引擎自身对象*/
  5. private static SelfTemplateEngine engine = new SelfTemplateEngine();
  6. /**
  7. * 私有构造
  8. */
  9. private SelfTemplateEngine() {
  10. initVe();
  11. }
  12. /** 获取当前实例 */
  13. private static SelfTemplateEngine getInstance() {
  14. return engine;
  15. }
  16. /***
  17. * Velocity模板引擎 初始化
  18. */
  19. private void initVe() {
  20. Properties p = new Properties();
  21. String charset = System.getProperty("file.encoding");
  22. if (null == charset) {
  23. charset = "GBK";
  24. }
  25. p.put("input.encoding", charset);
  26. p.put("output.encoding", "UTF-8");
  27. p.put("resource.loader", "srl");
  28. /**
  29. velocimacro.permissions.allow.inline.to.replace.global参数含义
  30. 允许标明是否允许在常规模板内定义的Velocimacro代替在模板库中定义并通过velocimacro.library属性在启动时装入的全局宏。默认设置为false。
  31. 参考:https://wizardforcel.gitbooks.io/velocity-doc/content/31.html
  32. */
  33. // 注意这里必须put 字符串"true",而不是 boolean类型,否则会空指针异常。
  34. p.put("velocimacro.permissions.allow.inline.to.replace.global", "true");
  35. //p.put("velocimacro.permissions.allow.inline.local.scope", true);
  36. //p.put("velocimacro.library.autoreload", true);
  37. //p.put("file.resource.loader.cache", true);
  38. //p.put("velocity.engine.resource.manager.cache.enabled", false);
  39. p.put("srl.resource.loader.class", "com.alipay.lego.util.velocity.loader.SelfDefinedResourceLoader");
  40. p.put(
  41. "userdirective",
  42. "com.alipay.lego.util.velocity.directive.DateFromatDirective,com.alipay.lego.util.velocity.directive.StringFormatDirective,com.alipay.lego.util.velocity.directive.URLFormatDirective,com.alipay.lego.util.velocity.directive.StringArrConcatDirective");
  43. try {
  44. LoggerUtil.info(logger, "DynamicTemplateEngine初始化开始。。。");
  45. super.init(p);
  46. } catch (Exception e) {
  47. LoggerUtil.error(logger, "DynamicTemplateEngine初始化出错:自定义Velocity模板解析引擎初始化失败", e);
  48. }
  49. }
  50. /**
  51. * 根据动态模板内容和传入的k-v map生产最终内容
  52. *
  53. * @param vmContent 动态模板内容
  54. * @param paramMap k-v map
  55. * @return 最终内容字符串: 如果转换出错,返回null
  56. */
  57. public static String generate(String vmContent, Map<String, String> paramMap) {
  58. try {
  59. SelfTemplateEngine engine = getInstance();
  60. Template template = engine.getTemplate(vmContent);
  61. VelocityContext context = new VelocityContext();
  62. if (!CollectionUtils.isEmpty(paramMap)) {
  63. Iterator<String> iterator = paramMap.keySet().iterator();
  64. while (iterator.hasNext()) {
  65. String keyName = iterator.next().toString();
  66. context.put(keyName, paramMap.get(keyName));
  67. }
  68. StringWriter writer = new StringWriter();
  69. template.merge(context, writer);
  70. return writer.toString();
  71. }
  72. } catch (Exception e) {
  73. logger.error("SelfTemplateEngine动态模板替换失败", e);
  74. }
  75. return null;
  76. }
  77. /**
  78. * 根据动态模板内容和传入的k-v map生产最终内容
  79. *
  80. * @param tempalteContent 动态模板内容
  81. * @param paramMap k-v map
  82. * @return 最终内容字符串: 如果转换出错,返回null
  83. */
  84. public static String generateByObj(String tempalteContent, Map<String, Object> paramMap) {
  85. try {
  86. SelfTemplateEngine engine = getInstance();
  87. Template template = engine.getTemplate(tempalteContent);
  88. VelocityContext context = new VelocityContext();
  89. if (!CollectionUtils.isEmpty(paramMap)) {
  90. Iterator<String> iterator = paramMap.keySet().iterator();
  91. while (iterator.hasNext()) {
  92. String keyName = iterator.next().toString();
  93. context.put(keyName, paramMap.get(keyName));
  94. }
  95. StringWriter writer = new StringWriter();
  96. template.merge(context, writer);
  97. return writer.toString();
  98. }
  99. } catch (Exception e) {
  100. logger.error("SelfTemplateEngine动态模板替换失败,tempalteContent=" + tempalteContent, e);
  101. }
  102. return null;
  103. }
  104. /**
  105. * 根据动态模板内容和传入的Object生产最终内容
  106. *
  107. * @param tempalteContent 动态模板内容:注意模板中的占位key需要增加“ctx.”前缀
  108. * @param paramObj 参数对象
  109. * @return 最终内容字符串: 如果转换出错,返回null
  110. */
  111. public static String generateByObj(String tempalteContent, Object paramObj) {
  112. try {
  113. SelfTemplateEngine engine = getInstance();
  114. Template template = engine.getTemplate(tempalteContent);
  115. VelocityContext context = new VelocityContext();
  116. if (null != paramObj) {
  117. StringWriter writer = new StringWriter();
  118. context.put("ctx", paramObj);
  119. template.merge(context, writer);
  120. return writer.toString();
  121. }
  122. } catch (Exception e) {
  123. logger.error("SelfTemplateEngine动态模板替换失败,tempalteContent=" + tempalteContent, e);
  124. }
  125. return null;
  126. }
  127. }

模板缓存问题(被一个技术问题阻塞了怎么办)

提升自己的技术自信

今天碰到一个技术问题搞了一下午。

【问题描述】

采用velocity模板时,macro宏定义函数具有缓存。导致更新模板中的函数体之后,没有立即生效。

【解决方式】

init时增加参数设置:velocimacro.permissions.allow.inline.to.replace.global=true

详见volecity中文文档:https://wizardforcel.gitbooks.io/velocity-doc/content/31.html

问题的阻塞点主要还不在这里,而在于设置这个参数的时候抛了空指针。

  1. Properties p = new Properties();
  2. p.put("velocimacro.permissions.allow.inline.to.replace.global", "true");

注意这里必须put 字符串”true”,而不是 boolean类型,否则会空指针异常。

其实定位这个空指针异常非常简单,只是debug多走一步就可以了。但自己总是觉得一旦debug到第三方库里面的逻辑,对自己而言就很难排查了,最终放弃,求助他人。

所以,建立自己的技术自信,靠自己多花点儿时间解决一个问题,值得。

这里也有一个点要注意,工作过程中一旦花较长时间解决一个问题就会焦躁

引用小狗钱钱里的一句话,其实说的是一个道理

吉娅,你真的认真找过工作了吗?我是说,你有没有用一整个下午的时间来考虑如何挣到钱的问题呢?”
我不得不承认,我考虑这个问题的时间加起来甚至还不到一个小时。其实每次想这件事,我总是很快就认定自己是不会有机会的

其他踩坑记录

vm最后一行不能是注释? 遇到报错,解析有问题。