Template和JSP技术.docx
《Template和JSP技术.docx》由会员分享,可在线阅读,更多相关《Template和JSP技术.docx(14页珍藏版)》请在冰豆网上搜索。
Template和JSP技术
Template和JSP技术
david2009-04-2710:
05
一、起源与现状:
关于Template和JSP的起源还要追述到Web开发的远古年代,那个时候的人们用CGI来开发web应用,在一个CGI程序中写HTML标签。
在这之后世界开始朝不同的方向发展:
sun公司提供了类似于CGI的servlet解决方案,但是无论是CGI还是servlet都面对同一个问题:
在程序里写html标签,无论如何都不是一个明智的解决方案。
于是sun公司于1999年推出了JSP技术。
而在另一个世界里,以PHP和ASP为代表的scriptlet页面脚本技术开始广泛应用。
不过即便如此,问题并没有结束,新的问题出现了:
业务和HTML标签的混合,这个问题不仅导致页面结构的混乱,同时也使代码本身难以维护。
于是来自起源于70年代后期的MVC模式被引入开发。
MVC的三个角色:
Model——包含除UI的数据和行为的所有数据和行为。
View是表示UI中模型的显示。
任何信息的变化都由MVC中的第三个成员来处理——控制器。
在之后的应用中,出现了技术的第一次飞跃:
前端的显示逻辑和后端的业务逻辑分离,COM组件或EJB或CORBA用于处理业务逻辑,ASP、JSP以及PHP被用于前端的显示。
这个就是Web开发的Model1阶段(页面控制器模式)。
不过这个开发模式有很多问题:
1. 页面中必须写入Scriptlet调用组件以获得所必需的数据。
2. 处理显示逻辑上Scriptlet代码和HTML代码混合交错。
3. 调试困难。
JSP被编译成servlet,页面上的调试信息不足以定位错误。
这一切都是因为在Model1中并没有分离视图和控制器。
完全分离视图和控制器就成了必须。
这就是Model2。
它把Model1中未解决的问题——分离对组件(业务逻辑)的调用工作,把这部分工作移植到了控制器。
现在似乎完美了,不过等等,原来的控制器从页面中分离后,页面所需的数据怎么获得,谁来处理页面显示逻辑?
两个办法:
1.继续利用asp,php或者jsp等机制,不过由于它们是运行在web环境下的,他们所要显示的数据(后端逻辑产生的结果)就需要通过控制器放入request流中;2.使用新手法——模板技术,使用独立的模板技术由于脱离的了web环境,会给开发测试带来相当的便利。
至于页面所需数据传入一个POJO就行而不是request对象。
模板技术最先开始于PHP的世界,出现了PHPLIBTemplate和FastTemplate这两位英雄。
不久模板技术就被引入到javaweb开发世界里。
目前比较流行的模板技术有:
XSTL,Velocity,JDynamiTe,Tapestry等。
另外因为JSP技术毕竟是目前标准,相当的系统还是利用JSP来完成页面显示逻辑部分,在Sun公司的JSTL外,各个第三方组织也纷纷推出了自己的Taglib,一个代表是strutstablib。
二、模板技术分析:
模板技术从本质上来讲,它是一个占位符动态替换技术。
一个完整的模板技术需要四个元素:
0.模板语言,1.包含模板语言的模板文件,2.拥有动态数据的数据对象,3.模板引擎。
以下就具体讨论这四个元素。
(在讨论过程中,我只列举了几个不同特点技术,其它技术或有雷同就不重复了)
1. 模板语言:
模板语言包括:
变量标识和表达式语句。
根据表达式的控制力不同,可以分为强控制力模板语言和弱控制力模板语言。
而根据模板语言与HTML的兼容性不同,又可以分为兼容性模板语言和非兼容性模板语言。
模板语言要处理三个要点:
1.标量标记。
把变量标识插入html的方法很多。
其中一种是使用类似html的标签;另一种是使用特殊标识,如Velocity或者JDynamiTe;第三种是扩展html标签,如tapestry。
采用何种方式有着很多考虑,一个比较常见的考虑是“所见即所得”的要求。
2.条件控制。
这是一个很棘手的问题。
一个简单的例子是某物流陪送系统中,物品数低于一定值的要高亮显示。
不过对于一个具体复杂显示逻辑的情况,条件控制似乎不可避免。
当你把类似于count引入,就象我们当初在ASP和PHP中所做得一样,我们将不得不再一次面对scriptlet嵌入网页所遇到的问题。
我相信你和我一样并不认为这是一个好得的编写方式。
实际上并非所有的模板技术都使用条件控制,很多已有的应用如PHP上中的以及我曾见过一个基于ASP.NET的应用,当然还有Java的JDynamiTe。
这样网页上没有任何逻辑,不过这样做的代价是把高亮显示的选择控制移交给编程代码。
你必需做个选择。
也许你也象我一样既不想在网页中使用条件控制,也不想在代码中写html标记,但是这个显示逻辑是无可逃避的(如果你不想被你的老板抄鱿鱼的话),一个可行的方法是用CSS,在编程代码中决定采用哪个css样式。
特别是CSS2技术,其selector机制,可以根据html类型甚至是element的attributes来apply不同的样式。
3.迭代(循环)。
在网页上显示一个数据表单是一个很基本的要求,使用集合标签将不可避免,不过幸运的是,它通常很简单,而且够用。
特别值得一提的是PHP的模板技术和JDynamiTe技术利用html的注释标签很简单的实现了它,又保持了“所见既所得”的特性。
下面是一些技术的比较:
Velocity
变量定义:
用$标志
表达式语句:
以#开始
强控制语言:
变量赋值:
#set$this="Velocity"
外部引用:
#include($1)
条件控制:
#if….#end
非兼容语言
JDynamiTe
变量定义:
用{}包装
表达式语句:
写在注释格式(
-- à)中
弱控制语言
兼容语言
XSLT
变量定义:
xml标签
表达式:
xsl标签
强控制语言:
外部引用:
import,include
条件控制:
if, choose…when…otherwise
非兼容语言
Tapestry
采用component的形式开发。
变量定义(组件定义):
在html标签中加上jwcid
表达式语句:
ognl规范
兼容语言
2. 模板文件:
模板文件指包含了模板语言的文本文件。
模板文件由于其模板语言的兼容性导致不同结果。
与HTML兼容性的模板文件只是一个资源文件,其具有良好的复用性和维护性。
例如JDynamiTe的模板文件不但可以在不同的项目中复用,甚至可以和PHP程序的模板文件互用。
而如velocity的非兼容模板文件,由于其事实上是一个脚本程序,复用性和可维护性大大降低。
3. 拥有动态数据的数据对象:
模板文件包含的是静态内容,那么其所需的动态数据就需要另外提供。
根据提供数据方式的不同可以分为3种:
1. Map:
利用key/value来定位。
这个是最常见的技术。
如velocity的VelocityContext就是包含了map对象。
Example.vm:
Hellofrom$nameinthe$projectproject.
Example.java:
VelocityContextcontext=newVelocityContext();
context.put("name","Velocity");
context.put("project","Jakarta");
2. DOM:
直接操作DOM数据对象,如XSLT利用XPath技术。
3. POJO:
直接利用反射取得DTO对象,利用JavaBean机制取得数据。
如Tapestry。
4. 模板引擎:
模板引擎的工作分为三步:
1.取得模板文件并确认其中的模板语言符合规范。
比如velocity,确定#if有对应得#end等。
Xml+xslt的模型中,xml文件标签是否完整等。
在完成这些工作后,模板引擎通常会把模板文件解析成一颗节点树(包含模板文件的静态内容节点和模板引擎所定义的特殊节点)。
2.取得数据对象。
该数据对象一般通过程序传递引用实现。
现有的大量框架在程序底层完成,处理方式也各自不同,有两种技术分别为推技术和拉技术。
推技术:
controller调用set方法把动态数据注入,模板引擎通过get方法获得,典型代表:
Struts;拉技术:
模板引擎根据配置信息,找到与view对应的model,调用model的get方法取得数据,典型代表:
Tapestry。
3.合并模板文件(静态内容)和数据对象(动态内容),并生成最终页面。
合并的机制一般如下,模板引擎遍历这颗节点树的每一个节点,并render该节点,遇到静态内容节点按正常输入,遇到特殊节点就从数据对象中去得对应值,并执行其表达式语句(如果有的话)。
以下详细说明:
Velocity
Templatetemplate=Velocity.getTemplate("test.wm");
Contextcontext=newVelocityContext();
context.put("foo","bar");
context.put("customer",newCustomer());
template.merge(context,writer);
当调用Velocity.getTemplate方法时,将调用ResourceManger的对应方法。
ResourceManger先查看该模板文件是否在cache中,如果没有就去获取,生成resource对象并调用process()方法,确定该模板是否有效,如果有效,则在内存中生成一个Node树。
当调用template.merge()时,遍历这颗Node树,并调用每个Node的render方法。
对于模板中的变量和对象Node,还将调用execute()方法,从context中取得value。
注:
ResourceManger在runtime\resource包下,Node在runtime\parser\node包下
Tapestry
Tapestry比较麻烦,先介绍一下http请求的处理过程。
当httprequest请求到达时。
该请求被ApplicationServlet捕获,随后ApplicationServlet通过getEngine取到对应的Engine,通过该engine的getService拿到对应的service,调用其service方法执行http请求。
每个service通过RequestCycle对象的getPage方法取得Page对象,并将其设置为该Cycle对象的activePage。
之后service调用renderResponse方法执行输出。
renderResponse调用page的getResponseWriter(output)取得writer对象,并把它传给cycle.renderPage(writer)方法,该方法调用page的renderPage方法。
Page执行renderPage时,首先判断是否有listener的请求,如果有则处理listener请求;然后调用BaseComponentTemplateLoader的process方法把模板文件载入并形成一个component节点树,依次执行节点的renderComponent方法。
每个component对象将通过ongl的机制取得对象属性。
并把该值写入输入流。
例如:
insertcomponent
protectedvoidrenderComponent(IMarkupWriterwriter,IRequestCyclecycle){
if(cycle.isRewinding())
return;
Objectvalue=getValue();
if(value==null)
return;
Stringinsert=null;
Formatformat=getFormat();
if(format==null){
insert=value.toString();
}
else{
try{
insert=format.format(value);
}
catch(Exceptionex){
thrownewApplicationRuntimeException(
Tapestry.format("Insert.unable-to-format",value),this,getFormatBinding().getLocation(),ex);
}
}
StringstyleClass=getStyleClass();
if(styleClass!
=null){
writer.begin("span");
writer.attribute("class",styleClass);
renderInformalParameters(writer,cycle);
}
if(getRaw())
writer.printRaw(insert);
else
writer.print(insert);
if(styleClass!
=null)
writer.end();//
}
getValue为取得insert的value属性。
三、JSP技术分析
1.JSP技术:
JSP,一个伪装后的servlet。
webserver会对任何一个jsp都生成一个对应jsp类,打开这个类,就会发现,jsp提供的是一个代码生成机制,把jsp文件中所有的scriptlet原封不动的copy的到生成的jsp类中,同时调用println把所有的html标签输出。
Test.jsp:
jsptest
Thejsptestfile |
Test_jsp.java:
packageorg.apache.jsp;
importjavax.servlet.*;
importjavax.servlet.http.*;
importjavax.servlet.jsp.*;
importorg.apache.jasper.runtime.*;
publicclassTest_jspextendsHttpJspBase{
privatestaticjava.util.Vector_jspx_includes;
publicjava.util.ListgetIncludes(){
return_jspx_includes;
}
publicvoid_jspService(HttpServletRequestrequest,HttpServletResponseresponse)
throwsjava.io.IOException,ServletException{
JspFactory_jspxFactory=null;
javax.servlet.jsp.PageContextpageContext=null;
HttpSessionsession=null;
ServletContextapplication=null;
ServletConfigconfig=null;
JspWriterout=null;
Objectpage=this;
JspWriter_jspx_out=null;
try{
_jspxFactory=JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=ISO-8859-1");
pageContext=_jspxFactory.getPageContext(this,request,response,null,true,8192,true);
application=pageContext.getServletContext();
config=pageContext.getServletConfig();
session=pageContext.getSession();
out=pageContext.getOut();
_jspx_out=out;
out.write("\r\n");
out.write("
jsptest\r\n");
out.write("
\r\n");
out.write("\r\n ");
out.write("
\r\n\t "); out.write("Thejsptestfile"); out.write("\r\n\t "); out.write(" |
\r\n\t");
out.write("\r\n");
out.write("
\r\n");
out.write("");
}catch(Throwablet){
out=_jspx_out;
if(out!
=null&&out.getBufferSize()!
=0)
out.clearBuffer();
if(pageContext!
=null)pageContext.handlePageException(t);
}finally{
if(_jspxFactory!
=null)_jspxFactory.releasePageContext(pageContext);
}
}
}
2.Taglib技术:
Taglib作为jsp之上的辅助技术,其工作本质依托与jsp技术,也是自定义标签翻译成java代码,不过这次和jsp略有不同,它还要经过几个过程。
先来看一下,实现一个tag的2个要点:
1.提供属性的set方法,此后这个属性就可以在jsp页面设置。
以jstl标签为例c:
outvalue=""/,这个value就是jsp数据到tag之间的入口。
所以tag里面必须有一个setValue方法,具体的属性可以不叫value。
例如setValue(Stringdata){this.data=data;}。
这个“value”的名称是在tld里定义的。
取什么名字都可以,只需tag里提供相应的set方法即可。
2.处理doStartTag或doEndTag。
这两个方法是TagSupport提供的。
还是以c:
outvalue=""/为例,当jsp解析这个标签的时候,在“<”处触发doStartTag事件,在“>”时触发doEndTag事件。
通常在doStartTag里进行逻辑操作,在doEndTag里控制输出。
在处理tag的时候:
0.从tagPool中取得对应tag。
1. 为该tag设置页面上下文。
2. 为该tag设置其父tag,如果没有就为null。
3. 调用setter方法传入标签属性值tag,如果该标签没有属性,此步跳过。
4. 调用doStartTag方法,取的返回值。
5. 如果该标签有body,根据doStartTag返回值确定是否pop该标签内容。
如果要pop其body,则:
setBodyContent(),在之后,doInitBody()。
如果该标签没有body,此步跳过。
6. 调用doEndTag()以确定是否跳过页面剩下部分。
7. 最后把tag类返还给