记一次在webx中velocity新建自定义指令的过程

简介: ### 记一次在webx中velocity新建自定义指令的过程 ![screenshot](http://img2.tbcdn.cn/L1/461/1/b59c6597d3f90168126d63ebc63f967ef1dc348f) webx和velocity就不介绍了。 都很熟悉。本文是记录在webx中增加唉velocity自定义指令的方法。 起因是在velocity渲染模板的时候

记一次在webx中velocity新建自定义指令的过程

screenshot
webx和velocity就不介绍了。 都很熟悉。本文是记录在webx中增加唉velocity自定义指令的方法。
起因是在velocity渲染模板的时候,我们使用了#esc_noesc(variable)做转义,当这个渲染变量未定义时,变量会渲染为${content},而使用$!{variable}渲染简单字符变量时,若未定义则会被渲染为空字符串,这两个场景对于js变量的渲染都会引起js语法错误造成js无法tryCatch, 由于这种后端变量渲染的逻辑大多用于主干代码, 一旦出现错误就会引起白屏,整个页面挂掉(不要问我怎么知道。。) 如下图:
variableError
escError

于是我想到能否将所有针对js的输出变量都赋有一个"''"。 避免页面直接白屏,且写大量的errorLog触发报警。
我们知道, velocity中可以设置自定义指令。
我们定义好一个基于Directive的子类,然后在directive.properties中配置即可。
这是一个简单的自定义指令,作用是获取context某个属性的值:

class CustomVelocityDirective extends Directive{

    static String methodName = 'getValue';

    @Override
    public String getName() {
        return methodName ;  // 指令名 对应到velocity模板中的 methodName()
    }
    @Override
    public int getType() {
        return LINE; // 指令类型 包括 LINE/BLOCK
    }

    @Override
    public void init(RuntimeServices rs, InternalContextAdapter context, Node node) throws TemplateInitException {
        super.init(rs, context, node); // 初始化模板
    }

    @Override
    public boolean render(InternalContextAdapter context, Writer writer, Node node) throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException {    
        SimpleNode getChildren = (SimpleNode) node.jjtGetChild(0); //获取指令传参
        String variableName = (String)sn_region.value(context); // 获取到传参的文字 or 对象
        Object value = context.get(variableName).toString();  //获取该key对应到context的属性
        writer.write(value); // 打印输出内容
        return true;  //打印成功
    }
}
AI 代码解读

配置directive.properties属性,声明这个指令

userdirective=xxx.xxx.YourDirectiveClass  // 可用逗号分隔指定多个
AI 代码解读

使用时:

// some HTML..   
#getValue("name")   // 打印value  by: context.put("name", "value")
// some HTML..   
AI 代码解读

参考文章

webx中的做法

以上是Velocity的做法, 看起来还比较容易, 简单明了。 然而现实很骨感, 现实是我们用的是webx。 当我们面对webx,一切都不那么好了。

首先我试着在我们的properties文件中增加userdirective=xxx.xxx.YourDirectiveClass, 当然, 一切并不成功。看来照搬velocity的做法在webx中并不合适。

接着在网上看到一篇文章 (在velocity中自定义标签) 在velocity中自定义标签 应该是唯一一篇提到了webx设置velocity自定义标签的文章。 照着做了一遍, 可是在如何声明Schema文件的地方有点含糊。 想起了我们在webx中用到过escape指令, 于是找相应的声明代码, 最终找到类似的EscapeSupport。 依葫芦画瓢,照着做了,也大致摸清了webx中增加自定义velocity插件的方法。

Velocity:

设置: userdirective=xxx.xxx.YourDirectiveClass

Webx:

新建parser文件的声明

在META-INF下新建services-template-engines-velocity-plugins.bean-definition-parsers 文件,指定Schema文件和Parser, 内容是:

custom-directive=xxxx.xxx.xxx.xxxParser   
AI 代码解读
新建Schema文件

按照META-INF/services/template/engines/velocity/plugins 目录,新建一个xsd文件,声明Schema,(由于我没有其他属性,所以Schema文件异常简单):

<xsd:element name="variable-parser" type="VariableParserType">
    <xsd:annotation>
        <xsd:documentation><![CDATA[some ducumentation]]></xsd:documentation>
        </xsd:annotation>
    </xsd:element>
    <xsd:complexType name="VariableParserType" />
AI 代码解读
定义Parser类:
class VariableDefinitionParser extends AbstractSingleBeanDefinitionParser<VariableParserSupport> {
    @Override
    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
    }
}
AI 代码解读
定义VelocityPlugin的实现类:
public class VariableParserSupport implements VelocityPlugin {
    public void init(VelocityConfiguration configuration) throws Exception {
        configuration.getProperties().addProperty("userdirective", CustomVelocityDirective.class.getName());
    }
    public Resource[] getMacros() throws IOException {
        return new Resource[] { };
    }
}
AI 代码解读
在webx-component-and-root.xml中增加声明
<services:template searchExtensions="true">
        <tpl-engines:velocity-engine templateEncoding="UTF-8" strictReference="false" path="/templates/${component}">
            <global-macros>
                <name>global/*.vm</name>
            </global-macros>
            <plugins>
                <vm-plugins:custom-directive />   // 写入我们自定义的标签的
                <vm-plugins:escape-support defaultEscape="html">
                 I   <noescape>
                        <if-matches pattern="^control\." />
                        <if-matches pattern="^screen_placeholder" />
                        <if-matches pattern="^stringEscapeUtil\.escape" />
                        <if-matches pattern="^csrfToken\.(get)?hiddenField" />
                        <if-matches pattern="^tbToken\.(get)?hiddenField" />
                        <if-matches pattern="^securityUtil\.(richtext|jsEncode|ignoretext)" />
                    </noescape>
                </vm-plugins:escape-support>
            </plugins>
        </tpl-engines:velocity-engine>
        <tpl-engines:freemarker-engine templateEncoding="UTF-8" path="/templates/${component}"/>
        <tpl-engines:jsp-engine path="/templates/${component}"/>
    </services:template>
AI 代码解读

在做到倒数第二不,声明Support类的时候, 我看到画龙点睛的一句:

configuration.getProperties().addProperty("userdirective", CustomVelocityDirective.class.getName());  
AI 代码解读

而我们做了这么多,就是为了这一句。 对应到velocity就只是这行代码::

userdirective=xxx.xxx.YourDirectiveClass
AI 代码解读

最后知道真相的我眼泪掉下来。。。。。。

当然,velocity的自定义指令在webx中是作为一个velocity插件的方式使用。 在webx中由于多了一套约定的Schame,导致在webx中增加自定义组件变得复杂数倍,当然功能也有所增强(例如支持宏)。
并且针对我想要的failover场景,也许新增自定义指令的方法并不是最优解,也许有其他成熟的webx解决方案。 也希望有人能指出,一起交流。

最后感谢下 @贾少天 的帮助。

参考资料:

1 WebX文档

  1. velocity自定义标签和指令variableError
萧元
+关注
目录
打赏
0
0
0
0
78372
分享
相关文章
CompletableFuture原理及应用场景详解
CompletableFuture是Java 8引入的异步编程工具,用于优化多任务并行处理。相比传统Future,它支持可组合操作(如thenApply、thenCombine),避免回调地狱,同时降低依赖间的阻塞。其核心通过result存储结果,stack管理依赖动作,基于观察者模式实现回调通知。使用中需注意:异步方法建议显式传入线程池以隔离资源;异常信息需通过get()或exceptionally捕获。适用于复杂业务场景,如APP页面加载涉及多服务API调用时,可显著提升性能与代码可读性。
259 3
以下是一个简化的车库管理系统工程概述,并附带Python代码示例和详解。
以下是一个简化的车库管理系统工程概述,并附带Python代码示例和详解。
工作上个的好搭子——通义灵码测评分享
作为一名运维开发工程师,我使用通义灵码的@workspace和@terminal功能,快速熟悉新项目代码并实现新需求。相比之前,提效了约50%。本文分享了我的使用体验和心得,详细介绍了通义灵码如何帮助我在复杂项目中提高开发效率、降低学习成本、提升代码质量和增强团队协作。
简化数据迁移:API接口的应用
在现代商业环境中,数据迁移已经成为企业运营中不可或缺的一部分。随着技术的进步和市场的变化,企业需要将商品数据从一个系统迁移到另一个更高效或更具成本效益的系统。这一过程可以借助应用程序编程接口(API)来简化。以下是如何通过使用API接口来简化商品数据迁移过程的详细步骤。
【Bistoury】Bistoury功能分析-在线debug
Bistoury是由去哪儿网开源的一款应用诊断工具,适用于Java应用的在线调试。通过增强字节码,Bistoury能够在不停止应用的情况下设置断点并获取执行信息。启动被调试应用后,使用`quick_start.sh`命令启动Bistoury,并通过浏览器访问`localhost:9091`进行调试。默认账号密码为admin。Bistoury通过ASM字节码增强技术确保行号一致性,并利用行增强技术收集局部变量及调用栈信息。尽管社区已不活跃,但其设计理念仍具参考价值。
132 0
【Bistoury】Bistoury功能分析-在线debug
【Python的魅力】:利用Pygame实现游戏坦克大战——含完整源码
【Python的魅力】:利用Pygame实现游戏坦克大战——含完整源码
prometheus的查询接口Instant queries 的缺点
Prometheus Instant queries 是一种实时的查询接口,它允许你在 Prometheus 中立即查询时间序列数据,而不需要像使用 PromQL 表达式一样定义监控规则。虽然 Instant queries 提供了方便的实时数据查询功能,但也有一些缺点需要考虑: 性能影响:Instant queries 可能会对 Prometheus 服务器的性能产生影响,特别是在处理大量的并发查询时。因为实时查询需要即时计算时间序列数据,可能会增加服务器的负载,导致性能下降。 资源消耗:Instant queries 需要消耗服务器的资源,包括 CPU 和内存等。如果频繁进行大量的实时
185 3
React Native 打包 App 发布 iOS 及加固混淆过程
本文将介绍如何使用 React Native 打包并发布 iOS 应用到 App Store,并介绍了如何进行应用的加固和混淆过程。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问