写在前面:上周在做项目的时候遇到这样一种情况,就是我在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 se
t,所有实例均在每次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)