写在前面:上周在做项目的时候遇到这样一种情况,就是我在vm模板里面接收到后端传过来的数据,数据格式是一个字符串,字符串是使用逗号隔开的一些监控点信息的名字,我需要在前端将这个字符串分割,然后分别对每一个监控点信息名字加上链接。当时第一想法是使用js实现,但是发现js没办法实现我的需求(至少我做不到),因为本身我拿到的字符串就是在一个对象的某一个属性,而这个对象我又是从List里面遍历出来的。代码如下:
#foreach ( $defination in $data )
<tr>
<!--<td>$velocityCount</td>-->
<td ><!--此处使用$!defination.monitors获取监控描述字符串--></td>
</tr>
#end
有人会说,为什么不在后台直接将字符串分割,然后拼接上超链接在传到前端直接输出呢,我没这么做的原因是:第一、这么做是有风险的、第二、我也没办法这么做。首先看看我的另外一篇博文解决webx的xss和csrf漏洞,文中我们提到,将html、js、css代码传到前端,前端不做任何处理就输出这是有安全漏洞的。所以,只能想办法在前端进行超链接的拼接,好在集团有关于这个的解决方案:
pull service
一、 概述
pull service的功能是将对象置入模板中。被pull service放到模板中的对象,不需要应用程序的干预即可直接使用。如果模板没有用到某个对象,则不会产生创建该对象的开销。看起来,这些对象像是被模板主动“拉”进context的,而不是由应用程序push进context中的。这就是pull service名称的来源。
1.1. 为什么需要pull service? 通常我们在做一个页面时,需要处理一些常见的操作,如:日期转换、字符串处理、生成动态URL、生成CSRF Tooken、获取上下文状态、表单验证等。要完这些操作,我们通常使用JEE设计模式中的视图助手(view helper)来完成,如:在JSP中是通过java bean的方式来封装这些处理逻辑,并在需要的地方引用这个java bean的相应方法,从而实现这些操作的重用。 java bean虽然解决了我们碰到的这些问题,但缺少统一的配置以及这些对象实例的生命周期管理(如:单例、request、session等级别)。这时,我们就需要引入一个类似于java bean的机制来辅助我们页面的开发,并增加统一配置与生命周期管理。
二、设计
Pull service的最终表现是由一组具有不同功能与作用域的pull tool,并为这些pull tool提供统一的配置、生命周期管理,使之可以方便的注入的我们的view层上下文,从而满足我们view层的渲染辅助。
2.1. Pull tools作用域 global :就是singleton,在系统启动时创建实例 request :在每个request的第一次访问该tool时,自动创建实例
注意,
ToolFactory和ToolSetFactory本身一定是singleton的。但他们所创建的tool对象的作用域,是由ToolFactory或ToolSetFactory.isSingleton方法决定的。
2.2. 单个tool和tool set 单个tool,由ToolFactory创建,每个factory创建一个tool tool集合,由ToolSetFactory创建,每个factory创建一组tool。这种接口赋予ToolSetFactory自主决定将创建哪些对象的权力。例如在webx中,uri broker tool将一组URI brokers对象放到模板中。
2.3. 延迟创建实例 对于global tool或tool set,全部实例均在系统启动时创建。 对于非global tool或tool set,所有实例均在每次request时,第一次访问该tool时创建。如果一个tool set将创建一组tools,那么每一个tool都是延迟创建的。
2.4. RuntimeToolSetFactory 这是一个特殊的tool set factory接口,主要用于兼容以前的pull service实现。这个factory会在每次request中,都调用createToolSet方法生成toolset实例。因此它的性能不如tool set factory。正常情况下不推荐使用它。
2.5. Pull tools全局性 parent context中的tools被所有子context中的tools共享。但子context可以覆盖父context中的同名tools(注:只是以当前context的tool的引用为优先,不影响父context中的实例)。
三、使用
3.1 配置: 在webx.xml文件中加入:
3.2 ID命名约定
<tool id="xxx">,(明确指定)此时id=xxx
<tool class="com....HelloTool>,此时id=hello
<tool class="com....Hello>,此时id=hello
<hello-tool > 此时id=hello
3.3
arrayUtil ==>> ArrayUtil
classLoaderUtil ==>> ClassLoaderUtil
classUtil ==>> ClassUtil
enumUtil ==>> EnumUtil
exceptionUtil ==>> ExceptionUtil
fileUtil ==>> FileUtil
localeUtil ==>> LocaleUtil
mathUtil ==>> MathUtil
messageUtil ==>> MessageUtil
objectUtil ==>> ObjectUtil
streamUtil ==>> StreamUtil
stringEscapeUtil ==>> StringEscapeUtil
stringUtil ==>> StringUtil
systemUtil ==>> SystemUtil
四、使用方式:
了解完了pull service,我们回到刚开始那个问题,如何解决我的需求:
#foreach ( $defination in $data )
<tr>
<!--<td>$velocityCount</td>-->
<td>
#foreach ($monitor in $!stringUtil.split($!defination.monitors,","))
<a href="/monitor/businessLin/globalViewCommon.htm?appName=$monitor">$monitor,</a>
#end
</td>
</tr>
#end
看到 中的那段代码了么,首先我们使用stringUtil类的split方法将字符串按照逗号进行拆解,拆解之后返回的是一个字符串数组,然后我们用foreach遍历这个数组,拿到每一个元素进行字符串的拼接。这段代码:$!stringUtil.split($!defination.monitors,",")就是使用pull service代码。上面我们介绍过得哪些内置工具都可以拿过来直接使用。
五、自定义
定义工具类:
public class IconTool {
public String getIconCodeByKey(String key){
String iconCode = "";
if(StringUtil.isNotBlank(key)){
switch (key) {
case "site":
iconCode ="xe629;";
break;
default:
iconCode ="xe63b;";
break;
}
return iconCode;
}
return "xe65e;";
}
}
配置文件:
<services:pull xmlns="http://www.alibaba.com/schema/services/pull/factories">
<utils />
<csrfToken />
<page-tool />
<control-tool />
<uris-tool />
<bean-tool id="iconTool" class="com.alibaba.intl.apaas.common.util.IconTool" scope="global" />
<factory id="unicornTool" class="com.alibaba.intl.web.unicorn.pull.webx3.UnicornPullTool" />
</services:pull>
使用: 在vm文件中调用: $!iconTool.getIconCodeByKey($page.iconCode)


