velocity自定义指令不生效问题解决之旅

一、背景现象

为了支持灵活的、可自定义的脱敏规则,工程拟采用velocity实现该目的,为此,自定义了:

  • mask、substr两个指令,其中
  • mask实现
public class MaskDirective extends Directive {

    @Override
    public String getName() {
        return "mask";
    }

    @Override
    public int getType() {
        return LINE;
    }

    @Override
    public boolean render(InternalContextAdapter ctx, Writer writer, Node node) throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException {

        //fieldName
        SimpleNode snField = (SimpleNode) node.jjtGetChild(0);

        String maskChar = (String)snField.value(ctx);

        snField = (SimpleNode) node.jjtGetChild(1);
        int count = (int)snField.value(ctx);

        StringBuilder buff = new StringBuilder();

        for(int i = 0; i < count; i++) {
            buff.append(maskChar);
        }

        writer.write(buff.toString());

        return true;

    }
}
  • substr实现
public class SubstrDirective extends Directive {

    @Override
    public String getName() {
        return "substr";
    }

    @Override
    public int getType() {
        return LINE;
    }

    @Override
    public boolean render(InternalContextAdapter ctx, Writer writer, Node node) throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException {

        //fieldName
        SimpleNode snField = (SimpleNode) node.jjtGetChild(0);

        String fieldValue = (String)snField.value(ctx);

        if(fieldValue == null || fieldValue.length() == 0){
            return false;
        }

        int items = node.jjtGetNumChildren();

        int startIdx = Integer.MIN_VALUE;
        int endIdx = fieldValue.length();

        if(items < 1){
            return false;
        }

        try{

            snField = (SimpleNode) node.jjtGetChild(1);
            startIdx = (int)snField.value(ctx);

            if(items > 2){
                snField = (SimpleNode) node.jjtGetChild(2);
                endIdx = (int)snField.value(ctx);

                if(endIdx > fieldValue.length()){
                    endIdx = fieldValue.length();
                }

            }

            if(startIdx >= endIdx){
                return false;
            }

            StringBuilder buff = new StringBuilder();

            if(items > 2){
                writer.write(fieldValue.substring(startIdx,endIdx));
            }else{

                if(startIdx <  0){
                    startIdx = startIdx + fieldValue.length();
                }

                writer.write(fieldValue.substring(startIdx));
            }

            return true;

        }catch(Exception e){
            return false;
        }
    }
}
  • velocity初始化方法:
public final class VelocityHelper {

    private Set<String> userExtDirective ;
    private final static String K_USERDIRECTIVE = "userdirective";

    private final static VelocityHelper VH = new VelocityHelper();
   private VelocityHelper(){

    }

    public static final VelocityHelper getInstance(){
        return VH;
    }

    /**
     * 初始化
     */
    public void init() throws Exception{

        Properties prop = new Properties();

        StringBuilder buff = new StringBuilder();

        for(String s : userExtDirective){
            buff.append(s.trim()).append(",");
        }

        LogHelper.trace("start VelocityHelper.init[" + buff.toString() + "]");

        prop.setProperty(K_USERDIRECTIVE, buff.toString().substring(0, buff.length()-1));
        Velocity.init(prop);

        LogHelper.trace("end VelocityHelper.init");

    }

    /**
     * 根据报文模板和<code>VelocityContext</code>,渲染出实际的业务报文
     *
     * @param context 业务上下文信息
     * @param template 模板
     * @return
     * @throws IOException
     */
    public String evaluate(String template, final VelocityContext context) throws IOException {
        Writer writer = new StringWriter();
        try {
            Velocity.evaluate(context, writer, StringUtil.EMPTY_STRING, template);
            return writer.toString();
        } finally {
            IOUtils.closeQuietly(writer);
        }
    }

    /**
     *
     * @param userExtDirective
     *
     */
    public void setUserExtDirective(Set<String> userExtDirective){

        this.userExtDirective = userExtDirective;
    }

}

部署到服务器后,以下表达式始终未得到替换:#substr($maskField, 0, 3)#mask(‘*‘, 4)#substr($maskField, -4) 

二、原因分析

开始怀疑是配置等问题,经过以下方法尝试均为解决问题:

1、怀疑spring xml配置问题----未解决

2、硬编码指令,在构造函数塞入指令----未解决

无奈,登录服务器grep velocity 得到了以下的输出(其中之一),茅塞顿开。

2019-02-02 07:49:52,315 [  - /// - ] INFO  assemble.VMRender - resource=class path resource [velocity/velocity.properties]

Velocity.ini及Velocity.evalute均是全局单一模式,既然有人捷足先登,自然不在做第二次初始化。

三、解决方案

采用VelocityEngine替换Velocity,实现jar包间隔离,独立管理自己的资源。

private VelocityEngine velocityEngine = null;
velocityEngine = new VelocityEngine(prop);
velocityEngine.evaluate(context, writer, StringUtil.EMPTY_STRING, template);

原文地址:https://www.cnblogs.com/frazeworld/p/10347496.html

时间: 2024-08-30 14:46:41

velocity自定义指令不生效问题解决之旅的相关文章

velocity自定义标签和指令

velocity本身支持自定义标签和指令的扩展, 在 Velocity 模板语言的语法中,以美元符 $ 开头的为变量的声明或者引用,而以井号 # 开头的语句则为 Velocity 的指令(Directive). velocity支持的指令有:#set,#foreach,#if #else #end,#parse,#include,#evaluate,#define,#macro, 在velocity的jar包中的directive.properties中定义了这些实现: [html] view

angular自定义指令命名的那个坑

Directive 先从定义一个简单的指令开始. 定义一个指令本质上是在HTML中通过元素.属性.类或注释来添加功能.AngularJS的内置指令都是以ng开头,如果想自定义指令,建议自定义一个前缀代表自己的命名空间.这里我们先使用my作为前缀: var myApp = angular.module('myApp', []) .directive('myDirective', function() { return { restrict: 'A', replace: true, template

angular创建自定义指令的四种方式

angular除了内置的部分指令,还可以通过.directive来自定义指令.要调用自定义指令,HTML 元素上需要添加自定义指令名.使用驼峰法来命名一个指令:nsHeader,在调用时使用需要-来分割:ns-header.自定义指令调用的的方式有四种,如下: 元素名 属性 类名 注释 1.使用元素名调用: 1 <!DOCTYPE html> 2 <html ng-app="myApp"> 3 <head> 4 <meta charset=&q

第二节:指令的使用(6)——自定义指令

1.指令定义的钩子函数:(这些钩子函数都是可选的) bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作. inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中). update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化.通过比较更新前后的绑定值,可以忽略不必要的模板更新. componentUpdated: 被绑定元素所在模板完成一次更新周期时调用,即模版更新完成的调用. unb

Vue2.0 - 自定义指令 vue-directive

Vue.directive('指令',function(el,binding,vnode){ el.style='color:'+binding.value;}); el : 指令所绑定的元素,可以用来直接操作DOM.. binding: 一个对象,包含指令的很多信息. 自定义指令有五个生命周期: >1.bind :  只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个绑定时执行一次的初始化动作. >2.inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存

Angularjs[25] - 自定义指令(1)(restrict, template, replace)

自定义指令: module.directive(name,directiveFactory) @see $compileProvider.directive() ??  不要使用 ng 为指令,若指令名为 xxx-yyy,在设置指令名时应为 xxxYyy 即驼峰命名法. restrict: 可以任意组合四种风格,如果忽略 restrict,默认为A. 字母 风格 示例 E 元素 <my-dir></my-dir> C 样式类 <span class="my-dir:

AngularJS笔记--自定义指令

在前端开发中, 我们会遇到很多地方都会用到同一种类型的控件.AngularJS提供了自定义指令功能,我们可以在指令里面定义特定的html模板.提供给前台html调用. 一. 指令的简单定义.  下面定义了一个简单的控件,点击后调转到博客园. Note: 1.命名方式:directive必须以小写字母开头,其中如果后面由大写字母,html调用的时候就要用 - 将单词分开. <html> <script src='angular.js'></script> <scri

带你走近AngularJS - 创建自定义指令

为什么使用AngularJS 指令? 使用过 AngularJS 的朋友应该最感兴趣的是它的指令.现今市场上的前端框架也只有AngularJS 拥有自定义指令的功能,并且AngularJS 是目前唯一提供Web应用可复用能力的框架. 目前有很多JavaScript 产品提供插件给Web开发人员.例如, Bootstrap 就是当前比较流行的提供样式和JavaScript插件的前端开发工具包.但是开发人员在使用Booostrap中的插件时, 必须切换到JavaScript 模式来写 jQuery

Vue 自定义指令练习

Vue.directive(id,definition)注册一个全局自定义指令,接收两个参数,指令ID以及定义对象 取值: <div v-demo="{ color: 'white', text: 'hello!' }"></div> Vue.directive("demo", function (el, binding) { alert(binding.value.color); alert(binding.value.text); })