velocity的另一种使用"简易动态语言"

背景

  相信大家对velocity这一类模板语言都并不陌生,一般velocity大部分是在web应用中,替换jsp的一种选择,做为html页面的渲染。实现UED和开发人员的分离。

  但在最近的一个项目中,遇到了一个比较"另类"的需求,就是我们需要抓取一个外部网站的页面内容信息,比如aleax排名。

 

设计

 

针对这样的需求,设计上需要考虑的点:

 

  1. 外部网站不稳定,超时时间的设置,访问的域名变化(域名换成ip等)
  2. 我们需要精确抓取外部网站的信息,外部网站页面的任意变化,都会导致抓取失败
  3. 有几个类似的需求,需要进行统一考虑

分析:

1. 外部网站的请求。可以使用HttpClient,使用开源的技术可以减少一些风险,比如编码问题,SSL支持,POST提交等问题。

2. 网站的信息分析和抓取,得看具体网站的返回信息,需要支持各种协议的数据。比如xml , json , 普通的html。

3. 外部网站页面变化的应对策略: 

  • 外部网站的抓取策略是可以配置型,比如xml或者数据库。这里比较倾向于数据库,通过一个后台页面,进行CRUD的管理。这样可以做到及时的响应,而不需要重新发布代码。
  • 对外部网站的抓取结果定时进行监控。 比如模拟请求一次,监控具体的返回结果,如有问题进行手机报警。

大的技术内容已经明确,这里主要考虑的是如何设计灵活的抓取策略的规则。

抓取策略大致内容:

1. 构造请求参数,不同的页面有不同的参数

2. 获取外部网站返回结果。比如返回编码处理

3. 解析对应的返回内容,按照json,xml,字符串进行处理。

4. 返回对应的结果。 不同页面的返回结果会出现不同。

因为按照需求设计分析,已经很明确抓取策略的这一块仅仅是通过xml这类静态配置会设计的很复杂,很明显一类动态语言比较适合干这样的活,但项目组中对动态语言基本没使用,大部门下使用的也几乎没有,存在比较大的技术风险。这时候,就轮到velocity登场了,使用模板语言代替动态语言。事实证明,使用velocity处理这类问题也挺适合

常见的velocity处理过程:

总的来说,可以分为两步: 模板查找,模板渲染。

模板查找:

   根据给定的name,返回对应的String,InputSteam流。

模板渲染:boolean evaluate( Context context,  Writer out,  String logTag, String instring )

   根据给定的上下文,和模板内容,进行渲染,并将最终的合并结果直接输出到Writer中。

项目特殊使用:

1. 模板查找: 

   因为对应的模板来自于数据库,所以这里就直接省去了模板查找的这一过程,直接给出对应的script。 同样你也可以使用velocity自带提供的基于database的模板查找DataSourceResourceLoader。

1.resource.loader = ds
2.ds.resource.loader.public.name = DataSource
3.ds.resource.loader.description = Velocity DataSource Resource Loader
4.ds.resource.loader.class = org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader
5.ds.resource.loader.resource.datasource = java:comp/env/jdbc/Velocity   ##依赖jndi
6.ds.resource.loader.resource.table = tb_velocity_template
7.ds.resource.loader.resource.keycolumn = id_template
8.ds.resource.loader.resource.templatecolumn = template_definition
9.ds.resource.loader.resource.timestampcolumn = template_timestamp
10.ds.resource.loader.cache = false
11.ds.resource.loader.modificationCheckInterval = 60

   因为项目使用的是spring database管理,所以这里不用velocity自带的一套。

2. 模板渲染:

    构造了自己的Context,进行页面渲染。直接从context获取对应的返回结果,因为velocity默认是进行字符串输出,对于复杂的返回对象不好处理

 

减简过后,最后的类设计: 

 

对应的时序图: 

 

核心类代码

1.public class VelocityScriptExecutor implements ScriptExecutor {
2.    private static final String     RESULT             = "result";
3.    private static final int        DEFAULT_CACHE_SIZE = 1000;
4.    private RuntimeInstance         ri                 = new RuntimeInstance();
5.    private Map<String, SimpleNode> cache;
6.    private int                     cacheSize          = DEFAULT_CACHE_SIZE;
7.    private Properties              velocityProperties;
8.    private boolean                 needCache          = true;
9.    private static final Writer     nop                = new Writer() { //自定义空的write对象
10.
11.                 public void close() throws IOException {
12.                 }
13.
14.                 public void flush() throws IOException {
15.                 }
16.
17.                 public void write(char[] cbuf, int off, int len) {
18.                 }
19.           };
20.
21.public void initialize() throws ScriptException {
22.        if (cacheSize <= 0) {// 不考虑cacheSize为0的情况,强制使用LRU cache机制
23.            cacheSize = DEFAULT_CACHE_SIZE;
24.        }
25.
26.        cache = new LRULinkedHashMap<String, SimpleNode>(cacheSize);
27.        // 初始化velocity instance
28.        ri.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new NullLogChute());// 设置日志输出
29.        if (velocityProperties != null) {
30.            try {
31.                ri.init(velocityProperties);
32.            } catch (Exception e) {
33.                throw new ScriptException(e);
34.            }
35.        }
36.    }
37.
38./**
39.     * <pre>
40.     * 1. 接受VelocityScriptContext上下文,自身并不关心所谓的pullTool的存在
41.     * 2. script针对对应name下的script脚本
42.     * </pre>
43.     */
44.    public Object evaluate(ScriptContext context, String script) throws ScriptException {
45.        InternalContextAdapterImpl ica = new InternalContextAdapterImpl((AbstractContext) context);
46.        SimpleNode nodeTree = null;
47.        if (needCache) {
48.            nodeTree = get(script);
49.        }
50.        if (nodeTree == null) {
51.            try {
52.                nodeTree = ri.parse(new StringReader(script), "");
53.            } catch (Exception e) {
54.                throw new ScriptException(e);
55.            }
56.            nodeTree.init(ica, ri);
57.            if (needCache) {
58.                put(script, nodeTree);
59.            }
60.        }
61.        try {
62.            nodeTree.render(ica, nop);
63.        } catch (Exception e) {
64.            throw new ScriptException(e);
65.        }
66.
67.        return ica.get(RESULT); //直接从context中获取对应#set(result=xxx),对应的result变量
68.    }

1.public class VelocityScriptContext extends AbstractContext implements ScriptContext {
2.
3.    private Map context  = new HashMap(); // 基于map的上线文实现
4.    private Map pullTool = new HashMap(); // pullTool上下文实现
5.
6.    @Override
7.    public boolean internalContainsKey(Object key) {
8.        return context.containsKey(key) || pullTool.containsKey(key);
9.    }
10.
11.    @Override
12.    public Object internalGet(String key) {
13.        return context.get(key) != null ? context.get(key) : pullTool.get(key);
14.    }
15.
16.    @Override
17.    public Object[] internalGetKeys() {
18.        return context.keySet().toArray();
19.    }
20.
21.    @Override
22.    public Object internalPut(String key, Object value) {
23.        return context.put(key, value);
24.    }
25.
26.    @Override
27.    public Object internalRemove(Object key) {
28.        return context.remove(key);
29.    }
30.
31.    // ================== setter / getter =======================
32.
33.    public void setContext(Map context) {
34.        this.context = context;
35.    }
36.
37.    public void setPullTool(Map pullTool) {
38.        this.pullTool = pullTool;
39.    }
40.
41.}

说明:

pulltool的概念就是类似于velocity toolbox提供的一堆工具util,比如HttpClientTool , JsonTool , XmlTool。可以按照自己的需求封装对应的tool.

 

最后一个script配置实现:(比如抓取google收录某关键字的记录数)

1.##处理参数
2.#set($keyword = $param.keyword)
3.#set($userAgent = $param.userAgent)
4.
5.## 构造url
6.#set($url="http://www.google.com.hk/search?hl=zh-CN&q=${keyword}")
7.## 获取页面
8.#set($param=$httpClient.createParam())
9.$param.config.setReadTimeout(3000)
10.$param.config.setConnectionTimeout(3000)
11.$param.config.setContentEncoding("UTF-8")
12.
13.$param.header.setUserAgent($userAgent)
14.#set($html=$httpClient.request($url,$param))
15.## 处理页面
16.#set($recordNum=$stringTool.substringBetween($html,"找到约","条结果"))
17.#set($cost=$stringTool.substringBetween($html,"(用时","秒)"))
18.
19.#set($record=$stringTool.trim($record))
20.#set($cost=$stringTool.trim($cost))
21.## 生成返回结果
22.#set($result = $resultTool.createMap())
23.$result.put("recordNum",$recordNum)
24.$result.put("cost",$cost)

返回结果,一个map对象,包含了两个key,一个是记录数,一个是耗时。最后在页面上展示时,可以包装成json对象进行控制和显示。

1.{recordNum="4,140,000,000 ",cost="0.13"}  

最后

  本文并没有太多高深的技术,只是对velocity的另一种使用,用于解决一些特定的业务场景。欢迎大家拍砖!

时间: 2024-09-27 17:05:52

velocity的另一种使用"简易动态语言"的相关文章

在Operamasks中使用ELite和JRuby动态语言的秘笈

随着微软正式发布IronPython,推出基于第一款基于动态语言的开源编程工具:紧接着Sun又正式对外宣布收购JRuby,Ruby将成为JVM支持的第一个动态语言,动态语言一直围绕在我们的身边.如何使用动态语言给我们编写JavaWebUI带来帮助?什么样的动态语言能够适合Java开发团队呢?SirsiDynix的架构师TravisJensen通过五条粗略的标准来评估Groovy,JRuby,Jython这三种动态语言在Java上的表现. 1.动态语言与Java之间的交互 2.IDE工具的支持 3

理解Javascript的动态语言特性

  这篇文章主要介绍了理解Javascript的动态语言特性,需要的朋友可以参考下 Javascript是一种解释性语言,而并非编译性,它不能编译成二进制文件. 理解动态执行与闭包的概念 动态执行:javascript提供eval()函数,用于动态解释一段文本,并在当前上下文环境中执行. 首先我们需要理解的是eval()方法它有全局闭包和当前函数的闭包,比如如下代码,大家认为会输出什么呢? ? 1 2 3 4 5 6 7 var i = 100; function myFunc() { var

《圣殿祭司的ASP.NET4.0专家技术手册》----2-2 静态与动态语言的比较

2-2 静态与动态语言的比较 圣殿祭司的ASP.NET4.0专家技术手册程序语言若按照类型检查的时机来划分,可分为: 静态语言(statically typed languages).若一种程序语言的类型检查(Type Checking)工作是发生在编译时期(Compile Time),则称为静态语言.C#与Java都是静态语言代表,程序编写时必须使用明确的类型声明,编译的当下,编译程序就会进行类型检查,且变量或对象的类型一旦声明后,在运行时就无法任意更换类型,否则会发生Exception错误.

理解Javascript的动态语言特性_javascript技巧

Javascript是一种解释性语言,而并非编译性,它不能编译成二进制文件. 理解动态执行与闭包的概念 动态执行:javascript提供eval()函数,用于动态解释一段文本,并在当前上下文环境中执行. 首先我们需要理解的是eval()方法它有全局闭包和当前函数的闭包,比如如下代码,大家认为会输出什么呢? var i = 100; function myFunc() { var i = 'test'; eval('i = "hello."'); } myFunc(); alert(i

一起谈.NET技术,VB.NET和C#的发展与动态语言运行时

在去年微软的专业开发者大会上(Professional Developers Conference PDC 2008),微软就开发者在.NET平台上的未来做了详细的展望,本文我们将概括地来看一下即将到来的.NET 4.0 Framework的一些特性,以及全新的Microsoft 系编程语言,讨论下微软对于.NET Platform在接下来几年的长远展望. VB.NET和C#的联合发展 当前来自软件的最首要的信息之一是针对Microsoft Visual Basic .NET开发者的.VB.NE

VB.NET和C#的发展与动态语言运行时

在去年微软的专业开发者大会上(Professional Developers Conference PDC 2008),微软就开发者在.NET平台上的未来做了详细的展望,本文我们将概括地来看一下即将到来的.NET 4.0 Framework的一些特性,以及全新的Microsoft 系编程语言,讨论下微软对于.NET Platform在接下来几年的长远展望. VB.NET和C#的联合发展 当前来自软件的最首要的信息之一是针对Microsoft Visual Basic .NET开发者的.VB.NE

试问:动态语言是否将会挽救Swing?

动态 据国外网站报道,动态语言是否将会挽救Swing?Swing是否需要挽救? Julian Doherty 通过陈述JRuby可以挽救Swing开始了这场讨论.2003年,Joshua Marinacci发文列出了Swing失败的原因: ·Swing应用建立缓慢 ·Swing设计管理器很恶心 ·Swing应用很难维护 ·Swing功能太广泛 ·没有自身特质 ·Swing应用历史纪录比较糟糕 Doherty 指出动态语言,尤其是Ruby,能帮助解决这些问题.回应Doherty的观点, pinde

ASP等动态语言网站做SEO时站内搜索应该注意的问题

WEB动态语言有很多,ASP,PHP,.NET,JSP等,之所以在标题中着重提到ASP,是因为目前市面上大多数的企业站点还是采用ASP来做的,这个语言因为学习的门槛较低,又有ACCESS的完美配合,所以是大多数程序员首选的企业站点语言.我们不在这篇文章中讨论ASP的安全性或技术层面的东西,我仅在这篇文章中分享最新学习到的一个理念,就是ASP网站的站内搜索功能对SEO的影响. 因为SEO对静态语言的收录有一定的优先(虽没有绝对性,但由于同样的配置上,静态页面的访问速度快于动态页面,所以从用户体验角

冒号和他的学生们(连载16)——动态语言

16.动态语言 Freedom is not free --Kelly Strong 叹号急不可耐地问:"现在可以谈动态语言了吧?" 冒号感言:"曾几何时,动态语言还只是陪太子读书的角色,那时候它们的名字是'脚本语言'.近来却迅速崛起,俨然有与静态语言分庭抗礼之势." 问号忍不住问道:"动态语言与脚本语言是一回事吗?" "相比动态语言定义上的模糊,脚本语言的概念还是比较明确的."冒号回避直接给出答案,"脚本(scri