Java Tools

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  83 随笔 :: 0 文章 :: 16 评论 :: 0 Trackbacks

#

Sun Microsystems 在 1997 年下半年推出了 Servlet API,将它定位为 CGI 开发人员使用的一种功能强大的工具,这些开发人员正在寻找比 CGI(通用网关接口)编程更高效和轻便的优秀解决方案。但是,开发人员很快就发现 Servlet API 有其自身的缺点,从代码的可维护性和可扩展性方面来看,该解决方案难以实施。在某种程度上,这种缺点促使团队开发一种允许在 HTML 中嵌入 Java 代码的解决方案 — JavaServer Pages (JSP) 因此而出现。

不久以后,开发人员意识到将表达与商务逻辑混合在一起的复杂 JSP 页不易于理解和维护。不能编写 scriplet 的页面制作人员所面临的另一个问题是由于标准标记集而带来的 JSP 限制。这些限制使得难点变成利用 JSP 实施自定义标记的机制来创建 JSP 自定义标记。

JSP 标准标记库 (JSTL) 是自定义标记库的集合,它将许多 JSP 应用程序通用的核心功能封装为简单的标记。它不再需要使用 JSP scriptlet
和表达式,而使用表达式的更高级语法。它还实现了通用目的的功能,如迭代和条件化、数据管理格式化、XML 操作、数据库访问、国际化和对本地化信息敏感的格式化标记以及 SQL 标记。JSTL 1.0 推出了 EL 的概念,但只限于 JSTL 标记。在 JSP 2.0 中,您可以使用带模板文本的 EL,甚至可以通过 javax.servlet.jsp.el 获得编程方式的访问。

在我们了解 JSTL 如何适应环境以及与 JSTL 表达式语言相关的限制以后,我们来看 JSP 2.0 的重要优点之一 — JSP 表达式语言 (EL)。我们将特别涉及到以下内容:

JSP 表达式语言定义
在无脚本的 JSP 页面中支持 EL 的机制
表达式语言语法
JSP EL 中有效的表达式
使用 EL 表达式

表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方法。它是一种简单的语言,基于可用的命名空间(PageContext 属性)、嵌套属性和对集合、操作符(算术型、关系型和逻辑型)的访问符、映射到 Java 类中静态方法的可扩展函数以及一组隐式对象。

EL 提供了在 JSP 脚本编制元素范围外使用运行时表达式的功能。脚本编制元素是指页面中能够用于在 JSP 文件中嵌入 Java 代码的元素。它们通常用于对象操作以及执行那些影响所生成内容的计算。JSP 2.0 将 EL 表达式添加为一种脚本编制元素。

脚本编制元素具有三个从属形式:

  • 声明
  • Scriptlet
  • 表达式。

    让我们来看代码中的这三种从属形式:

     

     

    table.getColumn( )

    在将 EL 添加到 JSP 工具包以后,可以使用更简单的语法来编写以上的代码,而获得与以上 JSP 元素相同的结果。EL 表达式的另一个优势是,可以在不允许使用上述任何脚本编制元素从属形式的无脚本的 JSP 页中使用它。但是必须注意,可以不使用三种脚本编制元素从属形式中的任一种来编写 JSP 页,而对 JSP 页是否应该为无脚本形式的选择则完全基于应用程序的需求。如果您希望明确分开表达与商务逻辑,则还可以选择将页面强制为无脚本形式。通过强制成为无脚本页面,必须通过其他元素(如 JavaBean、EL 表达式、自定义操作和标准标记库)提供 JSP 页的动态行为。

  • 有两种机制可以确保页面不包含任何脚本编制元素。每种机制还提供在无脚本的 JSP 页中支持 EL 的方法。

    • 使用页面指示:

      在使用页面指示时,您可以通过将 isELEnabled 指示的值相应地设为“true”或“false”,指定是否支持 EL,如下所示:


    • 使用部署描述符的元素:

      当使用部署描述符的元素时,您可以通过在 标记间包含布尔值“true”或“false”,指定是否支持 EL,如下所示:
      ...


      *.jsp
      true
      true


      ....

    JSP 表达式语言允许页面制作人员使用简单语法访问组件,如:

       
    ${expr}

    在以上的语法中,expr 代表有效的表达式。必须注意,该表达式可以与静态文本混合,还可以与其他表达式结合成更大的表达式。

    有效表达式可以包含文字、操作符、变量(对象引用)和函数调用。我们将分别了解这些有效表达式中的每一种:

    文字

    JSP 表达式语言定义可在表达式中使用的以下文字:

     

    文字 文字的值

    Boolean

    true 和 false

    Integer

    与 Java 类似。可以包含任何正数或负数,例如 24、-45、567

    Floating Point

    与 Java 类似。可以包含任何正的或负的浮点数,例如 -1.8E-45、4.567

    String

    任何由单引号或双引号限定的字符串。对于单引号、双引号和反斜杠,使用反斜杠字符作为转义序列。必须注意,如果在字符串两端使用双引号,则单引号不需要转义。

    Null null

    让我们来看一些使用文字作为有效表达式的示例:

    ${false} 
    ${3*8)

    操作符

    JSP 表达式语言提供以下操作符,其中大部分是 Java 中常用的操作符:

     

    术语 定义

    算术型

    +、-(二元)、*、/、div、%、mod、-(一元)

    逻辑型

    and、&&、or、||、!、not

    关系型

    ==、eq、!=、ne、、gt、<=、le、>=、ge。可以与其他值进行比较,或与布尔型、字符串型、整型或浮点型文字进行比较。

    空操作符是前缀操作,可用于确定值是否为空。

    条件型 A ?B :C。根据 A 赋值的结果来赋值 B 或 C。

    让我们来看一些使用操作符作为有效表达式的示例:

    ${ (6 * 5) + 5 } 
    ${empty name}

    隐式对象

    JSP 表达式语言定义了一组隐式对象,其中许多对象在 JSP scriplet 和表达式中可用:

    术语 定义

    pageContext

    JSP 页的上下文。它可以用于访问 JSP 隐式对象,如请求、响应、会话、输出、servletContext 等。例如,${pageContext.response} 为页面的响应对象赋值。

    此外,还提供几个隐式对象,允许对以下对象进行简易访问:

     

    术语 定义

    param

    将 请求参数名称映射到单个字符串参数值(通过调用 ServletRequest.getParameter (String name) 获得)。getParameter (String) 方法返回带有特定名称的参数。表达式 $(param.name) 相当于 request.getParameter (name)。

    paramValues

    将 请求参数名称映射到一个数值数组(通过调用 ServletRequest.getParameter (String name) 获得)。它与 param 隐式对象非常类似,但它检索一个字符串数组而不是单个值。表达式 ${paramvalues.name) 相当于 request.getParamterValues(name)。

    header

    将请求头名称映射到单个字符串头值(通过调用 ServletRequest.getHeader(String name) 获得)。表达式 ${header.name} 相当于 request.getHeader(name)。

    headerValues

    将 请求头名称映射到一个数值数组(通过调用 ServletRequest.getHeaders(String) 获得)。它与头隐式对象非常类似。表达式 ${headerValues.name} 相当于 request.getHeaderValues(name)。

    cookie 将 cookie 名称映射到单个 cookie 对象。向服务器发出的客户端请求可以获得一个或多个 cookie。表达式 ${cookie.name.value} 返回带有特定名称的第一个 cookie 值。如果请求包含多个同名的 cookie,则应该使用 ${headerValues.name} 表达式。
    initParam 将上下文初始化参数名称映射到单个值(通过调用 ServletContext.getInitparameter(String name) 获得)。

    除了上述两种类型的隐式对象之外,还有些对象允许访问多种范围的变量,如 Web 上下文、会话、请求、页面:

     

    术语 定义

    pageScope

    将页面范围的变量名称映射到其值。例如,EL 表达式可以使用 ${pageScope.objectName} 访问一个 JSP 中页面范围的对象,还可以使用 ${pageScope.objectName.attributeName} 访问对象的属性。

    requestScope

    将 请求范围的变量名称映射到其值。该对象允许访问请求对象的属性。例如,EL 表达式可以使用 ${requestScope.objectName} 访问一个 JSP 请求范围的对象,还可以使用 ${requestScope.objectName.attributeName} 访问对象的属性。

    sessionScope

    将会话范围的变量名称映射到其值。该对象允许访问会话对象的属性。例如:


    $sessionScope.name}

    applicationScope

    将应用程序范围的变量名称映射到其值。该隐式对象允许访问应用程序范围的对象。

    必须注意,当表达式根据名称引用这些对象之一时,返回的是相应的对象而不是相应的属性。例如:即使现有的 pageContext 属性包含某些其他值,${pageContext} 也返回 PageContext 对象。

     

    EL 表达式可用于两种情况:

    • 作为标准操作和自定义操作中的属性值

    • 在 JSP 文件的模板文本中,如 HTML 或非 JSP 元素 — 在这种情况下,模板文本中的表达式被赋值并插入到当前的输出中。但是,必须注意,如果标记的主体被声明为与标记相关,则不会对表达式赋值。

    posted @ 2007-07-02 12:54 和田雨 阅读(665) | 评论 (0)编辑 收藏

    ServletJSP技术是用Java开发服务器端应用的主要技术,是开发商务应用表示端的标准。Java开发者喜欢使用它有多种原因,其一是对于已经熟悉Java语言的开发者来说这个技术容易学习;其二是Java把“一次编写,到处运行”的理念带入到Web应用中,实现了“一次编写,到处实现”。而且更为重要的是,如果遵循一些良好的设计原则的话,就可以把表示和内容相分离,创造出高质量的、可以复用的、易于维护和修改的应用程序。比方说,在HTML文档中如果嵌入过多的Java代码(scriptlet),就会导致开发出来的应用非常复杂、难以阅读、不容易复用,而且对以后的维护和修改也会造成困难。事实上,在CSDN的JSP/Servlet论坛中,经常可以看到一些提问,代码很长,可以逻辑却不是很清晰,大量的HTML和Java代码混杂在一起,让人看得一头雾水。这就是随意开发的弊端。

      如果你已经基本了解JSPServlet的各项技术(最好也开发过一些Web应用),那么我们可以一起探讨一下如何开发“好”的应用的一些指导原则。我们首先对ServletJSP技术做一个浏览。

      ServletJSP概览

      早期的动态网页主要采用CGI(Common Gateway Interface,公共网关接口)技术,你可以使用不同的语言编写CGI程序,如VB、C/C++Delphi等。虽然CGI技术发展成熟且功能强大,但由于编程困难、效率低下、修改复杂等缺点,所以有逐渐被取代的趋势。在所有的新技术中,JSP/Servlet具备更高效、更容易编程、功能更强、更安全和具有良好的可移植性,因而被许多人认为是未来最有发展前途的动态网站技术。

      与CGI相似,Servlet支持请求/响应模型。当一个客户向服务器递交一个请求时,服务器把请求送给ServletServlet负责处理请求并生成响应,然后送给服务器,再由服务器发送给客户。与CGI不同的是,Servlet没有生成新的进程,而是与HTTP Server处于同一进程中。它通过使用线程技术,减小了服务器的开销。Servlet处理请求的过程是这样的:当收到来自客户端的请求后,调用service方法,该方法中Servlet先判断到来的请求是什么类型的(GET/POST/HEAD…),然后调用相应的处理方法(doGet/doPost/doHead…)并生成响应。

      别看这么复杂,其实简单说来Servlet就是一个Java类。与一般类的不同之处是,这个类运行在一个Servlet容器内,可以提供session管理和对象生命周期管理。因而当你使用Servlet的时候,你可以得到Java平台的所有好处,包括安全性管理、使用JDBC访问数据库以及跨平台的能力。而且,Servlet使用线程,因而可以开发出效率更高的Web应用。

      JavaServer Pages (JSP)

      JSP技术是J2EE的一个关键技术,它在更高一级的层次上抽象Servlet。它可以让常规静态HTML与动态产生的内容相结合,看起来像一个HTML网页,却作为Servlet来运行。现在有许多商业应用服务器支持JSP技术,比如BEA WebLogic、IBM WebSphere、 JRun等等。使用JSP比用Servlet更简单。如果你有一个支持JSP的Web服务器,并且有一个JSP文件,你可以把它放倒任何静态HTML文件可以放置的位置,不用编译,不用打包,也不用进行ClassPath的设置,就可以像访问普通网页那样访问它,服务器会自动帮你做好其他的工作。

      JSP工作原理

      JSP 文件看起来就像一个普通静态HTML文件,只不过里面包含了一些Java代码。它使用.jsp的后缀,用来告诉服务器这个文件需要特殊的处理。当我们访问一个JSP页面的时候,这个文件首先会被JSP引擎翻译为一个Java源文件,其实就是一个Servlet,并进行编译,然后像其他Servlet一样,由Servlet引擎来处理。Servlet引擎装载这个类,处理来自客户的请求,并把结果返回给客户,如下图所示:


    图1: 调用JSP页面的流程

      以后再有客户访问这个页面的时候,只要该文件没有发生过更改,JSP引擎就直接调用已经装载的Servlet。如果已经做过修改的话,那就会再次执行以上过程,翻译、编译并装载。其实这就是所谓的“第一人惩罚”。因为首次访问的时候要执行一系列以上的过程,所以会耗费一些时间;以后的访问就不会这样了。

    开发原则


      这一部分我们列出一些开发原则,重点是JSP页面。关于如何分离表现和内容的MVC因为要涉及到JSPServlet的整合,我们稍候再谈。

      不要在JSP页面中嵌入过量的Java代码:对于非常简单或是测试性的代码,把所有的Java 代码直接放入JSP页面内是没有问题的。但是这种方法不应该被过度使用,否则就会产生一大堆HTML和Java混合起来的代码,让人难以阅读和理解。解决方法是写一个单独的类,用来执行相关的计算。一旦这个类测试通过,就可以把它放在任何执行同样计算的场合中。这样可以促进代码的复用。

      选择合适的包含(include)机制: 如果一个应用中每个页面有一样的抬头和底部,或者还有导航条,那么就应该把它们放到一个单独的文件中,然后在每一个页面中使用包含机制把它们加入到这个页面中:

    Include 指令: <%@ include file="filename" %>或等效xml语法
    <jsp:directive.includefile=”filename” />

    Include 动作: <jsp:include page="page.jsp" flush="true" />

      Include指令是当JSP页面翻译为Servlet的时候包含另外一个文件,Include 动作是当请求时包含另外一个文件的输出。如果被包含的文件不是经常改动的时候,我建议使用Include 指令,这样速度更快。如果被包含的文件需要不时改动或者知道请求时才能决定需要包含的内容时,那么应该使用Include 动作。

      如果你使用JSP标准标记库(JavaServer pages Standard Tag Library即JSTL)的话,那么还有第三中包含机制<c:import>,可以用来包含本地或者远程的资源。例如:

    <c:import url="./copyright.html"/>
    <c:import url="http://www.somewhere.com/hello.xml"/>

      不要把业务逻辑和表示混合在一起: 复杂的应用涉及大量的代码,因而把业务逻辑和前端的表示相分离就显得格外重要,这种分离可以让任何一方的变化不会影响到另外一方。所以,所有的JSP代码都应该限制在表示层,可是如果这样的话,你如何实现你的业务逻辑呢?这就是JavaBean所做的事情。JavaBean技术是一个独立于平台的组件模型,它让开发者编写、测试通过一个组件后,可以随处使用,提高了复用性。在JSP技术中,JavaBean实现了业务逻辑部分,它把数据返回给JSP页面,由JSP页面负责格式化数据并输出到客户端的浏览器。在JSP页面中使用JavaBean组件的好处是:

      产生了可以复用的组件:任何应用都可以使用这些组件

      可以把业务逻辑和表示相分离:你可以修改数据的显示方式而不用考虑业务逻辑。这样做的结果也可以明确工作中开发人员的分工,网页开发人员可以把精力放到如何显示数据上,Java开发者则更为关注业务逻辑的实现。

      对于JavaBean你不用提供源代码,这样你的代码就不会被浏览器网页的人轻易获得,可以保护你的劳动成果。

      如果你的应用中使用了EJB组件,那么业务逻辑就应该放置在EJB中,因为EJB模型提供了生命周期管理、事务管理以及多客户访问域对象(Entity Beans)。你可以仔细看一下Enterprise BluePrints中的例子,它就是这么做的。

      使用定制的标记: 上面我们已经讨论过,把所有Java代码嵌入到JSP页面内并不合适,因为网页开发人员并不一定懂得Java语言,更难以理解Java语法。JavaBean可以封装很多Java代码,不过在JSP页面内使用JavaBean仍然要求页面开发人员了解一些Java语法。

      JSP技术中包含了定制标记库的功能。Java开发人员可以生成自己的标记库,这样网页设计人员就可以使用类似HTML的语法来使用这些标记。编写和使用自己定制的标记库可以在更大程度上促进业务逻辑和表示的分离。使用定制标记库主要有以下好处:

      可以消除在JSP页面中使用scriptlet 标记使用的任何参数都可以通过属性传入,从而不需要使用Java代码就可以达到希望的目的。

      可以简化使用 网页设计人员不需要学会使用Java语法,他们可以用类似HTML语法就可以使用标记。
    不懂Java的网页设计人员可以使用标记库来完成单独使用HTML不能完成的任务。

      提高了复用性 标记库完全可以复用,这可以节省开发和测试的时间。Scriptlet代码只能在“拷贝粘贴”级别上进行“复用”。

      简单说来,你可以像用HTML构建表示层一样使用标记库完成非常复杂的任务。下面是表页标记库的一些注意事项:

      1. 保持简洁性:如果一个标记需要好几个属性的话,那么尽可能把它分为几个标记。

      2. 保持复用性:同标记的使用人员(网页设计人员)多多交流,尽量开发出可以高度复用的标记库。

      3. 不要一切都从头开始:现在已经有一些可以免费使用的标记库,比如Jakarta Taglibs。如果你要用到一个标记,先看看是不是已经有现成的可以使用。

      不要“重新发明轮子”,不要一切从头开始: 通过定制组件可以提高复用性,不过定制组件仍然需要编写、测试和调试程序。问题是这个事情别人可能已经实现了,而且你的实现方式并不一定比人家做得更好。这就是JSP标准标记库(JavaServer Pages Standard Tag Library, JSTL)要做的事情(JSTL请参考JSTL官方网站)。JSTL提供了循环、读属性、遍历各种数据结构、条件表达式求值等各种标记。它也提供了一些复杂的标记,甚至像解析XML文档的标记它都有。所以如果你要用到一个标记的话,最好先看看有没有别人已经实现的可以使用,而不要次次从头开始,自己搞一套。

      使用JSTL表达使语言(JSTL Expression Language): 传递给JSP页面的数据一般通过JSP作用域属性或者请求参数来进行。专门为网页开发者设计的表达式语言(Expression Language, EL)把使用作用域属性传递信息作为从业务逻辑向JSP页面传递信息的标准方式。这里要注意的是,EL只是JSP技术中关键的一个方面,并不是一种通用的程序设计语言。相反,它只是一种数据访问语言,它可以简化应用程序的数据的访问,不用Scriptlet和请求时表达式求值就可以访问数据。

      在JSP中,网页设计师要使用表达式语法<%= name %>或JavaBean组件来取得某些变量或属性的值,例如:

    <tagLib:tag attribute="<%=

    pageContext.getAttribute("name") %>">

      或

    <%= aCustomerBean.getAddress().getCountry() %>

      表达使语言让网页设计师可以使用简化的语法来访问信息。如果你只是要访问一个简单的变量,你可以使用这样的语法:

    <tagLib:tag attribute="${name}">

      如果你要访问一个嵌套JavaBean的属性,你可以这样:

    <tagLib:tag attribute ="${

    aCustomerBean.address.country}">

      表达式语言(EL)借用了JavaScript 的语法,所以如果你对JavaScript 很熟悉的话,你就会觉得巨爽。

      使用过滤器(filter): 过滤器是一个对象,可以传输请求或修改响应。它可以在请求到达Servlet/JSP之前对其进行预处理,而且能够在响应离开Servlet/JSP之后对其进行后处理。所以如果你有几个Servlet/JSP需要执行同样的数据转换或页面处理的话,你就可以写一个过滤器类,然后在部署描述文件(web.xml)中把该过滤器与对应的Servlet/JSP联系起来。

      创建过滤器其实很容易,你只须实现javax.servlet.Filter接口及它的三个方法:

    public void init(FilterConfig config)

    public void doFilter(ServletRequest req, ServletResponse rep,

    FilterChain chain)

    public void destroy()

      这样,你就可以完成你的过滤器。

    使用可移植的安全模型:大部分的应用服务器都提供了安全模型,不过一般它们都是针对某一个服务器或某一个厂商专有的。如果你的应用需要移植的话,那么你的应用最好使用可以移植的安全模型。如果你的应用有一些预先定义的固定用户的话,那么你可以使用FROM验证和BASIC验证。可是如果你要动态生成客户的话(一般都是这种情况),你可能就需要使用服务器特定的API来创建和管理用户。这样当你的应用移植到另外一个服务器时,你可能就会碰到API不兼容的问题。这种情况下,最好的解决方法是使用适配器(Adapter)模式(如果你对设计模式不熟悉的话,请参看GoF的《设计模式》一书)。

      用数据库来保存持久性数据: Servlet/JSP中可以使用HttpSession对象也就是会话对象来保存用户的临时数据。不过如果你想保存持久性数据的时候,你应该使用数据库,数据保存数据会更安全,而且对客户所用的浏览器没有什么要求。这样即使你的应用服务器由于某种原因崩溃了,你的数据依然良好。

      高速缓存页面: 应用程序中总有一些东西是相对固定的,而另外一些东西是经常变化的。你应该使用静态的HTML文档来存储那些相对固定的内容,这样客户端就可以进行高速缓存,客户每次访问你的应用时,只需访问已经改动的部分。这样可以加快客户的访问速度。

      使用连接池: 如果你要自己写数据库访问代码的话,我觉得使用你应该学会如何使用数据库连接池技术。每一个服务器都有针对数据库连接池的配置文档,你要学习一下如何使用。数据库连接池可以加速你的应用的数据访问的速度,而且由于服务器替你管理了数据库连接,这可以节省你的很多工作。

      缓存数据库的访问结果: 如果你的应用要对数据库进行频繁访问的话,你可以使用一个对象来缓存你的数据,这样你就可以节省大量访问数据库的开销。在《J2EE核心模式》和《实用J2EE设计模式编程指南》两本书中都有关于值对象模式(Value Object Pattern)的详细探讨,你可以参考这两本书来获得相应的知识。

      使用数据访问对象模式:如果你的应用需要访问多个数据库系统或者可能会移植到其它的存储系统中,那么你针对特定厂商的优化代码就可能会失效。使用通用的代码存在执行效率的问题,而使用优化代码又存在移植的问题。所以就产生了数据访问对象模式(Data Access Object Pattern, DAO),该模式既提供了各数据库厂商的适应性,又能利用到他们提供的独特的好处。按照面向对象的分离任务的原则,该模式将与企业信息系统(Enterprise Information System, EIS)通讯需要的逻辑隔离到它自己的类中。这样,事物对象,如Servlet/JSP组件、JavaBean就可以利用数据访问对象(DAO)处理所有与EIS有关的事务。

      最好采用JSPXML语法: JSP技术中经常存在着两种完成同一个任务的语法,一种是常规的JSP语法,一种是对应的XML语法。虽然两种语法作用相同,你最好还是使用XML语法。存在两种语法的原因是,JSP语法可以与以前的代码兼容,而J2EE使用XML作为其交换数据的核心,所以同时提供了XML语法。随着J2EE的发展,XML的作用会越来越大,所以我建议你使用XML语法。

      研究Sun提供的J2EE BluePrints: Sun的Enterprise BluePrints 提供了大量指导原则、设计模式和很好的例子(宠物店,Pet Store)。你可以好好研究一下这些内容,这样可以提高你的设计和开发水平。

      整合ServletJSP

      JSP技术规范种给出了两种使用JSP开发Web应用的方式,这两种方式可以归纳为模型一和模型二,这两种模型的主要差别在于它们处理业务的流程不同。模型一,如下图所示,称之为JSP+JavaBeans模型。在这一模型中,JSP页面独自响应请求并将处理结果返回给客户,所有的数据通过JavaBean来处理,JSP实现页面的表现。


    图2: JSP模型一

      从上图可以看出,模型一也实现了页面表现和业务逻辑相分离。然而使用这种方式就要在JSP页面使用大量的Java代码,当需要处理的业务逻辑很复杂时,这种情况会变得非常糟糕。大量嵌入式代码使整个页面程序变得异常复杂。对于前端界面设计的网页开发人员来说,这简直是一场噩梦。所以,模型一不能满足大型应用的需要,但是对于小型应用,因为该模型简单,不用涉及诸多要素,从而可以很好地满足小型应用的需要,所以在简单应用中,可以考虑模型一。

      模型二,如下图所示,称之为JSP+Servlet+JavaBeans模型。这一模型结合了JSPServlet技术,充分利用了JSPServlet两种技术原有的优势。这个模型使用JSP技术来表现页面,使用Servlet技术完成大量的事务处理,使用Bean来存储数据。Servlet用来处理请求的事务,充当一个控制者的角色,并负责向客户发送请求。它创建JSP需要的Bean和对象,然后根据用户请求的行为,决定将哪个JSP页面发送给客户。


    图3: JSP模型二

      从开发的观点看,模型二具有更清晰的页面表现,清楚的开发角色的划分,可以充分利用开发团队中的网页设计人员和Java开发人员。这些优势在大型项目中表现得尤为突出,网页设计人员可以充分发挥自己的美术和设计才能来充分表现页面,程序编写人员可以充分发挥自己的业务逻辑处理思维,实现项目中的业务处理。

      另外,从设计结构来看,这种模型充分体现了模型视图控制器(MVC)的设计架构。事实上,现存的很多开发框架都是基于这种模型的,充分实现了MVC ,例如Apache Struts框架和JavaServer Faces框架
    posted @ 2007-07-02 12:51 和田雨 阅读(282) | 评论 (0)编辑 收藏

    最近整理以前写的东东,发现2004年底的时候对比各类Java反编译器时记下来的一篇心得,也不知道是不是有点儿过时了,仅供大家参考吧。

    =====================================================================

    JAVA语言是19955月由SUN公司发布的,由于其安全性高、代码优化、跨平台等特性,迅速取代了很多传统高级语言,占据了企业级网络应用开发等诸多领域的霸主地位。

    不过,JAVA最突出的跨平台优势使得它不能被编译成本地代码,而要以中间代码的形式运行在虚拟机环境中,这使得JAVA的反编译要比别的高级语言容易实现,并且反编译的代码经过优化后几乎可以与源代码相媲美。

    为了更好地保护知识产权,避免本公司的智力成果轻易被人窃取,开发者有必要对反编译工具深入了解,以便有针对性地采取保护措施。

    目前,比较流行的JAVA反编译工具超过30种,其中有三款堪称精品:

    一、         应用广泛的JAD

    在众多的JAVA反编译工具中,有几种非常著名的工具使用了相同的核心引擎——JAD,其中主要包括:Front End PlusmDeJavaDecafe ProCavaj Java DecompilerDJ Java DecompilerNMI’s Java Class Viewer和国产的JAVA源代码反编译专家等等。

    JAD本身是一个命令行工具,没有图形界面,上述的这些工具大多是在JAD内核的基础之上加了一个图形界面而已。这么多种产品的共同选择,足可证明JADJAVA反编译领域中的尊贵地位。

    笔者用来测试的JAD版本是1.5.8f。

    JAD是使用Microsoft Visual C++开发的,运行速度非常快,可以处理很复杂的JAVA编译文件。众多的参数使JAD可以灵活应付多种加密手段,令反编译的代码更加优化和易读。由于JAD参数太多,没必要一一解释,其中有几个最常用的如下:

             -d

    - 用于指定输出文件的目录

     

             -s - 输出文件扩展名(默认为: .jad),通常都会把输出文件扩展名直接指定为.java,以方便修改的重新编译。

             -8       - Unicode字符转换为ANSI字符串,如果输出字符串是中文的话一定要加上这个参数才能正确显示。

    最常用的反编译指令如下所示:

    Jad –d c:\javasource –s .java -8 javatest.class

    这条指令将当前目录下的javatest.class反编译为javatest.java并保存在c:\javasource目录里,其中的提示输出为中文,而不是Unicode代码。

    二、         源码开放的JODE

    JODE是全球最大的开源项目网站Sourceforge.net的成员,不要以为源码开放就小瞧它,在所有的JAVA反编译器中,JODE的反编译效果是最好的,尤其是对付一些常见的加密手段,例如混淆技术等,更是出类拔粹。

    JODE本身也是纯JAVA开发的,最近越来越多的JAVA反编译软件也选择JODE来做它们的核心引擎,例如JCavaj Java DecompilerBTJ (Back To Java)jEdit's JavaInsight plugin等。

    JODE是一个可运行的JAR文件,在windows环境下双击即可运行。

    需要特别说明的是,JODE不是通过常规的Open->File的方式来加载JAVA编译后的类文件(*.class)或是类包(*.jar)的,而是通过在Options菜单中的Set Classpath来实现的,单独的类文件可以将它的上一级目录作为Classpath输入,然后再选择Reload Classpath即可。

      

    新加入的类包或是类的名字会在左侧窗口出现,双击类包名可以展开目录树结构,双击需要反编译的类名则在右上角的窗口中直接显示反编译后的源代码。

     

    三、         独树一帜的DAVA

    DAVA不是一个独立的JAVA反编译器,而是JAVA代码优化工具Soot的一部分。SootJODE一样是纯JAVA开发的,也是一个独立的JAR包,但却不能通过双击直接运行,而是象JAD一样在命令行状态运行。

    Soot对环境变量的配置要求非常严格,通常情况下要对CLASSPATH做如下设置:

    Set CLASSPATH=%CLASSPATH%;c:\sootdir\sootclasses-2.1.0.jar;.;

    其中的c:\sootdir\是下载的soot类包放置的路径,CLASSPATH末尾的.;代表了当前目录,如果不加上这个的话Soot经常会报一个找不到类的错误。

          DAVA是作为Soot的一个参数使用的,通常的用法如下:

    Java soot.Main –f dava –d c:\javasource javatest

    注意最后的类名不用带.class后缀,因为它默认是处理class文件,这个操作与前述的JAD的参数效果相同。

    DAVA采取了流程优化的方式进行反编译,与传统反编译思路不尽相同,但却对改变流程类的加密方法有独特的反编译效果。

     

    上述的三种工具各有千秋,但效果都非常不错。经测试,它们基本上都可以把JDK自带的一些例程完全反编译,然后不加任何修改可再编译成功,并能正常运行!

    (文中工具均经过本人亲手测试,当时用的是jdk1.4.2_03,现在离写文章的时候过了一年多了,jdk都出到1.5了,怕是有些程序也不太好反编了)

    /*原创作品,转载请注明出处*/

    posted @ 2007-07-02 12:50 和田雨 阅读(2474) | 评论 (0)编辑 收藏

         摘要:   Apache Ant Project Ant 众所周知,Ant(蚂蚁)是一套基于java的程序...  阅读全文
    posted @ 2007-07-02 12:49 和田雨 阅读(254) | 评论 (0)编辑 收藏

     

    Java的线程调度操作在运行时是与平台无关的。一个多任务系统需要在任务之间实现QoS(Quality of Service)管理时,如果CPU资源的分配基于Java线程的优先级,那么它在不同平台上运行时的效果是很难预测的。本文利用协调式多任务模型,提出一个与平台无关、并且能在任务间动态分配CPU资源的方案。
      现在,由于计算机系统已经从人机交互逐步向机机交互转化,计算机和计算机之间的业务对于时间的要求非常高。软件系统对于业务的支持已经不仅表现为对不同业务的逻辑和数据(算法+数据结构)支持,而且还表现为对同时处理不同任务的时效性(任务响应速度)支持。一般,任务响应的速度可以通过算法优化及并行运算分担负载等手段来提高。但是,用户业务逻辑的复杂度决定了算法优化的发挥空间,硬件规模决定了所能够承担负载的大小。我们利用Java平台的特点,借鉴协调式多任务思想,使CPU资源能够在任务间动态分配,从而为时间要求强的任务分配更多的CPU运行资源。这也可以充分利用现有硬件,为用户业务提供最大的保障。

      用Java解决问题

      本着软件系统结构和现实系统结构一致的思想,开发复杂业务服务的程序一般按照计算机任务和现实业务对应的思路,最终形成一个大规模的多任务系统。由于其跨平台性,Java系统可以随着业务的扩大,平滑地升级到各种硬件平台上。由于Java自身的发展及其应用场合的不断扩大,用它实现多任务系统已经成为当前的应用方向。在J2EE(Java2 Enterprise Edition)推出以后,Sun公司已经将Java的重心放在了服务器端(Server Side)系统的构造上。由于客户/服务器模型固有的多对一的关系,服务器端程序也必然是一个多任务系统。

      在Java多任务应用中,动态地将CPU资源在任务间分配有很重要的意义。比如一个Inte.Net服务商的系统往往有多种任务同时运行,有HTTP、FTP、MAIL等协议的支持,也有商务、娱乐、生活、咨询等业务的服务。在白天,网站希望系统的CPU资源尽量保障网上用户的服务质量,提高电子商务等任务的响应速度;晚上则希望让自己的娱乐服务和资料下载尽可能满足下班后人们的需要。另外,在新兴的网管(比如TMN, Telecommunication Management.Network)等应用领域中,服务程序往往需要支持成千上万个并发响应事件的被管理对象(MO,Managed Object)。对于被管理对象执行的操作,不同用户在不同时刻往往有不同的时间要求。

      方案选择

      在考虑动态分配CPU资源的实施方案时,往往有以下两点要求:

      1. 须充分利用现有硬件资源,在系统空闲时,让低优先级任务也能够得到系统所能给予的最快响应。

      2.当硬件资源超负荷运行时,虽然系统中有大规模、多数量的任务不能处理,但它不应受影响,而能够顺利处理那些能够被处理的、最重要的高优先级任务。

      多任务系统要用多线程实现的最简单方法就是将线程和任务一一对应,动态调整线程的优先级,利用线程调度来完成CPU资源在不同任务间动态分配。这种思路在以前使用本地化代码(Native Code),充分利用特定硬件和操作系统技巧的基础上是基本可行的。但在跨平台的Java环境中,这个思路对仅有小规模任务数的简单系统才可行,原因有以下两点:

      1. Java的线程虽然在编程角度(API)是与平台无关的,但它的运行效果却和不同操作系统平台密切相关。为了利用更多的CPU资源,Java中的一个线程(Thread)就对应着不同操作系统下的一个真实线程。因为Java虚拟机没有实现线程的调度,所以这些Java的线程在不同操作系统调度下运行的差异性也就比较明显。例如在Windows系统中,不仅线程的优先级少于Java API参数规定的十个优先级,而且微软明确反对程序员动态调整线程优先级。即使在操作系统中有足够的优先权,让线程优先级的参数和真实线程的优先级对应,不同操作系统的调度方式也会有许多不同。这最终会造成代码在不同平台上的行为变得不可预测。这就很难满足复杂的、大规模并发任务的众多优先级需求,从而很难达到用户业务需要达到的效果。

      2. 由于在Java系统中,线程被包装在一个Java语言的对象类—Thread中,所以为了完成Java语言对象和操作系统线程的对应,Java线程的系统开销还是比较大的(在NT 4.0中,平均每个线程大致占用30KB内存)。因此如果让Thread对象个数和成千上万的任务数同比例增长,就显然是不合理的。

      综上所述,根据并发多任务的大规模需求和Java平台固有的特点,想要利用Java Thread对象的优先级调整CPU资源的分配是非常困难的,所以应该尽量避免让线程和任务直接对应,也尽量避免使用操作系统线程优先级的调度机制。

      解决方案

      根据以上分析,问题的症结在于:多任务系统中的任务在Java语言中的对应以及任务间的相互调度。

      从本质上看,一个任务就是一系列对象方法的调用序列,与Java的Thread对象或者别的类的对象没有必然联系。在避免使用不同操作系统线程调度且同时Java虚拟机又没有线程调度能力的情况下,要想构造一个协调式多任务系统,让各个任务相互配合就成了最直接的思路。协调式多任务系统一般有以下特点:

      1. 任务由消息驱动,消息的响应代码完成任务逻辑的处理;

      2. 消息队列完成消息的存储和管理,从而利用消息处理的次序体现任务优先级的不同;

      3. 任务中耗时的消息响应逻辑能够主动放弃CPU资源,让别的任务执行(像Windows 3.1中的Yield函数、Visual Basic中的DoEvents语句)。

      可能出于巧合,Java语言具有构造协调式多任务系统天然的条件。Java对象的方法不仅是一个函数调用,它还是一个Java.lang.reflect.Method类的对象。而所有对象的方法都可以通过Method类的invoke方法调用。如果能使每个任务所对应的一系列方法全部以对象形式包装成消息,放到消息队列中,然后再按照自己的优先级算法将队列中的消息取出,执行其Method对象的invoke调用,那么一个基本的协调式多任务系统就形成了。其中,任务的优先级和线程的优先级没有绑定关系。该系统的主体调度函数可以设置成一个“死循环”,按照需要的优先级算法处理消息队列。对于有多重循环、外设等待等耗时操作的消息响应函数,可以在响应函数内部递归调用主体调度函数,这一次调用把原来的“死循环”改成在消息队列长度减少到一定程度(或者为空)后退出。退出后,函数返回,执行刚才没有完成的消息响应逻辑,这样就非常自然地实现了协调式系统中任务主动放弃CPU资源的要求。

      如果仅仅做到这一步,完成一个像Windows 3.1中的多任务系统,实际只用了一个线程,没有利用Java多线程的特点。应该注意到,虽然Java系统中线程调度与平台相关,但是相同优先级的线程之间分时运行的特点基本上是不受特定平台影响的。各个相同优先级的线程共享CPU资源,而线程又被映射成了Java语言中的Thread对象。这些对象就可以被认为是CPU资源的代表。Thread与线程执行代码主体的接口—Runnable之间是多对一的关系。一个Runnable可以被多个Thread执行。只要将Runnable的执行代码设置成上述的消息调度函数,并和消息队列对应上,那么就可以通过控制为它服务的Thread个数来决定消息队列执行的快慢,并且在运行时可以动态地新增(new)和退出Thread对象。这样就能任意调整不同消息队列在执行时所占用CPU资源的多少。至此,任何一个Java调用都可以在Thread个数不同的消息队列中选择,并可以调整这些消息队列服务的Thread个数,从而实现在运行时调整任务所占用的CPU资源。

      纵观整个方案,由于仅仅基于Java语言固有的Method对象,不同任务间动态分配CPU资源并没有对任务的性质及其处理流程有任何限制,那么在消息队列中没有高优先级消息时,低优先级消息的处理函数自然会全部占用CPU资源。在不同消息队列处理速度任意设置时,并没有将特定的消息限制在快的或者慢的消息队列上。如果系统的负荷超出(比如消息队列长度超过一定限制),只要将队列中低优先级消息换出或者拒绝不能处理的消息进入,那么系统的运行就可以基本上不受负荷压力的影响,从而最大保障用户的关键业务需求。

      当然,协调式多任务的思想也有其局限性,主要就是它的调度粒度比较大。系统能够保证的粒度是一次消息处理过程。如果消息处理逻辑非常费时,那么编程人员就必须再处理函数内部,让系统主动让出CPU资源。这虽然需要在处理消息响应逻辑时增加一个考虑因素,但是,在Windows系统盛行的今天,这是一个已经被普遍接受的思路。由于方案中并没有局限为消息队列服务的线程数目,所以一个长时间的消息响应只会影响一个线程,而不会对整个系统产生致命的影响。除了调度粒度的问题以外,还有访问消息队列操作在各个线程间互斥的问题。取出消息的过程是串行化的,因此对于这一瓶颈的解决方案就是:假设取出一条消息的操作相对于处理消息的消耗可以忽略不计,那么对于多次调用且仅有两三行响应逻辑的消息,编程人员通过函数调用就可以直接执行。

      前面比较详细地阐述了多任务系统中任务的划分以及执行等内容。虽然这些是一个系统的核心,但是在一个实用的系统中,还需要任务间的同步、互斥等机制。在上述框架内,互斥可以简单地用Java的Synchronized机制实现。由于任务可以主动让出执行权限,要实现等待(Wait任务中止)和通知(Notify任务继续),从而实现任务同步也就比较容易了。

    posted @ 2007-07-02 12:48 和田雨 阅读(220) | 评论 (0)编辑 收藏

    SUN 的新版J2EE1.4提供了在J2EE中开发Web Service的基础,对开发工具做了一些重要的增强,在应用程序部署和服务器管理方面也提供了新的标准,在集成性和安全性方面有所增强,提升了J2EE开发Web应用程序的能力;在编程模型方面的重要变化包括JSP表达式语言、简化的标记库等;EJB 2.1中提供了新的timer服务,查询语言(QL)也有所增强;Jdbc3.0 API把通常的Jdbc API与扩充API结合起来;J2EE Connectors规范和独立于消息类型的EJB提供了对双向通信的支持。下面给大家重点介绍J2EE1.4中包含的JSP2.0、Servlet2.4、Jdbc3.0以及EJB2.1方面的新特性。

        JSP 2.0属于J2EE 1.4平台,它在JSP 1.2基础之上增加了新的功能。它保证了向下兼容,原先使用的JSP技术在JSP 2.0中都可以支持。JSP 2.0的新功能主要包括下面几部分:

    一)运行环境变化

    1、web.xml格式变化

    我们知道JSP 1.2可以在Java 2标准版1.3版本运行,而JSP 2.0要求使用Java 2标准版1.4或更新版本,JSP 2.0使用由Servlet 2.4规定的Web程序部署描述格式。

    在Web程序描述文件web.xml中需要使用xml schema打头的格式。在web.xml中主要的变化是所有有关JSP的设置信息要放在<jsp-config>标记中。下面程序例1显示了一个web.xml大致的样子。

        例1:

        <?xml version="1.0" encoding="IS0-8859-1"?>

        <web-app xmlns="http://java.sun.com/xml/ns/j2ee"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"

        version="2.4">

        .......

        <jsp-config>

        <taglib>   

        <taglib-uri>

        http://www.icconcept.com/ics/sample-taglib

        </taglib-uri>

        <taglib-location>

        /WEB-INF/jsp/sample-taglib.tld

        </taglib-location>

        </taglib>

        ........

        <jsp-property-group>

        <description>

        For config the ICW sample application

        </description>

        <display-name>JSPConfiguration</display-name>

        <url-pattern>/jsp/datareset.jsp</url-pattern>

        <el-ignored>true</el-ignored>

        <page-encoding>ISO-8859-1</page-encoding>

        <scripting-invalid>true</scripting-invalid>

        <include-prelude>/jsp/prelude.jspf</include-prelude>

        <include-coda>/jsp/coda.jspf</include-coda>

        </jsp-property-group>

        </jsp-config>

        </web-app>

    2、JSP设置
       在<jsp-config>标记提供了Web程序中JSP程序的设置信息。<jsp-config>包括<taglib>和<jsp-property-group>两类元素。<taglib>定义了Web程序使用的custom tag,它的用法和以前JSP 1.2中的用法相同。<jsp-property-group>定义了一组JSP的特性。这些特性实际上对应JSP的page directive定义的特性。通过<jsp-property-group>只不过可以方便地对多个具有相同属性的JSP统一定义。
        <jsp-property-group>定义一个或多个URL样式,在<jsp-property-group>中定义的属性会适用于匹配这些URL样式的所有JSP文件。在<jsp-property-group>中的属性可以定义以下设置:
        (1)允许或禁止使用表达式语言(EL)
        在<jsp-property-group>中可以设定是否允许对应<url-pattern>的JSP使用JSTL表达式语言(EL)。如果<el-ignored>属性标记被设定为false,JSP中的EL表达式会被处理;如果是true,Web容器会在转换JSP时忽略EL表达式。
        (2)允许或禁止使用scripting
        <scripting-invalid>属性可以允许或禁止使用JSP的脚本语言(scripting)。如果这个属性标记对应为true,即scripting元素被禁止,则JSP中不能使用scriptlet,scripting表达式和declaration,否则会有转换错误。当这个属性标记为false时,JSP可以像在1.2版本之前那样使用脚本语言。
        (3)声明JSP编码
        通过<page-encoding>标记可以设置对应<url-pattern>的JSP网页的编码。这个属性对应每个JSP中的pageEncoding属性,Web容器将根据这个属性对JSP内容进行编码。
        (4)对应隐含包括(Implicit Includes)
        在<jsp-property-group>中可以在对应JSP中加入抬头(preludes)和结尾(coda),使用<include-prelude>和<include-coda>属性可以设定在JSP网页中包括的preludes和coda的jspf文件。这些文件的位置相对于当前Web程序的context。当有超过一个preludes或coda元素在<jsp-property-group>中时,JSP会按照其顺序加入到内容中。
    二)引入表达式语言(EL)

    JSP 2.0的一个主要特点是它支持表达语言(expression language)。JSTL表达式语言可以使用标记格式方便地访问JSP的隐含对象和JavaBeans组件,JSTL的核心标记提供了流程和循环控制功能。自制标记也有自定义函数的功能,因此基本上所有seriptlet能实现的功能都可以由JSP替代。在JSP 2.0中,建议尽量使用EL而使JSP的格式更一致。

    在web.xml的<jsp-property-group>中可以控制一组JSP是否使用EL,在每个JSP中也可以指定是否该JSP使用EL。在page directive中的isELIgnored属性用来指定是否忽略。格式为:

      <%@ page isELIgnored="true|false"%>

    如果设定为真,那么JSP中的表达式被当成字符串处理。比如下面这个表达式<p>${2000 % 20}</p>在isELIgnored="true"时输出为${2000 % 20},而isELIgnored="false"时输出为100。Web容器默认isELIgnored="false"。

     虽然JSP 2.0可以使JSP中完全使用表达语言而避免scriptlet,在实际编程中,应该根据程序的功能要求和编程人员的自身条件选择合适的方式。使用表达语言的JSP比较方便规整,但是由于需要将标记进行转换,在第一次被调用时会比较慢;有些编程人员由于对Java比较了解,因而更习惯JSP 1.2之前的编程方式,因此,在使用中应因地制宜地选择适用的编程方法。   
     
    三)SimpleTag

    JSP 2.0中加入了新的创建自制标记的API,javax.servlet.jsp.tagext.SimpleTag定义了用来实现简单标记的接口。和JSP 1.2中的已有接口不同的是,SimpleTag接口不使用doStartTag()和doEndTag()方法,而提供了一个简单的doTag()方法。这个方法在调用该标记时只被使用一次。而需要在一个自制标记中实现的所有逻辑过程、循环和对标记体的评估等都在这个方法中实现。从这个方面来讲,SimpleTag和IterationTag可以达到同等的作用。但SimpleTag的方法和处理周期要简单得多。在SimpleTag中还有用来设置JSP内容的seUspBody()和getJspBody()方法。Web容器会使用setJspBody()方法定义一个代表JSP内容的JspFragment对象。实现SimpleTag标记的程序可以在doTag方法中根据需要多次调用getJspBody().invoke()方法以处理JSP内容。

     例如,程序例2 SimpleTag根据指定的次数(times)进行循环并输出当前序号(sequence)。程序的结构比较简单,所有逻辑都在doTag方法中实现。

    例2:

      packageICW.taglib;

      importjavax.servlet.jsp.JspException;

      importjavax.servlet.jsp.tagext.SimpleTagSupport;

      importjava.util.HashMap;

      importjava.io.IOException;

      public class IterationSimpleTag extends SimpleTagSupport{

      privateint times;

    public void setTimes(int_times){

        this.times=_times;

            }

        public void doTag() throws JspException,IOException{

        HashMapparams=new HashMap();

        for(inti=0; i<times;i++){

        params.put("sequence",String.valueOf(i+1));

        getJspBody().invoke(null,params);

          }

         }

        }
     
    这个标记的TLD文件内容如下,它使用了XML schcma定义标记的使用方法。

        程序例3如下:

        <?xml version="1.0" encoding="UTF-8" ?>

        <taglibxmlns="http://java.sun.com/xml/ns/i2ee"

        xmlns:xsi="http://WWW.w3.org/2001/XMLSchema-instance"

        xsl:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglihrary_2_0.xsd"

        version="2.0">

      <taglib>

        <tiib-version>1.0</tlib-version>

        <short-name>Jwad book simple tag</short-name>

        <uri>/JwadSimpleTag</uri>

        <description>Simple Tag Handler</description>

        <tag>

        <name>iteration</name>

        <tag-class>ICW.taglib.IterationSimpleTag</tag-class>

        <body-content>scriptless</body-content>

        <description>Iteration Tag</description>

        <variable>

        <description>Current iterationnumber</description>

        <name-given>sequence</name—given>

        </variable>

        <attribute>

        <name>times</name>

        <required>true</required>

        <rtexprvalue>true</rtexprvalue>

        </attribute>

        </tag>

       </taglib>
     

    程序例4中的JSP使用上面例3中定义的IterationSimpleTag,它根据Web请求参数中给定的“times”的值进行一定次数的循环。在每次循环中将输出"sequence"的值。

        例4:

        <%@ taglib prefix="ictag" uri="/WEB-INF/ics-jsp2.tld" %>

        <HTML><HEAD><TITLE>Simple Tag Sample</TITLE></HEAD>

        <BODY>

        <CENTER>

        <FONT COLOR='#009999' SIZE='4' face='Arial'>

        <STRONG>Interation Simple Tag</STRONG>

        </FONT>

        </CENTER>

        <HR>

        <c:setvar="time" value="$"/>

        <p><B>Reminder:</B></p><br>

        <ictag:iteration times="$">

        This is the $ Of $ times of reminder<br>

        </ictag:iteration>

        </body>

        </html>
     
    四)使用JSP fragment

     JSP 2.0中的一个主要功能是JSP fragment,它的基本特点是可以使处理JSP的容器推迟评估JSP标记属性。我们知道一般JSP是首先评估JSP标记的属性,然后在处理JSP标记时使用这些属性,而JSP fragment提供了动态的属性。也就是说,这些属性在JSP处理其标记体时是可以被改变的。JSP需要将这样的属性定义为javax.servlet.jsp.tagext.JspFragment类型。当JSP标记设置成这种形式时,这种标记属性实际上的处理方法类似于标记体。在实现标记的程序中,标记属性可以被反复评估多次。这种用法称为JSP fragment。JSP fragment还可以定义在一个SimpleTag处理程序中使用的自制标记动作。像前面例子说明的,getJspBody返回一个JspFragment对象并可以在doTag方法中多次使用。需要注意的是,使用JSP fragment的JSP只能有一般的文本和JSP action,不能有scriptlet和scriptlet表达式。

      我们可以简单地认为JSP fragment是一段可以重复使用的JSP。一段JSP fragment可以被传递到另一个JSP中并被使用。与JSP include概念不同的是,JSP fragment一般功能比较短小简单而且重复使用率高。

      JSP fragment一般在<jsp:attribute>标记体内或<jspcbody>标记体内定义。每次当含有JSP fragment的标记被使用时,Web容器生成一个JspFragment对象,这个对象和当前JSP的page scope联系起来。同时,这个JspFragment对象和包含它的父标记建立联系。JspFragment对象可以有两种调用方式:使用Java编写的标记处理程序,或者使用标记文件(tag file)。标记文件可以使用<jsp:invoke>,或者<jsp:doBody>动作使用JSP fragment。  JSP标记文件可以和JSP fragment共同工作。CustomTag都是以编程方式实现的。JSP标记文件是用文本文件格式(JSP语法)实现自制标记,这也是JSP 2.0的一个主要新功能。一个标记文件可以对应一个标记,可以不需tld文件定义该标记的方法。这样,即使编程人员不熟悉Java,也可以使用JSP语法定义自己的标记。标记文件一般使用.tag为后缀并放在Web程序的/WEB-INF目录下。

      程序例5中的taskstatus.jsp使用了两个JSP fragment。这个JSP的功能是显示一组Task的名称和完成日期,它通过<jsp:attribute name="...">定义了两段JSPfragment(名称为onSehedule和delayed)。在<jsp:attribute>标记内的JSP就是JSPfragment,而<jsp:attribute>标记被一个<ietag:listTasks>包围。这个标记是一个通过标记文件定义的自制标记,它的定义文件在/WEB-INF/tags目录下。标记文件的名称和标记名称一致为“listTasks.tag"。这个标记会使用到前面定义的两个JSP fragment。
     
     例5:

      <%@ taglib prefix="ictag" tagdir="/WEB-INF/tags" %>

      <HTML><HEAD><TITLE>JSP Fragment Sample</TITLE></HEAD>

      <BODY>

        <CENTER>

        <FONT COLOR='#009999' SIZE='4' face='Arial'>

        <STRONG>JSP Fragment Sample Using Tag Files</STRONG>

        </FONT>

        </CENTER>

        <HR>

        <h2>Tasks</h2>

        <ietag:listTasks>

        <jsp:attribute name="onSchedule">

        <td>

        Name:$<br/>

        </td><td>

        Date:$

        </td>

        </jsp:attribute>

        <jsp:attribute name="delayed">

        <td>

        Name:$<br/>

        </td><td>

        <font color="red">Plan:<strike> $</strike></font><br/>

        <b>Actural:$</b>

        </td>

        </jsp:attribute>

        </ictag:listTasks>

        </BODY>

        </HTML>
     
    五)其他特性

     JSP2.0还有一些其他特性变化,比如严格修正了I18N的语法规则,改进JSP对应XML语法从而允许使用namespaces等。这些并不是核心功能,大家可以参照java.sun.com的有关资料了解功能的细节,这里就不再阐述。

    posted @ 2007-07-02 12:32 和田雨 阅读(213) | 评论 (0)编辑 收藏

    标签库Taglib

    标签被定义和分布在一个称为标签库的结构中,一个标签库是由元信息和类组成的集合:

    1.标签处理器:实现定制标签功能的Java类。

    2.标签附加信息(TEI):向JSP容器提供边辑以确认标签属性和创建变量的类。

    3.标签库描述器(TLD):描述单个标签和整个标签库属性的XML文档。

    标签处理器和标签附加信息需要定位在JSP容器类载入器可以找到的地方。标签库描述器可在URL指定的符意位置。JSP1.1规范要求JSP容器接受一个打包成因定结构的JAR文件的标签库。TLD必须是/META-INF目录中名为taglib.tld的文件,JAR文件则复制到/WEB-INF/lib目录下。

    一、标签实现

    1.开发步骤

    a.定义标签的名字、属性、声明的变量和标签体的内容。

    b.编写标签库描述器TLD。

    c.编写标签处理器。

    d.在JSP页面中使用标签。

    2.JSP页面在JSP容器中的转换步骤:

    JSP页面存在三种形式:jsp文件、java文件和class文件。

    a.指令元素<%@page%>、<%@include%>和<%@taglib%>向JSP容器提供转换时信息。

    b.HTML行在_jspService()方法中依顺序转换到out.print()语名中。

    c.脚本元素的声明被原封不动地复制到_jspService()方法外的源码中。

    d.脚本元素的表达式在_jspService()方法中依顺序转换到out.print()语名中。

    e.脚本元素的Scriptlet被原封不动地复制到_jspService()方法中。

    f.行为元素被转换为执行其功能的运行时逻辑代码。

    g.定制标签被扩展到调用其相应标签处理器中方法的Java语句中。

    3.标签在JSP容器中的转换步骤:

    a.JSP容器使用taglib指令元素定位标签库描述器,将页面中用到的定制标签和TLD相匹配。

    b.读取标签库描述器的标签列表和每一标签相关的类名字。

    c.在页面中遇到一个标签时,查找与具有指定名字的标签前缀相关的一个标签库。

    d.容器使用在TLD中找到的标签结构信息生成一系列完成标签功能的Java语句。

    二、标签库描述器(TLD)

    标签库描述器是一个描述整个标签库标记信息和库中每个标签处理器及其属性的XML文档。

    标签库描述器的DTD由一个简单的元素组成,此元素包含下列一些子元素。

    整个标签库标记信息

    tlibversion标签库版本号。是一个点式十进制数,最多为4组小数点分隔的数字组成。

    jspversion标签库所需的JSP规范最低版本。例如JSP1.1

    shortname标签库的缩写名。JSP可以使用该名字作为库中标签的缺省前缀。

    uri标签库唯一URI的元素。典型URL位置来自可下载taglib的位置。

    info标签库描述信息。

    每个标签处理器及其属性

    tag在TLD中加入标签,描述组成库的每个标签。

    name与标签库的名字前缀一起使用的标签的名字,是JSP容器唯一的标签标识。

    tagclass实现标签的标签处理器类的全名。

    teiclass标签附加信息(TEI)类的全名。TEI类给出关于标签处理器创建变量及对标签司性执行的任意有效性验证的信息。

    bodycontent描述标签处理器如何使用标签体的内容。有三种取值:

    empty:表示标签体必须为空;

    JSP:表示脚本元素和模板及其它标签一样被评估。

    tagdependent:体内容被原封不动写入BodyContent,其它脚本元素以源码形式出现,而不被JSP容器解释。

    info标签的人工可读描述性信息。

    attribute使用标签时被编码的属性信息。用于定义标签的属性。

    属性名:属性的名字。

    true|false:属性在标签用到的位置是否要被编码。

    true|false:属性值能否用表达式指定。

    三、标签处理器

    标签处理器是通过实现JSP容器调用的一系列预定义方法执行定制标签行为的一个Java类。

    标签处理器实现了标签的行为,标签处理器是Java类。

    1.标签处理器的工作方式

    a.导入javax.servlet.jsp和javax.servlet.jsp.tagext包。

    b.实现javax.servlet.jsp.tagext包中的Tag接口或BodyTag接口。BodyTag是Tag的子接口。

    c.继承TagSupport类或BodyTagSuppoert类。它们是上述接口的缺省实现。

    d.重载publicintdoStartTag()throwsJspException方法。

    2.标签处理器的接口与实现

    javax.servlet.jsp.tagext.Tag是实现标签的最基本的接口。

    javax.servlet.jsp.tagext.TagSupport是实现Tag接口的具体类。

    通常情况下继承tagSupport类而不直接实现Tag接口通常是有益的。除了对所有必需方法提供了缺省实现外、还保存了pageContext对象及对嵌套标签的支持。

    Tag接口包含4个常量,表示doStartTag()和doEndTag()方法可能的返回码。

    EVAL_BODY_INCLUDE当doStartTag()返回时,指明servlet应对标签体进行评估。

    SKIP_BODY当doStartTag()返回时,指明servlet应忽视标签体。

    EVAL_PAGE当doEndTag()返回时,指明页面其余部分应被评估。

    SKIP_PAGE当doEndTag()返回时,指明页面其余部分就被跳过。

    Tag接口的方法

    publicvoidsetPageContext(PageContextctx)生成的servlet在请求处理器执行其它任务前首先调用此方法,实现类应保存上下文对象以便它可以在标签生命期中使用。从页面上下文中标签处理器可以访问所有JSP隐含对象。

    publicvoidsetParent(Tagp)使用一个标答可以找到操作栈中它上面的标签。在setPageContext后立即调用。

    publicTaggetParent()返回父标签。

    publicintdoStartTag()throwsJsp在设置了页面上下文、父标签和开始标记中编码的属性后调用。返回码表明JSP实现servlet是否就评估标签体。

    publicintdoEndTag()throwsJspException当遇到结否标记时调用。返回码表明JSP是否就继纽页面的其余部份。

    publicvoidrelease()确保在页面退出前被调用。释放资源并重置标签处理器状态。

    TagSupport类的方法

    publicstaticTagfinAncestorWithClass(TagthisTag,Classcls)为所需的父标签处理器查找运行时标签栈。一个标签处理器可以提供其范围内子标签调用的方法。

    publicvoidsetId(Stringid)保存和检索在id属性中指定的名字。

    publicvoidsetValue(Stringname,Objecto)在本地哈希表中设置指定名字的值。

    publicObjectgetValue(Stringname)从本地哈希表中获取指定名称的值。

    publicvoidremoveValue(Stringname)从本地哈希表中删除指定名称的值。

    publicEnumerationgetValues()返回哈希表中关键字的一个枚举。

    3.标签处理器的生命期

    a.生成servlet需要创建标签处理器类的一个实例。实现方式通常是调用JSP容器的工厂类的一个方法,工厂类包含一个标签处理器实例池以使其可重用不再处于激活状态的对象。

    b.初始化标签处理器,使servlet获知其存在性。servlet通过调用标签处理器的两个方法实现此过程:setPageContext(PageContextctx)和setParent(Tagparent)。

    c.如果标签具有属性,属性的取值通过处理器提供setter方法传入到对象。属性setter方法是一个标签支持属性所需的唯一方法。

    d.页面的上下文和父标签已被调置,并已具备属性。此时调用标签处理器的doStartTag()方法,该方法可以读取这些变量并执行实现标答功能所需的计算和操作。doStartTag()方法必须返回一个整型数。返回EVAL_BODY_INCLUDE则正常处理标签体,返回SKIP_BODY则从初始JSP页面中直到此标签结束标记处的内容均被忽略。

    e.标签体被评估或忽视后调用标签处理器的doEndTag()方法,返回EVAL_PAGE则页面的其余部分被评估,返回SKIP_PAGE则servlet代码立即从_jspService()中返回。

    4.体标签处理器的接口与实现

    javax.servlet.jsp.tagext.BodyTag是Tag的子接口。

    javax.servlet.jsp.tagext.BodyTagSupport是实现BodyTag类。

    BodyContent是javax.servlet.jsp.JspWriter的子类,但与其父类有所区别。

    BodyContent对象的内容不自动写了入servlet的输出流,而是积累在一字符串缓存中。当标签体完成后其对象仍可在doEndTag()方法中可以应用,由getString()或getReader()方法操作。并在必要时修改及写入恢复的JspWriter输出流。

    BodyContent类的方法

    publicvoidflush()throwsIOException复写JspWrite.flush()方法以便它总是产生溢出。刷新写入已失效,因为它没有连接到将被写入的实际输出流中。

    publicvoidclearBody()重置BodyContent缓存为空。

    publicReadergetReader()返回Reader读取体内容。

    publicStringgetString()返回包含体内容的一个字符串。

    publicvoidwriteOut(Writew)将体内容写入指定输出。

    publicJspWritegetEnclosingWrite()返回栈中下一个更高的写入者对象(可能是另一个BodyContent对象)。

    BodyTag接口定义了一个新的整型常量

    EVAL_BODY_TAG当doStartTag()返回时,使得新的BodyContent对象被创建并与此标签处理器相关联。当doAfterBody()返回时,使得JSPservlet在修改完此标签控制的任意变量后再次评估体。

    BodyTag接口的方法

    publicvoidsetBodyContern(BodyContentout)在当前JspWriter已被写入,一个新的BodyContent在被创建后由Jspservlet调用,它发生在doStartTag()之后。

    publicvoiddoInitBody()throwsJspExceptionsetBodyContent()之后,体被评估前调用的生命期方法。如果多次评估体,此方法只调用一次。

    publicinitdoAfterBody()throwsJspException体被评估后,BodyContent写入者仍处于激活状态时调用的生命期方法。此方法必须返回EVAL_BODY_TAG或SKIP_BODY,若返回EVAL_BODY_TAG时体再次被评估。

    BodyTagSupport类的方法

    publicintdoStartTag()throwsJspException复写TagSupport中的doStartTag()方法。

    publicintdoEndTag()throwsJspException调用TagSupport中的doEndTag()方法,返回结果。

    publicvoidsetBodyContent(BodyContentout)在一保护成员变量bodyContent中保存新的体内容对象,子类可直接访问此对象。

    publicvoiddoInitBody()throwsJspException缺省什么都不做。被需要执行初始化的子类所复写。

    publicintdoAfterBody()throwsJspException每次体被评估后由JSPservlet调用,体同容对象仍处于激活状态。返回SKEP_BODY或EVAL_BODY_TAG则体再次被评估

    publicvoidrelease()设置bodyContent对象为null,然后调用super.release()。

    publicBodyContentgetBodyContent()返回bodyContent变量。子类已经可以访问保护变量,但此方法允许无关的标签处理类对此体内容发送输出。

    publicJspWritergetPreviousOut()在bodyContent变量上调用getEnclosingWriter()并返回结果的简便方法。

    5.体标签处理器的生命期

    a.生成servlet需要创建标签处理器类的一个实例。实现方式通常是调用JSP容器的工厂类的一个方法,工厂类包含一个标签处理器实例池以使其可重用不再处于激活状态的对象。

    b.初始化标签处理器,使servlet获知其存在性。servlet通过调用标签处理器的两个方法实现此过程:setPageContext(PageContextctx)和setParent(Tagparent)。

    c.如果标签具有属性,属性的取值通过处理器提供setter方法传入到对象。属性setter方法是一个标签支持属性所需的唯一方法。

    d.页面的上下文和父标签已被调置,并已具备属性。调用标签处理器的doStartTag()方法,该方法可以读取这些变量并执行实现标答功能所需的计算和操作。

    doStartTag()方法必须返回一个整型数。

    返回EVAL_BODY_TAG则正常处理标签体(跳到e);

    返回SKIP_BODY则从初始JSP页面中直到此标签结束标记处的内容均被忽略。(跳到f)

    e.如果返回EVAL_BODY_TAG时,则正常处理标签体。

    e1.在栈中保存当前的JspWriter对象,创建新的BodyContent对象,并将其置为JSP页面的out对象保存在上下文范围内名为name的属性中。并调用它的setBodyContent()方法。

    e2.调用doInitBody()方法进行初始化。

    e3.处理标签体。将输出写入BodyContent对象中,此过程依赖于TLD的标签元素 ,有三种可能取值。

    e4.调用doAfterBody()方法,将体内体内容写入JspWriter,可如下实现:

    JspWriterout=bodyContent.getEnclosingWriter();

    out.println(bodyContent.getString());//bodyContent.writeOut(out);

    bodyContent.clear();

    e5.doAfterBody()方法返回两种可能:

    返回EVAL_BODY_TAG时,再对标签体进行评估,这是数组和枚举被循环处理的典型情况。

    返回SKIP_PAGE时,继续页面的其余部份。

    e6.体内容完成,因此创建它的过程被反向:

    调用pageContent.popBody()方法检索前面的JspWriter对象。

    将写入者设置回out隐含对象。

    f.标签体被评估或忽视后调用doEndTag()方法,允许标签处理器像输出流发回内容。

    返回EVAL_PAGE则页面的其余部分被评估;

    返回SKIP_PAGE则servlet代码立即从_jspService()中返回。

    g.此时体的内容在受保护的bodyContent对象中仍然可用。

    可以将它写入servlet输出流中:

    JspWriterout=pageContext.getOut();

    out.println(bodyContent.getString());

    或者

    bodyContent.WriteOut(pageContext.getOut());

    6.标签附加信息类

    四、标签指令

    taglib指令元素的目的是指定TLD的位置,设置在页面上与标签区分开来的一个短别名。

    语法:<%@taglibprefix=”tagprefix”uri=”tagliburi”%>

    属性:prefix:用于标识标签库的唯一标识。uri:标签库本身的URI。

    uri不必指向一个实际文件,它是JSP容器可以在web.xml中查找实际文件位置的唯一标识符。

    posted @ 2007-07-02 12:29 和田雨 阅读(195) | 评论 (0)编辑 收藏

    Servlet API 很久以前就已成为企业应用开发的基石,而 Servlet 过滤器则是对 J2EE 家族的相对较新的补充。本文将向您介绍 Servlet 过滤器体系结构,定义过滤器的许多应用,并指导您完成典型过滤器实现的三个步骤。同时本文还会透露bean 的一些激动人心的变化,预计刚发布的 Java Servlet 2.4规范会引入这些变化。

      Servlet 过滤器是可插入的 Web 组件,它允许我们实现 Web 应用程序中的预处理和后期处理逻辑。过滤器支持 servlet 和 JSP 页面的基本请求处理功能,比如日志记录、性能、安全、会话处理、XSLT 转换,等等。 过滤器最初是随 Java Servlet 2.3 规范发布的,最近定稿的 2.4 规范对它进行了重大升级。在此我将向您介绍 Servlet 过滤器的基础知识 ―― 比如总体的体系结构设计、实现细节,以及在 J2EE Web 应用程序中的典型应用,还会涉及一些预计最新的 Servlet 规范将会提供的扩展功能。

      Servlet 过滤器是什么?

      Servlet 过滤器是小型的 Web 组件,它们拦截请求和响应,以便查看、提取或以某种方式操作正在客户机和服务器之间交换的数据。过滤器是通常封装了一些功能的 Web 组件,这些功能虽然很重要,但是对于处理客户机请求或发送响应来说不是决定性的。典型的例子包括记录关于请求和响应的数据、处理安全协议、管理会话属性,等等。过滤器提供一种面向对象的模块化机制,用以将公共任务封装到可插入的组件中,这些组件通过一个配置文件来声明,并动态地处理。

      Servlet 过滤器中结合了许多元素,从而使得过滤器成为独特、强大和模块化的 Web 组件。也就是说,Servlet 过滤器是:

    • 声明式的:过滤器通过 Web 部署描述符(web.xml)中的 XML 标签来声明。这样允许添加和删除过滤器,而无需改动任何应用程序代码或 JSP 页面。
    • 动态的:过滤器在运行时由 Servlet 容器调用来拦截和处理请求和响应。
    • 灵活的:过滤器在 Web 处理环境中的应用很广泛,涵盖诸如日志记录和安全等许多最公共的辅助任务。过滤器还是灵活的,因为它们可用于对来自客户机的直接调用执行预处理和后期处理,以及处理在防火墙之后的 Web 组件之间调度的请求。最后,可以将过滤器链接起来以提供必需的功能。
    • 模块化的:通过把应用程序处理逻辑封装到单个类文件中,过滤器从而定义了可容易地从请求/响应链中添加或删除的模块化单元。
    • 可移植的:与 Java 平台的其他许多方面一样,Servlet 过滤器是跨平台和跨容器可移植的,从而进一步支持了 Servler 过滤器的模块化和可重用本质。
    • 可重用的:归功于过滤器实现类的模块化设计,以及声明式的过滤器配置方式,过滤器可以容易地跨越不同的项目和应用程序使用。
    • 透明的:在请求/响应链中包括过滤器,这种设计是为了补充(而不是以任何方式替代)servlet 或 JSP 页面提供的核心处理。因而,过滤器可以根据需要添加或删除,而不会破坏 servlet 或 JSP 页面。

      所以 Servlet 过滤器是通过一个配置文件来灵活声明的模块化可重用组件。过滤器动态地处理传入的请求和传出的响应,并且无需修改应用程序代码就可以透明地添加或删除它们。最后,过滤器独立于任何平台或者 Servlet 容器,从而允许将它们容易地部署到任何相容的 J2EE 环境中。

      在接下来的几小节中,我们将进一步考察 Servlet 过滤器机制的总体设计,以及实现、配置和部署过滤器所涉及的步骤。我们还将探讨 Servlet 过滤器的一些实际应用,最后简要考察一下模型-视图-控制器(MVC)体系结构中包含的 Servlet 过滤器,从而结束本文的讨论。

      Servlet 过滤器体系结构

      正如其名称所暗示的, Servlet 过滤器用于拦截传入的请求和/或传出的响应,并监视、修改或以某种方式处理正在通过的数据流。过滤器是自包含、模块化的组件,可以将它们添加到请求/响应链中,或者在无需影响应用程序中其他 Web 组件的情况下删除它们。过滤器仅只是改动请求和响应的运行时处理,因而不应该将它们直接嵌入 Web 应用程序框架,除非是通过 Servlet API 中良好定义的标准接口来实现。

      Web 资源可以配置为没有过滤器与之关联(这是默认情况)、与单个过滤器关联(这是典型情况),甚至是与一个过滤器链相关联。那么过滤器究竟做什么呢? 像 servlet 一样,它接受请求并响应对象。然后过滤器会检查请求对象,并决定将该请求转发给链中的下一个组件,或者中止该请求并直接向客户机发回一个响应。如果请求被转发了,它将被传递给链中的下一个资源(另一个过滤器、servlet 或 JSP 页面)。在这个请求设法通过过滤器链并被服务器处理之后,一个响应将以相反的顺序通过该链发送回去。这样就给每个过滤器都提供了根据需要处理响应对象的机会。

      当过滤器在 Servlet 2.3 规范中首次引入时,它们只能过滤 Web 客户机和客户机所访问的指定 Web 资源之间的内容。如果该资源然后将请求调度给其他 Web 资源,那就不能向幕后委托的任何请求应用过滤器。2.4 规范消除了这个限制。Servlet 过滤器现在可以应用于 J2EE Web 环境中存在请求和响应对象的任何地方。因此,Servlet 过滤器可以应用在客户机和 servlet 之间、servlet 和 servlet 或 JSP 页面之间,以及所包括的每个 JSP 页面之间。这才是我所称的强大能力和灵活性!

    posted @ 2007-07-02 12:28 和田雨 阅读(188) | 评论 (0)编辑 收藏

    常用的设定,详细请看:http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd
    XML声明,定义XML的版本、编码格式。还有最重要的schema的来源
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
      .......
    </web-app>
     
    <description>
    当servlet的URL定义为其他文件类型的扩展名,该文件类型将不能访问,而访问了servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
      <display-name>myweb</display-name><!--站点名称-->
      <description>这里是站点描述</description>
      <icon>
        <small-icon>/images/small.gif</small-icon><!--小图标的路径16*16,必须为gif jpge格式-->
        <large-icon>/images/large.gif</large-icon><!--大图标的路径32*32,必须为gif jpge格式-->
      </icon>
      <!--如果存在<distributable/>则代表该站点能在多个JSP Container之间分散执行(分布式)-->
      <distributable/>




      <context-param><!--环境参数,设置常量,取得常量  this.getInitParameter("context");-->
        <param-name>context</param-name>
        <param-value>10000</param-value>
      </context-param>
      <filter><!--声明filter-->

        <filter-name>filterName</filter-name>
        <filter-class>filter.filterName</filter-class>
        <init-param>
          <param-name>encoding</param-name>
          <param-value>UTF-8</param-value>
        </init-param>
      </filter>
      <filter-mapping><!--定义filter所对应的URL-->
      <filter-name>filterName</filter-name>
      <url-pattern>/filterName</url-pattern>
      <dispatcher>REQUEST|INCLUDE|FORWARD|ERROR</dispatcher><!--filter对应的请求方式,有4种方式,默认REQUEST-->
      </filter-mapping>
      <listener><!--设定Listener接口-->
        <listener-class>listener.ListenerClassName</listener-class>
      </listener>
      <servlet><!--声明servlet的数据-->
      <servlet-name>servletName</servlet-name>
      <servlet-class>servlet.servletName</servlet-class>
      <init-param><!--初始化参数,可以在init()中使用ServletConfig().getInitParamenter("name")取得-->
        <param-name>name</param-name>
        <param-value>123</param-value>
      </init-param>
      <load-on-startup>n</load-on-startup><!--站点启动时,此servlet会被自动加载执行,1表示最早,2,3...-->

      </servlet>
      <servlet-mapping><!--定义servlet所对应的URL-->

        <servlet-name>name</servlet-name>
        <url-pattern>/name</url-pattern>
      </servlet-mapping>
      <session-config><!--设置session超时时间(20分钟)默认按服务器配置-->
        <session-timeout>20</session-timeout>
      </session-config>
      <mime-mapping><!--定义某个扩展名和某个MIME Type做映射-->
        <extension>doc</extension>
        <mime-type>application/vnd.ms-word</mime-type>
      </mime-mapping>
      <welcome-file-list><!--设置首页列表-->
      <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
      <error-page><!--将错误代码对应到WEB站点的资源路径-->
      <error-code>错误代码</error-code>Exception
      <location>/路径/</location>

      </error-page>
      <error-page><!--将异常的种类对应到WEB站点的资源路径-->
      <exception-type>Exception</exception-type>
      <location>/路径/</location>

      </error-page>

      <jsp-config><!--JSP的相关配置-->
        <taglib><!--JSP所用到的Tag Library-->
          <taglib-uri>URI</taglib-uri>
          <taglib-location>/WEB-INF/lib/xxx.tld</taglib-location>
        </taglib>
        <jsp-property-group>
          <description>此设定的说明</description>
          <display-name>Name</display-name>
          <url-pattern>URL</url-pattern><!--此设定影响的范围-->
          <el-ignored>true|false</el-ignored><!--true表示不支持EL语法-->
          <scripting-invalid>encoding</scripting-invalid><!--jsp的编码-->
          <include-coda>...jspf</include-coda><!--JSP的结尾,扩展名.jspf-->
        </jsp-property-group>
      </jsp-config>
      <resource-ref><!--利用JNDI取得站点可利用的资源-->
        <description>资源说明</description>
        <res-ref-name>资源名称</res-ref-name>
        <res-type>资源种类</res-type>
        <res-auth>Application|Container</res-auth><!--资源经由什么许可-->
        <res-sharing-scope>Shareable|Unshareable</res-sharing-scope><!--资源是否可以共享,默认为Shareable-->
      </resource-ref>
    </web-app>
    posted @ 2007-07-02 12:22 和田雨 阅读(4966) | 评论 (1)编辑 收藏

    2005年9月26日,Sun公司和JSR154的专家组发布Servlet API的一个新的版本。在一般情况下,一个JSR的新版本仅仅包括对以前少数有名无实的规范进行去除更新。但这次,新版本中增加新的特征和变化,他们对Servlets的产生重要影响,使得Servlet的版本升到了 2.5。
    在这篇文章里,我主要谈谈Servlet2.5版本中的新特征。描述每一个变化,阐述那些必要变化产生的背景,并展示如何在基于Servlet的项目中利用这些变化。 
    事实上,这是我为JavaWorld提供的第六篇关于Servlet API更新资料的文章。这篇文章意在两个目的:从眼前来看,向你介绍 Servlet的新特征。从长远来看,是展现Servlet变化的历史概要,这样当你基于老的Servlet API版本进行编码的时候,你可以正确地决定哪些特征和功能你可以使用,而哪些特征和功能你不应该使用。你可以参考我先前写的关于Servlet的文章。
    注意:当你想实践这些Servlet的新特征和功能时,你要知道的是:并不是所有的Servlet容器和Java企业级应用服务器都能立即适用于最新版的Servlet API,写这篇文章时(2006年1月2日),Jetty 6 server和Sun公司的GlassFish server是公认最好的支持Servlet2.5的容器,而Apache Tomcat5.5和Jboss 4.0目前只支持Servlet2.4。
    Servlet2.5一些变化的介绍:
    1)    基于最新的J2SE 5.0开发的。
    2)    支持注释。
    3)    web.xml中的几处配置更加方便。
    4)    去除了少数的限制。
    5)    优化了一些实例
    J2SE 5.0的产物:
    从一开始,Servlet 2.5 规范就列出J2SE 5.0 (JDK 1.5) 作为它最小的平台要求。它使得Servlet2.5只能适用基于J2SE 5.0开发的平台,这个变动意味着所有J2SE5.0的新特性可以保证对Servlet2.5程序员有用。
    传统意义上,Servlet和JEE版本一直与JDK的版本保持同步发展,但是这次,Servlet的版本跳过1.4版本。专家组认为版本的加速增长是正当的,因为J2SE5.0提出一个引人注目的,Servlet和JEE规范都要利用的特征??注释。
    注释:
    注释是作为JSR175的一部分提出的(一种为Java语言设计提供便利的Metadata)一种新的语言特色。它是利用Metadata为Java 编码结构(类,方法,域等等)装饰的一种机制。它不能像代码那样执行,但是可以用于标记代码,这个过程是基于Metadata信息的代码处理机,通过更新他们的事件行为来实现的。

    我们可以凭借不同的技巧来注释类和方法,例如连续地标记接口或者是@deprecated Javadoc评论。这种新式的Metadata可以便利地提供了一种标准的机制来实现注释功能,以及通过库来创建用户自己的注释类型的变量。
    下面是一个简单的Web service 注释例子:
    import javax.jws.WebService;
    import javax.jws.WebMethod;
    @WebService
    public class HelloWorldService {
      @WebMethod
      public String helloWorld() {
        return "Hello World!";
      }
    }
    @WebService和@WebMethod这两个注释类型,在JSR181(为Java平台提供的Web ServicesMetadata)有详细说明,可以像类一样的引用,标记这个类作为一个Web service并且标记它的helloWorld()方法做为一个Web service方法。对于他们本身来说,注释只是写在那里并没有什么作用,好像在岗位上做记录一样,但是,一个容器一旦加载这个类并对那些注释进行二进制编码,就可以把这个类连到Web service上。
    注释可以接受属性/值这些参数。它保存着参数的信息并且可以利用这些参数来更改被请求的事件行为。例如下面更高级的注释例子:
    @WebService(
     name = "PingService",
      targetNamespace=http://acme.com/ping
    )
    @SOAPBinding(
      style=SOAPBinding.Style.RPC,
      use=SOAPBinding.Use.LITERAL
    )
    public class Ping {
      @WebMethod(operationName = "Foo")
      public void foo() { }
    }
    一旦加载了这个类,一个正确配置的容器就会识别出注释及其参数,并将这个做为一个PingService通过利用remote-procedure- call/literal的编码方式与一个Foo operation相连。实际上,注释便指明了类和类的容器之间的联系。

    Java本身的规范(JSR175)仅仅有少量的注释类型变量。而这些有趣的注释类型变量主要来自于其他的JSRs:
    "JSR 250: Java平台的公共注释
    "JSR 220: 企业级JavaBeans 3.0 
    "JSR 224: 基于XML的Java API Web Services (JAX-WS) 2.0 
    "JSR 181: Java平台的Web Services Metadata 
    Servlet2.5中的注释:
    回到Servlet2.5上来,一种新的规范描述了几种注释在Servlet环境中是如何工作的。功能弱的Servlet容器忽略了这些规范,然而JEE容器中的Servlet却严格遵守这些规范。

    有的注释提供了在XML注册的可选择性,否则就要注册在配置文件web.xml中。有的作为容器的请求来执行其任务,否则就由Servlet亲自来执行。还有的注释两者都具备。

    注释准确的定义不是完全固定的,因为Servlet本身并没有定义注释。它仅仅解释了它们如何影响Servlet环境,下面是注释的一个简要的概述,你可以看到在JEE5中它们的用途:

    "@Resource and @Resources:@Resource位于类或变量中以对Servlet容器进行“资源注入”。当容器识别出这个注释时,它会在获得服务地位之前,用适当的值实现带注释的变量的重新注入。通过使用这种注释,你不必利用JNDI来查找命令和在配置文件web.xml中手动声明资源。服务器通过Servlet的自我调整来执行它的任务。变量的名称和类型由映像机制自动确定,尽管你可以利用注释的参数来超越这一限制。一个注入的资源可以是数据源,Java信息服务目的文件或者是环境设置的标量。下面是一个例子:
    @Resource javax.sql.DataSource catalog;
    public getData() {
      Connection con = catalog.getConnection();
    }
    现在,在这段Servlet代码变成服务之前,容器会定位JNDI变量,并对于目录变量进行手动分配。
    为了效率,仅仅某些类支持资源注入,这些类有:Servlets,Servlet过滤器,Servlet事件监听器,JSP标签操作器,JSP库事件监听器,用于处理beans的JSF,以及一些与Serlvets无关的类。
    "@Resources注释与@Resource相似,但是它用于一组@Resource注释。它们都来自JSR250,是Java平台的公共注释。
    "@PostConstruct and @PreDestroy:可以使方法成为带有生命周期的方法。@PostConstruct方法用于资源注入初始化之后。@PreDestroy方法用于Servlet脱离服务并释放注入的资源的时候。回收的方法必须是事实的方法,返回void并且不可以抛出任何异常。这些注释本质上使得任何方法都成为init()和destroy()的子方法,这些特征也来自与JSR250。
    "@EJB:类似于@Resource,设计用于注入企业级的JavaBeans。比起@Resource,它略有不同,在于@EJB的参数特定设计用来定位EJB的参数。这个注释来自EJB3.0的规范。
    "@WebServiceRef:与@Resource 和 @EJB相似,设计用于注入Web service参数。来自于JAX-WS2.0规范。
    "@PersistenceContext, @PersistenceContexts, @PersistenceUnit, and @PersistenceUnits:这些注释来自EJB3.0规范来支持Java对象的持久化。
    "@DeclareRoles: 定义应用程序中安全角色的使用。当定义一个Servlet类时,在配置文件web.xml中<security-role>标签中对它进行设置,来自JSR250。
    " @RunAs:用于声明哪个类应该执行。当定义一个Servlet类时,在配置文件web.xml中<run-as>标签中对它进行设置。来自于JSR250。

    注释的执行:
    不论你使用注释与否??尤其在你不使用时??它对于理解服务器上程序的执行有着重要意义。为了让服务器识别类中的注释,它必须加载这些类,这就意味着服务器必须是启动着的,服务器通过WEB-INF/classes目录下和WEB-INF/lib目录下的所有类文件来查找注释。(每个规范下,服务器不必查找这两个目录以外的目录。)你可以通过下面的方法指明<web-app>根的属性而不必使用如何进行注释:
    <web-app xmlns="http://java.sun.com/xml/ns/javaee"
                 version="2.5" full="true">
    </web-app>
    web.xml的便利:
    Servlet2.5对于web.xml引入几个小的变动,使得它更加方便。
    Servlet名称的通配符化:
    首先,当你写<filter-mapping>,你现在可以在<Servlet-name>标签中使用*号来代表所有的Servlets。而以前,你必须一次把一个Servlet绑定到过滤器上,像这样:

    <filter-mapping>
      <filter-name>Image Filter</filter-name>
      <Servlet-name>ImageServlet</Servlet-name>
    </filter-mapping> 

    现在,你可以一次绑定所有的Servlets:

    <filter-mapping>
      <filter-name>Image Filter</filter-name>
      <Servlet-name>*</Servlet-name>  <!?新特征 -->
    </filter-mapping>

    这有着很大用途,例如:

    <filter-mapping>
      <filter-name>Dispatch Filter</filter-name>
      <Servlet-name>*</Servlet-name>
      <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

    映射的复合模式:
    其次,当我们写<Servlet-mapping> 或者 <filter-mapping>时,你现在可以在同一的标签中采用复合匹配的标准。以前一个<Servlet-mapping>只支持一个<url-pattern>元素,现在它不只支持一个,例如:
    <Servlet-mapping>
      <Servlet-name>color</Servlet-name>
      <url-pattern>/color/*</url-pattern>
      <url-pattern>/colour/*</url-pattern>
    </Servlet-mapping>

    同样地,以前<filter-mapping>也是只支持一个<url-pattern> 或者一个 <Servlet-name>。现在它对于每个元素都可以支持任意多个:

    <filter-mapping>
      <filter-name>Multipe Mappings Filter</filter-name>
      <url-pattern>/foo/*</url-pattern>
      <Servlet-name>Servlet1</Servlet-name>
      <Servlet-name>Servlet2</Servlet-name>
      <url-pattern>/bar/*</url-pattern>
    </filter-mapping>

    HTTP方法名:

    最近,你可以将合法的HTTP/1.1方法名放进<http-method>元素中。当你使用这些方法时,<http- method>将指明<security-constraint>标记里的方法应该被应用。从以前来看,它仅限于HTTP/1.1的7 个标准方法:GET,POST,PUT,DELETE,HEAD,OPTIONS和TRACE。但是,HTTP/1.1允许对方法进行扩展,WebDAV 是用于这种扩展的普遍技术。在Servlet2.5中,你可以安全地约束任何可能的HTTP方法名,标准及扩展,包括WebDAV方法,例如:LOCK, UNLOCK,COPY及MOVE。
    如果你写一个WebDAV的Servlet,你不必使用doLock()和doCopy()方法。你必须写自己的service()方法及分派request.getMethod()方法。正由于这种变化,你不必管理系统的安全性。
    去除限制:

    Servlet2.5去除了关于错误处理和回话跟踪的一些限制。对于错误处理,Servlet2.5之前,配置在<error- page>中的错误处理页面不能通过调用setStatus()方法来修改触发他们的错误代码,而Servlet2.5减弱了这一规范。这样的规范的产生于这样的观点,就是错误页面的工作是指出每个错误而不是修改错误。但是,实际使用中,错误页面不只是用于指出错误,而是还能做更多的事情,或许可以代替在线帮助来帮助用户解决问题。这个规范将不再限制错误页面所产生的反馈信息。

    对于会话跟踪,Servlet2.5之前,调用RequestDispatcher.include()的Servlet不能设置响应的标题头,而 Servlet2.5减弱了这一规范。原规范的目的是使内部的Servlets限制在自己的页面空间中,不可以影响外部的页面。现在这个规范已经减弱,允许在内部的Servlet中使用request.getSession()命令,这个命令可以悄悄地创建一个会话跟踪cookie的标题头。逻辑上要求限制内部的资源,但逻辑上也要求这些限制不应该取消其启动session的这一功能。这个变动对于Portlet规范来说显得尤其重要。作用是:如果响应已经有效,则getSession()命令就会抛出一个IllegalStateException(异常),而在此之前,就没有这个功能。

    优化:
    最近,新的规范优化了一些实例,使得Servlets更加方便而且保证了更好地按要求工作。

    终止响应:
    第一处优化细小又深奥,但做为规范中的一个例子还有蛮有趣的。Servlet2.4规范规定响应在这几种情况下应该是有效的,包括:在响应的 setContentLength方法中内容已经明确说明,以及内容已经写进了响应中。这种情况只有你的代码像下面这样才可以使响应重新定向:

    response.setHeader("Host", "localhost");
    response.setHeader("Pragma", "no-cache");
    response.setHeader("Content-Length", "0");
    response.setHeader("Location", "http://www.apache.org");

    Servlet技术忽略特定区域的标题头,因为内容满足0字节长度,响应就会立即生效。而在它开始之前,响应就已失效了!Servlet容器通常拒绝执行这种行为,而Servlet2.5版本增加了“长度必须大于0”这个原则。
    实例编码:

    Servlet2.4规范规定必须在调用request.getReader()方法之前调用 request.setCharacterEncoding()方法。但是,如果你忽略这个原则而在其之后去调用 request.setCharacterEncoding()方法,那么会产生什么后果,这个问题规范里并没有说。为了简便,现在消除这种情况!
    Cross-context sessions(不同上下文目录间的会话):
    最近,关于Cross-context会话处理的规则已经明确说明。当Servlets指派从一个上下文到其他上下文的请求时,这个规则就发挥了作用??在目标调用过程中,包括哪些会话。这个版本的出现使得一个上下文目录的主页里的portlets可以通过几种内部的命令来对别的上下文目录里的 portlets起作用。Servlet2.5明确指出一个上下文目录里的资源可以访问其他上下文目录的session(会话),而不用考虑这个请求从哪里开始的。这意味着portlets可以脱离主页的范围而在自己的范围里运行,而且这个规范还会应用在不兼容的Serlvet容器中。
    期待:

    由于Servlet2.5版本要保持一些旧的性质,几个大的概念不得不延后到下一个阶段。它们包括:
    "新的输入/输出(NIO)支持:使NIO通道更有利于Servlets进行客户端通信成为可能。
    "过滤器wrap-under或wrap-over语义:有时用过滤器包装请求,和/或者响应对象去修改方法行为或者启用新的方法。当把这种包装和服务器对请求和响应的包装结合起来时,又应该怎么包装在一起?
    "用于欢迎的Servlets文件:做为索引应该充当欢迎作用的文件吗?在此之前,这个回答是肯定的。但是规范没有明确说明如何使用这个功能,尤其在没有索引的情况下。
    "用于欢迎的文件的分派规则:如何分派欢迎文件,这个细节并没有完全说明,而是遗留了一些开放的缺口来应对不兼容问题。
    "登陆后选择默认页面:如果用户通过他们的书签访问Servlet的登陆页面,那么在成功登陆后页面应该转向哪里呢?这个问题至今尚未明确说明。
    "用户的主题日志:在通过网站正确地注册之后,不通过传统地登陆方式没有办法使Servlet信任用户。
    结束语:
    如果抛开注释来看Servlet2.5的变化,可见在配置文件web.xml中去除了一些限制,是有利的,同时又优化了实例行为使其更适合更便于开发Web系统(网页)。
    Servlet2.5中注释的作用更加戏剧化。Servlets本身不能声明注释类型的变量,甚至性能弱的Servlet容器都不支持注释。然而在 JEE5环境下的Servlets编写者可以看到,通过公共的注释及EJB3.0和JAX-WS2.0规范而引入的注释类型会对代码产生很大变化,并且这也将对Servlet如何管理外部资源,对象的持久化及EJB的构成产生重大影响

    J2EE的两种重要的表现层技术JSP和JSF发布了新技术规范的预览版本,其中最重要的一点是两者将表达式语言(Expression Language,EL)部分合二为一。在不久的将来,这两种技术有可能更进一步地彼此融合,成为一种统一的表现层技术。然而在J2EE社群的普遍观点中,如果单单作为一种视图技术,JSP并不是最佳的选择,Velocity和XSLT等基于模板的视图技术通常比JSP更方便;而基于组件的JSF也面临广泛的信任危机。两者的组合是否能得到业界的认可,还需要时间的检验。

    jsp 2.1


        我们很高兴向大家宣告,JavaServer Pages、JSR-245下开发的Faces.JavaServer Pages(JSP)2.1和JSR-252下开发的JavaServer Faces(Faces)1.2的新版规范的Early Draft Review发布。

        JSP 2.1把Expression Language(EL)输出到它自己各自分离的文档中,在技术上,这些文档是JSP规范的子文档。这些统一的EL规范定义了一个更高层的java 包,javax.el。这个包与使用它的技术之间完全独立,并且允许此技术将自身插入EL处理过程。更改的JSP规范遵从使用标准化EL的规范。

        对于前面提到的JSR-252,这个规范并没什么新特性。Faces 1.2支持新的标准化EL,还包含一些bug修复的相关规范。

        Faces和JSP在JSRs下的结盟带来了一些新功能,也为将来的发展打下了坚实的基础。例如,在同时使用Faces和JSP的web应用中,网页仅使用JSP(不包含任何faces内容)来访问Managed Beans成为可能。在JSP规范的附录E中和Faces规范的前言中都可以看到更改内容的细节。

    posted @ 2007-07-02 12:20 和田雨 阅读(2294) | 评论 (0)编辑 收藏

    仅列出标题
    共9页: 上一页 1 2 3 4 5 6 7 8 9 下一页