|
|
Sun Java System Application Server 7 Web应用开发者指南 使用servlet 这部分将介绍如何通过创建高效的servlet(包括标准servlet)来控制运行在 Sun Java System Application Server 上的应用的交互。另外,这部分还介绍了Sun Java System Application Server用于对标准进行扩充的一些特性。 本模块包括以下几节: servlet与applet一样,是一种可再用的Java应用。不过,servlet需要运行在应用服务器或Web服务器上,而不能直接在Web浏览器中运行。 Sun Java System Aplication Server所支持的servlet是基于v2.3版Java Servlet 规范。所有相关的规范都可以通过install_dir/docs/index.htm访问到,其中install_dir是指Sun Java System Application Server的安装路径。 servlet可用于应用的表现逻辑。servlet充当的是应用的中心调度器的角色,它负责处理表单输入、调用被封装在EJB组件中的业务逻辑组件,以及使用JSP来格式化网页的输出。 servlet在对用户请求作出的响应中生成内容,以此来控制应用流从对一个用户的交互转到对下一个用户的交互。 Servlet的基本特征有:
Servlet数据流 当用户点击了 Submit(提交)按钮时,输入在显示页面中的信息将被交付给servlet。这个servlet会处理这些输入的数据,并通过生成内容来编制出一个响应,内容的生成通常是由业务逻辑组件(EJB组件)来完成的。一旦生成了内容,servlet就创建一个响应页面——一般是通过将内容转交给JSP。最后该响应被返回给客户,这样就可以开始下一次的用户交互。 下面的示例展示了进出servlet的信息流:
servlet仍驻留在内存中,以供处理其它的请求。 Servlet数据流的流程 Servlet的分类 servlet主要有两种:
这两种类型的servlet都实现了构造器方法init()和毁坏器方法destroy(),分别用于初始化和回收资源。 所有的servlet都必须实现一个service()方法,由该方法来负责处理servlet请求。在通用servlet中,只需通过简单地重载service方法来提供处理请求的例程。而HTTP servlet提供了一个service方法,该方法自动地将请求发送给本servlet中的另外一个方法,而在这个另外的方法中使用了HTTP传输方法。因此,在HTTP servlet中,要处理POST请求就重载doPost方法,要处理GET请求就重载doGet方法,以此类推。 要创建一个servlet,需执行以下操作:
剩下的几节讨论以下话题:
创建一个servlet,需要写一个public Java类,该类应包含基本I/O的支持,还需要引入javax.servlet包。这个类必须继承GenericServlet 或HttpServlet。由于Since Sun Java System Application Server的servlet是在HTTP环境下的,所以这里推荐使用后一类的servlet(即HTTP servlet,译者注)。如果这个servlet属于一个package(包)的一部分,你还必须声明这个package的名字,以便类装载器可以正确地找到这个package。 下面的头文件的例子展示了一个名为myServlet的HTTP servlet类的声明: import java.io.*; 接着,重载一个或更多的方法,从而为servlet执行不同的任务提供了相应的指令。servlet执行的所有处理都是基于request-by-request方式的,这些处理都在service方法中进行,在通用servlet中就是service()方法,而对于HTTP servlet,就是doOperation()方法。service方法接收进来的请求,根据所提供的指令处理这些请求,然后作出适当的输出。你还可以在servlet中创建其它的方法。 为了执行一次事务,业务逻辑可能还需访问数据库或者将请求传给一个EJB组件。 重载初始化方法 重载类初始化方法init(),在init()方法中为servlet实例的生命周期初始化或分配资源,例如一个counter(计数器)。init()方法将在servlet初始化之后、接收任何请求之前运行。如果想了解更多的信息,请参考servlet API规范说明书。
下面例子中的init()方法通过创建一个名为thisMany的整型变量来初始化一个counter。 public class myServlet extends HttpServlet { 现在,其它的servlet方法就可以访问这个变量了。 重载毁坏器方法 重载类毁坏器destroy()方法是为了记录日志消息或释放在servlet的生命周期内打开了的资源。资源必须正确地关闭并且释放,这样才可以被重新使用或由垃圾收集器收回。destroy()方法在servlet本身正要退出内存之前运行。如果想知道更多的信息,请参考servlet API规范说明书。 举个例子,根据前面重载初始化方法中的例子,destroy()方法可以写如下所示的日志消息: out.println("myServlet was accessed " + thisMany " times.\n"); 重载Service、Get和Post方法 当有请求发生时,Sun Java System Application Server将收到的数据传给servlet引擎,由servlet引擎来处理这个请求。请求包括表单数据、cookie、session信息,以及URL name-value对,所有这些都包含在一个称为request对象的HttpServletRequest对象中。客户端元数据被封装成一个称为response对象的HttpServletResponse对象中。Servlet引擎将这两个对象都传给servlet的service()方法作为参数。 在HTTP servlet中,默认的service()方法会将请求发送给另外一个基于HTTP传输方法(POST,GET等)的方法。这样就使得servlet可以通过传输方法来执行不同的request数据处理。由于请求的发送是在service()方法中进行的,所以在HTTP servlet中通常不必重载service()方法。但是,需要重载doGet()、doPost()等方法,视预计的请求类型而定。 在HTTP servlet中,请求的自动转送是通过简单地调用request.getMethod()来实现的,该方法提供了HTTP传输方法。在Sun Java System Application Server中,request数据在到达servlet之前就已经被预处理成一个name-value表的形式,因此在HTTP servlet中只需重载service()方法,而不用担心会降低其功能性。然而,这样一来servlet就缺乏了可移植性,因为它现在依赖于经过预处理的数据。 为执行需要对请求作出应答的任务,需要重载service()方法(在通用servlet中)或者doGet()或doPost()方法(在HTTP servlet中)。往往这就意味着要通过访问EJB组件来执行业务处理,选取需要的信息(这些信息在request对象或JDBC ResultSet对象中),然后将新生成的内容传递给JSP进行格式化并返回给客户端。 大多数涉及到表单的操作都需要使用GET或POST操作中的一种,因此,在大多数servlet中都需要重载doGet()或doPost()方法。注意,实现这两个方法时,都需要为它们提供输入类型或者直接将request对象传给一个集中处理方法,如下例所示: public void doGet (HttpServletRequest request, 在HTTP servlet中,所有的request-by-request事务都在适当的doOperation()中处理,包括session管理、用户鉴别、调度EJB组件和JSP,以及使用Sun Java System Application Server的一些特殊功能等。 如果试图在servlet中调用RequestDispatcher方法include()或forward(),要清楚request信息不再是以HTTP POST、GET等形式发送的。换句话说,如果servlet重载了doPost(),那么当其它servlet调用该方法时,该方法处理不了任何东西,即使在调用servlet时正好通过HTTP GET收到了这个请求的数据。因此,正如上面所解释的那样,应该确保对各种可能的输入类型都要实现例程。RequestDispatcher方法总是会调用service()方法。 要了解更多的信息,请参考"Calling a Servlet Programmatically"。
传进来的数据是封装在一个request对象中的。对于HTTP servlet来说,request对象的类型是HttpServletRequest;对于通用servlet来说,request对象的类型是ServletRequest。Request对象包含了所有的请求参数,包括名为attributes的自定义的request数值。 要访问所有传进来的request参数,可以通过getParameter()方法。例如: String username = request.getParameter("username"); 可以通过setAttribute()和getAttribute()分别对request对象赋值和从中取值。例如: request.setAttribute("favoriteDwarf", "Dwalin"); 上述例子向我们展示了一种将数据传给JSP的方法,既然request对象是JSP的内建bean,JSP自然可以访问得到。 从一个Web或应用服务器的角度来看,一个Web应用实际上就是一系列互不相干的服务器命中事件。如果某个用户曾经访问过这个站点,甚至用户的最后一次交互就发生在几秒钟之前,都不会自动地在服务器上留下记载。而session(会话)却可以通过记住应用的状态来支持多个用户的交互。在每次交互中,客户端通过一个cookie来对自己进行确认,如果浏览器是不带cookie的,也可以通过将session标识符放在URL中来进行确认。 session对象可以用于存放对象,如表列数据、关于应用当前状态的信息以及关于当前用户的信息。绑定在session中的对象还可以被其它使用了同一个session的组件访问。 如果想了解更多信息,请参考"创建和管理用户会话"。 登录成功后,你应该引导servlet在一个称为session对象的标准对象中建立用户角色,包括关于当前会话的信息,用户用来登录的名字和所有需要保留的附加信息。这样一来,应用组件就可以查询session对象,从而对用户进行确认。 如果需要为应用提供安全的用户session,请参考"Web应用安全性"。 在Sun Java System Application Server编程模式下,你可以通过EJB组件实现业务逻辑,包括数据库或姓名地址录的事务处理以及复杂的计算。在处理特定任务时,需要将一个request对象的引用作为参数来传递。 将从访问数据库得来的结果存放在JDBC ResultSet对象中,并将该对象的一个引用传给其它的组件,从而进行格式化并发送给客户。另外,通过request.setAttribute()方法可以将request对象得到的结果保存下来,对于session,可以使用session.setAttribute()方法。存放在request对象中的对象必须与请求的长度相匹配,换句话说,要和这种特定的servlet线程相匹配。存放在session对象中的对象可以维持到session的整个生命周期,在这段时间内,可以经历很多的用户交互。 下面的例子展示了一个访问了一个名为ShoppingCart 的EJB组件的servlet。这个servlet在导入cart的远程接口后,通过获取用户的session ID创建了一个名为cart的cart的句柄,该句柄被存放在用户的session中。 import cart.ShoppingCart; 在servlet中通过Java命名目录接口(Java Naming Directory Interface,JNDI)访问EJB组件,建立一个EJB组件的句柄,或者是代理。接着,像一个正规的对象那样引用EJB组件,具体操作由bean的容器来管理。 以下是JNDI查找shopping cart(购物车)的代理的例子: String jndiNm = "java:comp/env/ejb/ShoppingCart"; 如果想了解更多关于EJB组件的信息,请参考Sun Java System Application Server开发者指南中Enterprise JavaBeans 技术部分的内容。
在默认情况下,servlet不具备线程安全性。一个单独的servlet实例中的方法通常并发地执行多次(视可用的内存而定)。每次方法的执行都是通过servlet引擎仅有的一个servlet拷贝而在一个不同的线程中发生。 虽然这样可以有效地利用系统资源,但却是危险的,因为Java管理内存的方式决定了这样做的不安全性。由于属于servlet类的变量是通过引用来传递的,不同的线程会以覆盖的方式写同一块内存中的内容,这就是上述做法的副作用。要使一个servlet(或一个servlet中的某个程序块)是线程安全的,可以选用以下介绍的方法中的一种:
举个例子,假设该servlet在doGet()中有一个线程安全的程序块,以及一个线程安全的方法mySafeMethod(): import
java.io.*;
下面的例子中,servlet是完全单线程的: import
java.io.*; 用户交互的最后一个活动是提交一个响应页面给客户。响应页面可以通过两种方式提交: 可以通过将内容写入output流的方式来创建servlet的输出页面。具体应采取的方式取决于输出的类型。 在进行任何输出之前,都要通过使用setContentType()方法来指定输出的MIME类型。如下所示: response.setContentType("text/html"); 对于一些文本输出,如简单的HTML,可以创建一个PrintWriter对象并通过println语句将文本写入该对象中。例如: PrintWriter output = response.getWriter(); 对于二进制输出,可以直接写入到输出流中。先创建一个ServletOutputStream对象,然后通过print()语句将内容写入到该对象中。例如: ServletOutputStream output = response.getOutputStream(); servlet可以通过两种方式调用JSP:
下面的例子展示了一个使用include()方法的JSP: RequestDispatcher
dispatcher =
|