TomcatJava Web开发技术详解
    本章介绍如何在Tomcat上创建和发布Web应用。这里首先讲解Tomcat的目录结构以及Web应用的目录结构,接着介绍如何将HTMLServletJSPTag Library部署到Web应用中,然后介绍把整个Web应用打包并发布的方法,最后介绍如何在Tomcat上配置虚拟主机。
    本章侧重于讨论Web应用的结构和发布方法,所以没有对本章的ServletJSP的例子进行详细解释,关于ServletJSP的技术可以分别参考其它章节的内容。
2.1 Tomcat的目录结构
Tomcat上发布Web应用之前,首先要了解Tomcat的目录结构。Tomcat的目录结构参见表2-1,这些目录都是的子目录。
2-1 Tomcat的目录结构
/bin                        存放在Windows平台 以及Linux平台上启动 和关闭Tomcat的脚本文件
/conf                      存放Tomcat服务器的各种配置文件 其中最重要的配置文件是l
/server                    包含3个子目录: classes(空目录)libwebapps
/server/lib              Tomcat服务器所需的各种JAR文件
/server/webapps  存放Tomcat自带的两个Web应用: admin应用和manager应用
/common/lib            存放Tomcat服务器以及所有 Web应用都可以访问的JAR文件
/shared/lib             存放所有Web应用都可以访问的JAR文件
/logs                        存放Tomcat的日志文件
/webapps                发布Web应用时, 默认情况下把Web应用 文件放于此目录下
/work                      Tomcat把由JSP生成的 Servlet放于此目录下
从表2-1可以看出,在/server/lib目录、/common/lib/shared/lib目录下都可以放JAR文件,它们的区别在于:
/server/lib目录下的JAR文件只可被Tomcat服务器访问
/shared/lib目录下的JAR文件可以被所有的Web应用访问,但不能被Tomcat服务器访问
/common/lib目录下的JAR文件可以被Tomcat服务器和所有Web应用访问
    此外,对于下面将要介绍的Java Web应用,在它的WEB-INF目录下,也可以建立lib子目录,在lib子目录下可以放各种JAR文件,这些JAR文件只能被当前Web应用访问。
在以上提到的lib目录下都只接受JAR文件,如果类压缩文件为ZIP文件,应该将它展开,重新打包为JAR文件再拷贝到lib目录中。如果直接将ZIP文件拷贝到lib目录,则会发现Tomcat服务器仍然不到相关的类。
2.2 创建和发布Web应用
Java Web应用由一组静态HTML页、ServletJSP和其他相关的class组成。每种组件在Web应用中都有固定的存放目录。Web应用的配置信息存放在l文件中。在发布某些组件(如Servlet)时,必须在l文件中添加相应的配置信息。
2.2.1 Web应用的目录结构
    Web应用具有固定的目录结构,这里假定开发一个名为helloappWeb应用。首先,应该在/webapps目录下创建这个Web应用的目录结构,参见表2-2
2-2 Web应用的目录结构


/helloapp                                    应用的根目录 所有的JSPHTML文件 都存放于此目录下
/helloapp/WEB-INF                    存放Web应用的发布描述文件l
/helloapp/WEB-INF/classes      存放各种class文件Servlet类文件也放于此目录下
/helloapp/WEB-INF/lib              存放Web应用所需的各种JAR文件。例如,在这个目录下,可以存放JDBC驱动程序的JAR文件
从表2-2中,我们看到在classes以及lib子目录下,都可以存放Java类文件。在运行过程中,Tomcat的类装载器先装载classes目录下的类再装载lib目录下的类因此,如果两个目录下存在同名的类,classes目录下的类具有优先权。
helloapp应用中创建了如下组件
HTML组件:index.htm
JSP组件:login.jsphello.jsp
Servlet组件:DispatcherServlet
这些组件之间的链接关系为:
index.htmlogin.jspDispatcherServlethello.jsp
2-1 helloapp应用的目录结构
2-2 helloapp应用在Windows资源管理器中的展开图

2.2.2 l文件
Java Web应用通过一个基于XML的发布描述符文件来配置其发布信息,这个文件名为l,它存放于WEB-INF子目录下。
l文件中可包含如下配置信息:
1Servlet定义
2Servlet初始化参数
3Servlet以及JSP映射
4、安全域配置参数
5welcome文件清单
6、资源引用
7、环境变量的定义
现在,创建一个默认的l文件,并把这个文件放到WEB-INF目录中。

<?xml version="1.0" encoding="ISO-8859-1"?>  //xml版本类型
<!DOCTYPE web-app PUBLIC    //文档类型
'-//Sun Microsystems,
Inc.//DTD Web Application 2.3
//EN'
'java.sun/
j2ee/dtds/web-app_2_3.dtd'>
<web-app>             //一个元素
</web-app>
以上l文件的第一行指定了XML的版本和字符编码,第二行DOCTYPE指定文档类型,接下来声明了一个元素,所有关于Web应用的配置元素都将加入到这个元素中。
2.2.3 l中加入元素
    元素是/l中使用最频繁的元素,它代表了运行在上的单个Web应用。一个中可以有多个元素。每个Web应用必须有惟一的URL路径,这个URL路径在元素的path属性中设定。例如,在名为"localhost"的元素中加入如下元素:

<Host name="localhost" debug="0" appBase="webapps"
unpackWARs="true" autoDeploy="true">
……
……
<Context path="/helloapp"
docBase="helloapp" debug="0"
reloadable="true"/>
</Host>
Context元素的各个属性的说明参见表2-3
2-3 Context元素的属性


path
指定访问该Web应用的URL入口
docBase
指定Web应用的文件路径,可以给定绝对路径,也可以给定相对于HostappBase属性的相对路径(关于HostappBase属性参见2.3节)。如果Web应用采用开放目录结构,则指定Web应用的根目录;如果Web应用是个WAR文件,则指定WAR文件的路径
reloadable
如果这个属性设为true Tomcat服务器在运行状态下会监视WEB-INF/classes WEB-INF/lib目录下class文件的改动。如果监测到有class文件被更新,服务器会自动重新加载Web应用
在开发阶段,将reloadable属性设为true,有助于调试Servlet和其他的class文件。但是由于这一功能会加重服务器的运行负荷,因此建议在Web应用的产品发布阶段,将这个属性设为false
2.2.4 部署HTML文件

helloapp目录下加入index.htm文件,这个文件仅仅用来显示一串带链接的字符"Welcome to HelloApp", 它链接到login.jsp文件。以下是index.htm文件的代码:
<html>
<head>
<title>helloapp</title>
</head>
<body >
<p><font size="7">Welcome to HelloApp</font></p>
<p><a href="login.jsp
language=English">English version </a>
</body>
</html>


访问index.htmURL localhost:8080/helloapp/index.htm,该页面的显示结果如图2-3所示。
2-3 index.htm


2.2.5 部署JSP

接下来,创建两个JSP文件,其中一个是login.jsp(参见例程2-1),它显示登录页面,要求输入用户名和口令,这个页面链接到一个名为DispatcherServletServlet 还有一个JSP文件是hello.jsp(参见例程2-2),这个JSPDispatcherServlet调用,显示Hello页面。JSP的语法将在第4章详细讨论,本节侧重于介绍JSP的发布过程。这两个JSP文件都应放在helloapp目录下。

例程2-1 login.jsp
<html>
<head>
      <title>helloapp</title>
</head>
<body >
<br>
<form name="loginForm" method="post"
action="dispatcher">
<table>
<tr>
<td><div align="right">User Name:</div></td>
<td> <input type="text" name="username"></td>
</tr>
<tr>
<td><div align="right">Password:</div></td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td></td>
<td><input type="Submit"
name="Submit" value="Submit"></td>
</tr>
</table>
</form>
</body>
</html>


例程2-2 hello.jsp
<html>
<head>
      <title>helloapp</title>
</head>
<body>
      <b>Welcome: <%= Attribute("USER") %></b>
</body>
</html>


login.jsp中生成了一个loginForm表单,它有两个字段:usernamepassoword。访问login.jspURLlocalhost:8080/helloapp/login.jsp,它生成的页面如图2-4所示。
2-4 login.jsp网页


2.2.6 部署Servlet

下面,创建一个Servlet文件,名为DispatcherServlet.java(参见例程2-3),它调用HttpServletRequest对象的getParameter方法读取客户提交的loginForm表单数据,获取用户名和口令,然后将用户名和口令保存在HttpServletRequest对象的属性中,再把请求转发给hello.jsp

例程2-3 DispatcherServlet.java
package mypack;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

public class DispatcherServlet
extends HttpServlet
{

      private String target = "/hello.jsp";

      public void init(ServletConfig config)
        throws ServletException
{
        super.init(config);
      }

      public void doGet
      (HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException
{

        // If it is a get request
forward to doPost()
        doPost(request, response);
      }

      public void doPost
      (HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException
{

        // Get the username from the request
        String username =
Parameter("username");
        // Get the password from the request
        String password =
Parameter("password");

        // Add the      user to the request
        request.setAttribute
("USER", username);
        request.setAttribute
("PASSWORD", password);

        // Forward the request
to the target named
        ServletContext context
= getServletContext();

        System.out.println
("Redirecting to " + target);
        RequestDispatcher dispatcher =
          RequestDispatcher(target);
        dispatcher.forward(request, response);
      }

      public void destroy()
      {
      }
}


编译并发布DispatcherServlet的步骤如下。

1)编译DispatcherServlet.java。编译时,需要将Java Servlet APIJAR文件(servlet-api.jar)设置为classpathservlet-api.jar文件位于/common/lib目录下。

2)把编译出来的class文件拷贝到/helloapp/WEB_INF/classes目录下。DispatcherServlet.class的存放位置为/helloapp/WEB_INF/classes/mypack/DispatcherServlet

在本例中,声明将DispatcherServlet类放在包mypack下,所以应该在/WEB_INF/classes目录下先创建子目录/mypack,然后在子目录下放DispatcherServlet.class文件。

3)接下来在l中为DispatcherServlet类加上和元素。
<web-app>

<servlet>
      <servlet-name>dispatcher</servlet-name>
      <servlet-class>mypack.DispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
      <servlet-name>dispatcher</servlet-name>
      <url-pattern>/dispatcher</url-pattern>
</servlet-mapping>

</web-app>


元素的属性描述参见表2-4

在本例配置中,没有为DispatcherServlet设置load-on-startup属性,因此当Web应用启动时,Servlet容器不会加载这个Servlet,只有当Web客户首次访问这个Servlet时才加载它。

2-4 元素的属性

servlet-name
定义Servlet的名字
servlet-class
指定实现这个Servlet的类
init-param
定义Servlet的初始化参数 (包括参数名和参数值),一个元素中可以有多个
load-on-startup
指定当Web应用启动时, 装载Servlet的次序。 当这个值为正数或零, Servlet容器先加载数值小的Servlet 再依次加载其他数值大的Servlet 如果这个值为负数或者没有设定, 那么Servlet容器将在Web客户首次 访问这个Servlet时加载它


元素用来指定和映射。是指访问Servlet的相对URL路径。

根据以上属性,访问DispatcherServletURLlocalhost:8080/ helloapp/dispatcherDispatcherServlet接受到客户请求后,再把请求转发给jsp定义hello.jsphello.jsp生成的页面如图2-5所示。
2-5 DispatcherServlet调用hello.jsp生成的网页


2.2.7 部署JSP Tag Library

最后,在Web应用中加入Tag Library(标签库)。Tag Library向用户提供了自定义JSP标签的功能。我们将定义一个名为mytaglib的标签库,它包含了一个简单的hello标签,这个标签能够将JSP页面中所有的解析为字符串"hello"。以下是创建和发布mytaglib标签库的步骤。

1)编写用于处理hello标签的类HelloTag.java,例程2-4列出了HelloTag.java的源代码。

例程2-4 HelloTag.java
package mypack;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.TagSupport;

public class HelloTag extends TagSupport
{
        public void HelloTag()
{
}

        // Method called when the
closing hello tag is encountered
        public int doEndTag() throws JspException
{

            try {

                // We use the pageContext to get a Writer
                // We then print the text string Hello
                Out().print("Hello");
            }
            catch (Exception e)
      {

                throw new Message());
            }
            // We want to return SKIP_BODY because
      this Tag does not support
            // a Tag Body
            return SKIP_BODY;
        }

        public void release()
{

            // Call the parent's release
      to release any resources
            // used by the parent tag.
            // This is just good practice
      for when you start creating
            // hierarchies of tags.
            lease();
        }
}


编译HelloTag.java时,需要将jsp-api.jar文件添加到classpath中,这个JAR文件位于/common/lib目录下。编译生成的HelloTag.class存放位置为/WEB-INF/classes/mypack/HelloTag.class

2)创建Tag Library的描述文件mytaglib.tld文件,在这个文件中定义mytaglib标签库和hello标签。这个文件存放位置为/WEB-INF/mytaglib.tld。例程2-5列出了mytaglib.tld的源代码。

例程2-5 mytaglib.tld
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
            PUBLIC "-//Sun Microsystems,
      Inc.//DTD JSP Tag Library 1.1
      //EN"
"java.sun/j2ee/dtds
/web-jsptaglibrary_1_1.dtd">



<taglib>
      <tlibversion>1.0</tlibversion>
      <jspversion>1.1</jspversion>
      <shortname>mytaglib</shortname>
      <uri>/mytaglib</uri>

      <tag>
        <name>hello</name>
        <tagclass>mypack.HelloTag</tagclass>
        <bodycontent>empty</bodycontent>
        <info>Just Says Hello</info>
      </tag>

</taglib>


3)在l文件中加入元素,例程2-6列出了修改后的l文件。

例程2-6 加入元素的l
<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app PUBLIC
      '-//Sun Microsystems, Inc.
      //DTD Web Application 2.3//EN'
      'java.sun/j2ee
      /dtds/web-app_2_3.dtd'>

<web-app>

        <servlet>
            <servlet-name>dispatcher</servlet-name>
            <servlet-class>mypack.DispatcherServlet
      </servlet-class>
        </servlet>

        <servlet-mapping>
            <servlet-name>dispatcher</servlet-name>
            <url-pattern>/dispatcher</url-pattern>
        </servlet-mapping>

<taglib>
        <taglib-uri>/mytaglib</taglib-uri>
        <taglib-location>/WEB-INF/mytaglib.tld
</taglib-location>
      </taglib>

</web-app>


中包含两个属性和。其中指定Tag Library标示符;指定Tag Library的描述文件(TLD)的位置。

4)在hello.jsp文件中加入hello标签。首先,在hello.jsp中加入引用mytaglibtaglib指令:
<%@ taglib uri="/mytaglib" prefix="mm" %>


以上taglib指令中,prefix用来指定引用mytaglib标签库时的前缀,修改后的hello.jsp文件参见例程2-7

例程2-7 加入Tag标签的hello.jsp
<%@ taglib uri="/mytaglib" prefix="mm" %>
<html>
<head>
      <title>helloapp</title>
</head>
      <b><mm:hello/> :
      <%= Attribute("USER") %></b>
</body>
</html>


hello.jsp修改后,再依次访问index.htmlogin.jspDispatcherServlethello.jsp,最后生成的网页如图2-6所示。