第一个Java Web应用

概述

Tomcat是符合Servlet规范的优秀Servlet容器。Java Web应用运行在Servlet容器中,Servlet容器能够动态调用Java Web应用中的Servlet。
下面以一个简单的helloapp应用为例,分享Java Web应用实操经验。在这个例子中将分享一下内容:

  • Java Web应用的基本组成内容和目录结构。
  • 在web.xml中配置Servlet。
  • 在Tomcat中发布Java Web应用。
  • 配置Tomcat的虚拟主机
  • 创建、发布和使用自定义JSP标签。

Java Web应用简介

Oracle公司的Servlet规范对Java Web应用做了这样的定义:“Java Web应用由一组Servlet/JSP、HTML文件、相关Java类,以及其他可以被绑定的资源构成。它可以在各种由供应商提供符合Servlet规范的Servlet容器中运行。”
从Java Web应用的定义可以看出,Java Web应用不仅可以在Tomcat中运行,还可以在其他符合Servlet规范的Servlet容器中运行。在Java Web应用中可以包含如下内容:

  • Servlet组件
    标准Servlet接口的实现类,运行在服务器,包含了被Servlet容器动态调用的程序代码。

  • JSP组件
    包含Java程序代码的HTML文档。运行在服务器端,当客户端请求访问JSP文件时,Servlet容器先把它编译成Servlet类,然后动态调用它的程序代码。

  • 相关的Java类
    开发人员自定义的与Web应用相关的Java类。

  • 静态文档
    存放在服务器端的文件系统中,如HTML文件、图片文件和声音文件等。当客户端请求访问这些文件时,Servlet容器从本地文件系统中读取这些文件的数据,再把它发送到客户端。

  • 客户端脚本程序
    是由客户端来运行的程序,JavaScript是典型的客户端脚本程序。

  • web.xml文件
    Java Web应用的配置文件,该文件采用XML格式。该文件必须位于Web应用的WEB-INF目录下。

提示

Servlet容器能够运行Java Web应用,而Java Web应用的最主要的组件就是Servlet和JSP。因此Servlet容器也被成为Jave Web容器或者Servlet/JSP容器。

创建Java Web应用

Java Web应用中可以包含HTML文档、Servlet、JSP和相关Java类等。为了让Servlet容器能顺利地找到Java Web应用中的各个组件,Servlet规范规定,Java Web应用必须是固定的目录结构,每种类型的组件在Web应用中都有固定的存放目录。Servlet规范还规定Java Web应用的配置信息存放在WEB-INF/web.xml文件中,Servlet容器从该文件中读取配置信息。在发布某些Web组件(如Servlet)时,需要在web.xml文件中添加相应的关于这些Web组件的配置信息。

Java Web应用的目录结构

Java Web应用具有固定的目录结构,假定开发一个名为helloapp的Java Web应用。首先,应该创建这个Web应用的目录结构,参见下表。

目录描述
/helloappWeb应用的根目录,所有的JSP和HTML文件都存放于此目录或子目录下
/helloapp/WEB-INF存放Web应用的配置文件web.xml
/helloapp/WEB-INF/classes存放各种.class文件,Servlet类的.class文件也放于此目录下
/helloapp/WEB-INF/lib存放Web应用所需的各种JAR文件(类库文件)。例如,在这个目录下可以存放JDBC驱动程序的JAR文件。

从上表可以看出,在WEB-INF目录的classes以及lib子目录下,都可以存放Java类文件。在运行时,Servlet容器的类加载器先加载classes目录下的类,再加载lib目录下的JAR文件(Java类库的打包文件)中的类。因此,如果两个目录下存在同名的类,classes目录下的类具有优先权。另外,浏览器端不可以直接请求访问WEB-INF目录下的文件,这些文件只能被服务器端的组件访问。
在此例中介绍的helloapp应用的完整目录结构如下图所示。

Created with Raphaël 2.3.0 helloapp目录 src目录 helloapp目录 DispathcherServlet.java、HelloTag.java login.htm、hello.jsp WEB-INF目录 web.xml、mytaglib.tld classes目录 helloapp目录 DispathcherServlet.class、HelloTag.class

上图中有一个src目录,这是在开发helloapp应用阶段,开发者自定义的目录,该目录用于存放所有Java类的源文件。代理Web应用产品正式发布阶段,一般都不希望对外公开Java源码,所以届时应该将src目录转移到其他地方。
在helloapp应用中包含如下组件。

  • HTML组件:login.html
  • Servlet组件:DispatcherServlet类
  • JSP组件:hello.jsp
    这些组件之间的关系如下图所示。login.html与DispatcherServlet类之间的超级链接关系,DispatcherServlet类与hello.jsp之间为请求转发关系。
超链接关系
服务器端的请求转发关系
login.html
DispatcherServlet类
hello.jsp

目录结构图还包含HelloTag.java、mytaglib.tld和HelloTag.class文件,这些文件用于创建、发布和使用自定义JSP标签。

创建HTML文件

在helloapp目录下加入login.html文件,它包含一个名为"loginForm"的登陆表单,要求客户输入用户名和口令。当用户提交表单,将由URI为“dispathcher”的servlet来处理,这个Servlet的Java类名为helloapp.DispathcherServle。以下是login.html的例程代码。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>helloapp</title>
</head>
<body>
	<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><input type="submit" name="submit" value="submit"></td>
			<td><input type="reset" name="reset" value="reset"></td>
		</tr>
	</table>
	</form>
</body>
</html>

访问login.html的URL为http://localhost/helloapp/login.html,该页面的显示结果如下图所示。
在这里插入图片描述

创建Servlet类

下面创建DispathcherServlet类,它调用ServletRequest对像的getParameter()方法读取客户提交的login.html表单数据,获取用户名和口令,然后将用户名和口令作为属性保存在ServletRequest对象中,再把请求转发给hello.jsp。

package helloapp;

import jakarta.servlet.*;
import jakarta.servlet.http.*;

import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

import java.io.*;
import java.util.*;

public class DispatcherServlet extends GenericServlet{

	private String target = "/hello.jsp";
	
	//响应客户请求
	public void service(ServletRequest request, ServletResponse response)
	throws ServletException, IOException
	{
		//读取表单中的用户名
		String username = request.getParameter("username");
		//读取表单中的口令
		String password = request.getParameter("password");
		
		//在request中添加USER属性
		request.setAttribute("USER", username);
		//在request中添加PASSWORD属性
		request.setAttribute("PASSWORD", password);
		
		//把请求转发给hello.jsp
		ServletContext context = getServletContext();
		RequestDispatcher dispatcher = context.getRequestDispatcher(target);
		dispatcher.forward(request, response);
	}
}

编译servlet时,需要将Servlet API的JAR文件(servlet-api.jar)加入到classpath中。servlet-api.jar文件位于<CATALINA-HOME>/lib目录下。此外,从Oracle的官方网址(http://www.oracle.com/technetwork/java/index.html)也可以下载该文件。
创建好DispathcherServlet后,还必须在web.xml文件中对其进行配置,这样客户端才能访问Servlet。

创建JSP文件

下面创建hello.jsp,hello.jsp文件放在helloapp根目录下。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>helloapp</title>
</head>
<body>
  <b>hello: <%= request.getAttribute("USER") %></b>
</body>
</html>

hello.jsp和HTML文档看上去很相似,仅仅在一处地方使用了JSP语法:

<%= request.getAttribute("USER") %>

以上代码的作用是从request(即ServletRequest)对像中获得USER属性值,然后输出该USER属性值。我们在上面的创建Servlet类小节介绍的DispatcherServlet现在request对象中设置了USER属性,再把请求转发给hello.jsp,所以接下来hello.jsp就能从request对象中获取USER属性值,hello.jsp生成的页面如下图所示。
在这里插入图片描述

创建web.xml文件

web.xml文件是Java Web应用的XML格式的配置文件,存放于WEB-INF子目录下。web.xml文件由开发人员编写,供Servlet容器访问。web.xml文件也被称为Java Web应用的发布描述文件,Servlet容器在加载和启动Java Web应用时会读取它的web.xml文件,从中获得关于当前web的发布应用信息。在web.xml文件中可包含如下配置信息:

  • Servlet的定义
  • Servlet的初始化参数
  • Servlet以及JSP的映射
  • 安全域配置参数
  • welcome文件清单
  • 资源引用
  • 环境变量的定义
    现在创建一个默认的web.xml文件,并把这个文件放到WEB-INF目录中。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  version="4.0" >
  ......
</web-app>

以上web.xml文件的第一行指定了XML的版本和字符编码,接下来声明一个<web-app>元素,它是根元素,所有关于Java Web应用的具体配置元素都将加入到这个<web-app>元素中。
接下来在web.xml中为DispatcherServlet类加上<servlet>和<servlet-mapping>元素。

<web-app xmlns=......>
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>helloapp.DispatcherServlet</servlet-class>
  </servlet>

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

<servlet>元素用于为Servlet定义一个名字,它的子元素的说明参见下表。在此例配置中,没有为DispatcherServlet设置<load_startup>子元素,因此当web应用启动时,Servlet容器不会初始化这个Servlet,只有当客户端首次访问这个Servlet时,Servlet容器才会初始化它。

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

<servlet-mapping>元素用于为Servlet映射一个URI。<servlet-name>子元素指定待映射的Servlet名字;<url-pattern>子元素指定访问Servlet的相对URL路径。根据以上范例的<url-pattern>子元素的值,可以确定访问DispatcherServlet的URL为http://localhost/helloapp/dispatcher。
在web.xml文件中还可以加入元素,它用来为Web应用设置默认的主页。例如,当客户提供的URL为http://localhost/helloapp/时。如果希望服务器端自动返回login.html页面,则可以在web.xml文件中加入以下元素:

  <welcome-file-list>
    <welcome-file>login.html</welcome-file>
  </welcome-file-list>

在Tomcat中发布Java Web应用

Java Web应用可以运行在各种符合Servlet规范的Servlet容器中。此节介绍在Tomcat中发布Java Web应用的步骤。尽管发布的具体细节依赖于Tomcat本身的实现,但以下关于发布Java Web应用的基本思想适用于所有Servlet容器:

  • 把Web应用的所有文件复制到Servlet容器的特定目录下,这是发布Web应用的最快捷的一种方式。
  • 各种容器Servlet容器实现都会从Web应用的web.xml配置文件读取有关Web组件的配置信息。
  • 为了使用户能更加灵活地控制Servlet容器发布和运行Web应用的行为,并且为了使Servlet容器与Web应用能进行更紧密地协作,许多Servlet容器还允许使用用户使用额外地配置文件以及配置元素,这些配置文件及配置元素的语法由Servlet容器的实现决定,与Oracle的Servlet规范无关。

Tomcat的目录结构

在Tomcat上发布Java Web应用之前,首先要了解Tomcat的目录结构。Tomcat的目录结构是由自身的实现决定的,与Oracle公司的Servlet规范无关。Tomcat9.x的目录结构参见下表,表中的目录都是<CATALINA_HOME>的子目录。

项目Value
/bin存放Windows平台以及Linux平台上启动和关闭Tomcat的脚本
/conf存放Tomcat服务器的各种配置文件,其中最重要的配置文件是server.xml
/lib存放Tomcat服务器以及所有Web应用都可以访问的JAR文件
/logs存放Tomcat的日志文件
/webapps在Tomcat上发布Java Web应用时,默认情况下把Web应用文件存放于此目录下
/workTomcat的工作目录,Tomcat在运行时把生成的一些工作文件存放于此目录下。例如默认情况下,Tomcat把编译JSP生成的Servlet类文件放在此目录下。

按照默认方式发布Java Web应用

在Tomcat中发布Java Web应用的最快捷方式,就是直接把Java Web应用的所有文件复制到Tomcat的<CATALINA_HOME>/webapps目录下。默认情况下,<CATALINA_HOME>/webapps中所有的Web应用运行在名为localhost的虚拟主机中,而localhost虚拟主机则运行在名为Catalina的Engine组件中。
Tomcat既可以运行采用开放式目录结构的Web应用,也可以运行Web应用的打包文件(简称为WAR文件)。
在Web应用的开发阶段,为了便于调试,通常采用开放式的目录结构来发布Web应用,这压根可以方便更新或替换文件。如果开发完毕,进入产品发布阶段,应该将整个Web应用打包为WAR文件,再进行发布。此例中,按照如下步骤helloapp。

  • 在DOS中进入helloapp应用的根目录helloapp。
  • 使用jar cvf命令把整个Web应用打包为helloapp.war文件。
  • 如果在Tomcat的webapps目录下已经由helloapp子目录,先将helloapp子目录删除。
  • 把helloapp.war文件复制到<CATALINA_HOME>/webapps目录下。
  • 驱动Tomcat服务器。
    Tomcat服务器启动时,会把webapps目录下的所有WAR文件自动展开为开放的目录结构。因此服务器启动后,读者会发现服务器自动在webapps目录下创建一个helloapp目录,并把helloapp.war中的所有内容展开到helloapp子目录中。

Web组件的URL

无论按照开放式目录结构还是按照打包文件方式发布Web应用,Web应用的默认URL入口都是Web应用的根目录名。例如对于helloapp应用,它的URL入口为"/helloapp"。
对于HTML或JSP文件,它们的URL为http://localhost/helloapp/login.html。同样,hello/jsp的绝对路径为helloapp/hello.jsp,因此它的URL为http://localhost/helloapp/hello.jsp。
HTML或JSP问价不仅可以放在Web应用的根目录下,也可以放到自定义的子目录中。例如,假定hello.jsp的文件路劲为helloapp/dir1/dir/2hello.jsp,那么它的URL为:http://localhost/helloapp/dir1/dir2/hello.jsp。
提示:浏览器无法直接访问Web应用的WEB-INF目录下的文件。因此,如果出于安全的原因,不希望浏览器直接访问某个JSP文件,可以把它放到WEB-INF目录或其子目录下,在这种情况下,只有服务器端的组件才能访问该JSP文件,例如Servlet可以把请求转发给JSP文件。
对于Servlet,对其映射URL由两种方式:

  • 在web.xml文件种映射URL。
  • 用@WebServlet标注映射URL。
    在此介绍如何在web.xml种配置Servlet。Servlet的URL由web.xml文件中的<url-pattern>元素来指定。在此例中,web.xml文件对helloapp.DispatcherServlet类做如下配置:
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>helloapp.DispatcherServlet</servlet-class>
  </servlet>

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

以上<url-pattern>元素值为“/dispatcher”,因此访问helloapp.DispatcherServlet类的URL为:http://localhost/helloapp/dispatcher。当浏览器端首次通过该URL请求访问DispatcherServlet时,Tomcat需要先找到文件系统中的DispatcherServlet.class文件,从而能加载DispatcherServlet类。Tomcat查找DispatcherServlet文件的步骤如下。

  1. 参考helloapp应用的web.xml文件,找到<servlet-pattern>子元素的值为“/dispatcher”的<servlet-mapping>元素。
  2. 读取<servlet-mapping>元素的<servlet-name>子元素的值,因此确定Servlet的名字为dispatcher。
  3. 找到<servlet-name>子元素为dispatcher的<servlet>元素。
  4. 读取<servlet>元素的<servlet-class>子元素的值,由此确定Servlet的类名为helloapp.DispatcherServlet。
  5. 到<CATALINA_HOME>/helloapp/WEB-INF/classes/helloapp目录下查找DispatcherServlet.class文件。

提示:Tomcat在加载Web应用时,就会把相应的web.xml文件中的数据读入到内存中。因此当Tomcat需要参考web.xml文件时,实际上只要从内存中读取相关数据就行了,无需再到文件系统中读取web.xml文件。
初学者发布了Servlet后,再通过浏览器访问Servlet时,服务器端可能会返回“该文件不存在”的错误。这可能时由以下原因导致的:

  1. 提供的URL不正确,例如以下URL都无法访问DispatcherServlet类:
http://localhost/helloapp/DispatcherServlet.class
http://localhost/helloapp/DispatcherServlet
http://localhost/helloapp/helloapp/DispatcherServlet.class
http://localhost/dispatcher
  1. web.xml作为XML格式的文件,需要区分大小写。如果不注意大小写,可能会导致对Servlet的配置不正确。例如以下<Servlet>元素把helloapp.DispatcherServlet类命名为“Dispatcher”。而<servlet-mapping>元素对一个名为“dispatcher”的Servlet进行了URL映射。这使得名为“Dispatcher”的Servlet实际上没有进行URL映射。
  <servlet>
    <servlet-name>Dispatcher</servlet-name>
    <servlet-class>helloapp.DispatcherServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/dispatcher</url-pattern>
  </servlet-mapping>
  1. Servlet的.class文件的存放路径不正确。Servlet的.class文件必须位于Web应用的WEB-INF/classes目录下,并且类的包名与文件路径不匹配。例如对于一个完整类名为helloapp1.helloapp2.MyServlet的类,它的文件路径应该为:
WEB-INF/helloapp1.helloapp2.MyServlet.class

也许你会问:Servlet规范为什么要对于Servlet进行URL映射呢?如果能直接根据Servlet的文件路径来访问Servlet,不是更方便吗?假如这种设想成立,访问DispatcherServlet类的URL将变为:

http://localhost/helloapp/helloapp/DispatcherServlet.class

Servlet规范之所以要对Servlet进行URL映射,主要由两个原因:

  1. 为一个Servlet对应多个URL提供方便地设置途径。加入有个Web应用规定所有以“.DO”结尾地URL都由ActionServlet来处理,那么只需在web.xml文件中对ActionServlet进行如下配置:
  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>helloapp.ActionServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>/*.do</url-pattern>
  </servlet-mapping>

这样,所有以“.DO”结尾的URL都对应ActionServlet,例如以下URL都映射到ActionServlet:

http://localhost/helloapp/login.DO
http://localhost/helloapp/logout.DO
http://localhost/helloapp/checkout.Do

再例如一个<servlet>还可以对应多个<servlet-mapping>元素:

  <servlet>
    <servlet-name>manager</servlet-name>
    <servlet-class>helloapp.managerServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>manager</servlet-name>
    <url-pattern>/list</url-pattern>
  </servlet-mapping>
  
  <servlet-mapping>
    <servlet-name>manager</servlet-name>
    <url-pattern>/sessions</url-pattern>
  </servlet-mapping>
  1. 简化Servlet地URL,并且可以向客户端隐藏Web应用的实现细节。如果在URL中暴露Servlet的完整类名,会让不懂Java Web开发的普通用户觉得URL很复杂,不容易理解和记忆。通过对Servlet进行URL映射,则可以提供一个简洁、易懂的URL。

提示:JSP文件和Servlet一样,也可以在web.xml文件中映射URL。

配置Tomcat的<Context>元素

前面已经介绍了发布Java Web应用的最快捷方式,只需把Java Web应用的所有文件复制到<CATALINA_HOME>/webapps目录下即可,Tomcat会按照默认方式来发布和运行Java Web应用。如果需要更加灵活地发布Web应用,则需要为Web应用配置Tomcat的<Context>元素。
<Context>元素是Tomcat中使用最频繁的元素,它代表了运行在虚拟主机<Host>上的单个Web应用。在Tomcat的配置文件中,一个<Engine>中可以有多个<Host>,一个<Host>中可以有多个<Context>。<Context>元素的主要属性的说明参见下表。

属性描述
path指定访问该Web应用的URL路径
docBase指定Web应用的文件路径,可以给定绝对路径,也可以给定相对于<Host>的appBase属性的相对路径。如果Web应用采用开放目录结构,则指定Web应用的根目录;如果Web应用是个WAR文件,则指定WAR文件的路径
className指定实现Context组件的Java类的名字,这个Java类必须实现org.apache.catalina.Context接口。该属性的默认值为org.apache.catalina.core.StandardContext
reloadable如果这个属性设为true,Tomcat服务器在运行状态下会监视在WEB-INF/classes和WEB-INF/lib目录下.class类文件或.jar类库文件的改动。如果检测到由类文件或类库文件被更新,服务器会自动重新加载Web应用。该属性的默认值为false。在Web应用的开发和调试阶段,把reloadable设为false,可以降低Tomcat的运行负荷,提高Tomcat的运行性能

一般情况下,<Context>元素都会使用默认的标准Context组件,即className属性采用默认值org.apache.catalina.core.StandardContext。标准Context组件除了具有上表列出的属性,还具有下表所示的属性。

属性描述
uploadDelay设定Tomcat等待Servlet卸载毫秒数。该属性的默认值为2000毫秒
workDir指定Web应用的工作目录。Tomcat运行时会把与这个Web应用相关的临时文件放在此目录下
uppackWar如果此项设为true,表示将把Web应用WAR文件先展开为开放目录结构后在运行。如果设为false则直接运行WAR文件。该属性默认值为true

在Tomcat低版本中,允许直接在<CATALINA_HOME>/conf/servlet.xml文件中配置<Context>元素。这种配置方式有一个弊端:如果在Tomcat运行时修改servlet.xml文件,比如添加<Context>元素,那么所作的修改不会立即生效,而必须重新启动Tomcat,才能使所做的修改生效。
因此从Tomcat 6.x开始的高版本尽管也允许直接在servlet.xml文件中配置<Context>元素,但不提倡采用这种方式。如今的Tomcat提供了多种配置<Context>元素的途径。当Tomcat加载一个应用时,会按照以下顺序查找Tomcat的<Context>元素:

  1. 到<CATALINA_HOME>/conf/context.xml文件中查找<Context>元素。这个文件中的<Context>元素的信息适用于所有Web应用。
  2. 到<CATALINA_HOME>/conf/[enginename]/[hostname]/context.xml.default文件中查找<Context>元素。[enginename]表示[Engine]的name属性,[hostname]表示<Host>的name属性。在context.xml.default文件中的<Context>元素的信息适用于当前虚拟主机中的所有Web应用。例如以下文件中的<Context>元素适用于名为Catalina的Engine下的localhost主机中的所有Web应用:
<CATALINA>/conf/Catalina/localhost/context.xml.default
  1. 到<CATALINA_HOME>/conf/[enginename]/[hostname]/[contextpath].xml文件中查找<Context>元素。[contextpath]表示单个Web应用的URL入口。在[contextpath].xml文件中的<Context>的信息只适用于耽搁Web应用。例如以下文件中的<Context>元素只适用于名为catalina的Engine下的localhost主机中的helloapp应用:
<CATALINA_HOME>/conf/Catelina/localhost/helloqpp.xml
  1. 到Web应用的META-INF/context.xml文件中查找<Context>元素。这个文件中的<Context>元素信息适用于当前Web应用。
  2. 到<CATALINA_HOME>/conf/server.xml文件中的<Host>元素中查找<Context>子元素。该<Context>元素的信息只适用于单个Web应用。

如果仅仅为单个Web应用配置<Context>元素,可以有限选择第三种或第四种方式。第三种方式要求在Tomcat的相关目录下增加一个包含<Context>元素的配置文件,而第四种方式则要求在Web应用的相关目录下增加一个包含<Context>元素的配置文件。对于这两种方式,Tomcat在运行时会监测包含<Context>元素的配置文件是否被更新,如果被更新,Tomcat会自动重新加载并启动Web应用,使对<Context>元素所在的修改内容生效。
下面先采用第四种方式配置<Context>元素。在helloapp目录下新建一个META-INF子目录,然后在其中创建一个context.xml文件,它的内容如下:

<Context path="/helloapp" docBase="helloapp" reloadable="true" />

以上<Context>元素的docBase属性表明,helloapp应用文件的文件路径为<CATALINA_HOME>/webapps/helloapp;path属性表明访问helloapp应用的URL入口为“/helloapp”。
下面再采用第三种方式配置<Context>元素,假定helloapp应用的文件路径为D:\TomcatAndJavaWeb\project\helloapp,并且在<CATALINA_HOME>/webapps目录下没有发布helloapp应用。在<CATALINA_HOME>/conf/Catalina/localhost目录下创建helloapp.xml文件,它的内容如下:

<Context path="/helloapp" 
docBase="D:\TomcatAndJavaWeb\project\helloapp" 
reloadable="true"/>

以上<Context>元素的docBase属性指定了helloapp应用的绝对路径,D:\TomcatAndJavaWeb\project\helloapp;path属性表明访问helloapp应用的入口为“/helloapp”。由于helloapp.xml文件位于Catalina/localhost子目录下,因此helloapp应用将运行在名为Catalina的Engine组件的localhost虚拟主机中。访问helloapp的login.html和hello.jsp的URL分别为:

http://localhost/helloapp/login.html
http://localhost/helloapp/hello.jsp

在server.xml文件中已经有一个名为localhost的<Host>元素,如果采用第五种方式配置<Context>元素,最常见的方法是在该<Host>元素中插入<Context>子元素,例如:

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
            ...
		<Context path="/helloapp" docBase="D:\TomcatAndJavaWeb\project\helloapp" reloadable="true"/>
      </Host>

提示:如果没有为Web应用配置Tomcat的Context元素,那么Tomcat会为Web应用提供一个默认的Context组件。例如前面介绍的按照默认方式发布helloapp应用时,Tomcat就给它提供了默认的Context组件。

配置Tomcat的虚拟主机

在Tomcat的配置文件servere.xml中,<Host>元素代表虚拟主机,在同一个<Engine>元素下可以配置多个虚拟主机。例如,有两个公司的Web应用都发布在同一个Tomcat服务器上,可以为两家公司分别创建一个虚拟主机,它们的虚拟主机名分别为:

www.helloapp.com
www.helloappex.com

尽管以上两个虚拟主机实际上对应同一个主机,但是当客户端通过以上两个不同的虚拟主机名访问Web应用时,客户端会感觉这两个不同应用分别拥有独立的主机。
此外,还可以为虚拟主机建立别名,例如,如果希望有客户端访问www.helloapp.com或www.helloappex.com都能对应同一个应用,那么可以把helloappex.com当作别名来处理。
下面介绍如何配置www.helloappex.com虚拟主机。

  1. 打开<CATALINA_HOME>/conf/server.xml文件,会发现在<Engine>元素中已经有一个名为localhoat的<Host>元素,可以在它后面(即<Host>标记后面)加入如下<Host>元素:
<Host name="www.helloappex.com" appBase="D:\Tomcat 10.0\webapps\helloapp" 
         unpackWARs="true" autoDeploy="true">
         
		<Alias>helloappex.com</Alias>
		<Alias>helloappex</Alias>
</Host>

<Host>元素的属性描述参见下表。<Host>元素还有一个子元素<Alias>,它用于指定虚拟主机的别名。<Host>元素允许包含多个<Alias>子元素,因此可以指定多个别名。

项目Value
name指定虚拟主机的名字
className指定实现虚拟主机的Java类名字,这个类必须实现org.apache.catalina.Host接口。该属性的值默认为org.apache.catalina.StandadHost
appBase指定虚拟主机的目录,可以指定绝对目录,也乐意指定相对<CATALINA_HOME>的相对目录。如果此项没有设定,默认值为<CATALINA_HOME>/webapps
autoDeploy如果此项设为true,表示Tomcat服务器处于运行状态时,能够监测appBase下的文件,如果有新的Web应用加入进来,则会自动发布这个Web应用
deployOnStartup如果此项设为true,则表示Tomcat启动时会自动发布appBase目录下所有的Web应用。如果Web应用没有相应的<Context>元素,那么Tomcat会提供一个默认的Context组件。deployOnStartup的默认值是true

一般情况下,<Host>元素都会使用默认的标准虚拟主机,即className属性使用默认值org.apache.catalina.StandardHost。标准虚拟主机除了具有上表列出的属性,还具有以下所示的属性。

项目Value
unpackWARS如果此项设为true,表示将把appBase属性指定的目录下的Web应用的WAR文件先展开为开放目录结构后再运行。如果设为false,则直接运行WAR文件
workDir指定虚拟主机的工作目录。Tomcat运行时会把与这个虚拟主机的所有Went应用相关的临时文件放在此目录下。它的默认值为<CATALINA_HOME>/work。如果<Host>元素下的一个<Context>元素也设置了workDir属性,那么<Context>元素的workDir属性会覆盖<Host>元素的workDir属性
deployXML如果设为false,那么Tomcat不会解析Web应用中的用于设置Context元素的META-INF/context.xml文件。处于安全原因,如果不希望Web应用中包含Tomcat配置元素,就可以把这个设为false,在这种情况下,应该在<CATALINA_HOME>/conf/[enginename]/[hostname]下设置context元素。该属性的默认值为true
  1. 把整个helloapp应用(helloapp.war文件或者是整个helloapp目录)复制到<Host>元素的appBase属性指定的目录D:\Tomcat 10.0\webapps\helloapp下。
  2. 为了使以上配置的虚拟主机生效,必须在DNS服务器中注册以上虚拟主机名称和别名,使它们和Tomcat服务器所在的主机IP地址进行映射。在章节的最后会介绍如何在Windows中配置DNS映射。必须在DNS服务器中注册以下虚拟主机名字和别名:
www.helloappex.com
helloappex.com
helloappex
  1. 重启Tomcat服务器,然后通过浏览器访问:
http://www.helloappex.com/

如果返回正常的页面就说明配置成功。还可以通过虚拟机的别名来访问helloapp应用:

http://helloappex.com
http://helloapp

每个虚拟主机都可以有一个默认的Web应用,它的默认根目录为ROOT。例如在<CATALINA_HOME>/webapps目录下有一个ROOT目录,它是localhoat虚拟主机的默认Web应用,访问http://localhost/index.jsp,就会显示这个Web应用的index.jsp页面。
对于www.helloappex.com虚拟主机也可以提供默认的Web应用。把D:\Tomcat 10.0\webapps\下的helloapp目录改名为ROOT目录,这个虚拟主机就有了一个默认的Web应用。访问http://www.helloappex.com/login.html,就会显示这个Web应用的login.html页面。
提示:如果要设置虚拟主机的默认Web应用的<Context>元素,那么它的path属性的值应该为一个空的字符串(即path=“”)。
以上步骤(3)提到的要惊醒虚拟主机名和IP地址之间的DNS映射。下面介绍在Windows系统中进行DNS映射的步骤。

  1. 在文件资源管理器中找到文件:C:\Windows\System32\drivers\etc\hosts。选中hosts文件,按下鼠标右键,在弹出的菜单中,选择【属性】->【安全】->【编辑】,在hosts文件的访问权限编辑窗口中,设置Windows用户具有“完全控制”权限,参见下图。
    在这里插入图片描述

  2. 用Windows记事本打开hosts文件,在文件中加入如下内容,使得"www.helloappex.com"等虚拟主机名和本地主机IP地址映射:

127.0.0.1 www.helloappex.com
127.0.0.1 helloappex.com
127.0.0.1 helloappex

:: www.helloappex.com
:: helloappex.com
:: helloappex

以上代码中“127.0.0.1”和“::1”分别是本地主机的IPV4格式和IPV6格式的IP地址。

创建、配置和使用自定义JSP标签

接下来创建一个名为hello的简单的自定义JSP标签,它的作用是输出字符串“Hello”。hello标签位于一个名为mytaglib的标签库(Tag Library)中。hello.jsp会使用mytaglib标签库中的hello标签。在此我们侧重介绍hello标签的发布和使用。
以下是创建、配置和使用hello标签步骤。

  1. 编写用于处理hello标签的类,名为HelloTag类,下面例程列出了HelloTag.java的源代码。
package helloapp;

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

public class HelloTag extends TagSupport{
	//当JSP解析器遇到hello标签的结束标志时,调用此方法
	public int doEndTag() throws JspException{
		try {
			//打印字符串“hello”
			pageContext.getOut().print("Hello");
		}catch(Exception e)
		{
			throw new JspTagException(e.getMessage());
		}
		return EVAL_PAGE;
	}
}

编译HelloTag.java时,需要将Servlet API的类库文件(servlet-api.jar)以及JSP API的类库文件(jsp-api.jar)添加到classpath中,这两个JAR文件位于<CATALINA_HOME>/lib,目录下。此外,在Oracle的官方地址(http://www.oracle.com/technetwork/java/index.html)也可以下载JSP API的类库文件。编译生成的HelloTag.class存放位置为WEB-INF/calsses/helloapp/HelloTag.class。
2. 创建一个TLD(Tag Library Descriptor,标签库描述符)文件。假定hello标签位于mytaglib标签库中,因此创建一个名为mytaglib.tld的TLD文件。在这个文件中定义mytaglib标签库和hello标签。这个文件的存放位置为WEB-INF/mytaglib.tld。下面例程列出了mytaglib.tld的源代码。

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

<!-- a tag library descriptor -->

<taglib>
	<tlib-version>1.1</tlib-version>
	<jsp-version>2.4</jsp-version>
	<short-name>mytaglib</short-name>
	<uri>mytaglib</uri>
	
	<tag>
		<name>hello</name>
		<tag-class>helloapp.HelloTag</tag-class>
		<body-content>empty</body-content>
		<description>Just Says Hello</description>
	</tag>
</taglib>

提示:Servlet规定,TLD文件在Web应用中必须存放在web-INF目录或这自定义的子目录下,但不能放在web-INF\classes目录和web-INF\lib目录下。web.xml文件中的<taglib>元素的<tag-location>子元素用来设置标签库描述文件的存放路径,应该保证<taglib-location>子元素的取值与TLD文件实际存放位置相符。

  1. 在web.xml文件中配置<taglib>元素,下面例程列出了修改后的web.xml文件。
<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  version="4.0" >

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

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

  <welcome-file-list>
    <welcome-file>login.html</welcome-file>
  </welcome-file-list>
  
  <jsp-config>
    <taglib>
      <taglib-uri>
        /mytaglib
      </taglib-uri>
      
      <taglib-location>
         /WEB-INF/mytaglib.tld
      </taglib-location>
    </taglib>
  </jsp-config>

</web-app>

<taglib>元素位于<jsp-config>元素中。<taglib>元素中包含两个子元素:<taglib-uri>和<taglib-location>。其中<taglib-uri>指定标签库的URI;<taglib-location>指定标签库的TLD文件的存放位置。

  1. 在hello.jsp文件中使用hello标签。首先,在hello.jsp中加入引用mytaglib标签库的taglib标签指令:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<%@ taglib uri="/mytaglib" prefix="mm" %>
<html>
<head>
  <title>helloapp</title>
</head>
<body>
  <b><mm:hello/> : <%= request.getAttribute("USER") %></b>
</body>
</html>	

hello.jsp修改后,再次访问login.html->DispatcherServlet->hello.jsp,最后生成的网页如下图所示。这个网页中的“Hello”字符串就是由<mm:hello/>标签输出来的。
在这里插入图片描述

当客户端请求访问hello.jsp时,Servlet容器会按照如下步骤处理hello.jsp中的<mm:hello/>标签。

  1. 由于<mm:hello/>的前缀为“mm”,与hello.jsp中的如下taglib指令匹配:
<%@ taglib uri="/mytaglib" prefix="mm" %>

由此得知hello标签来自URI为“/mytaglib”的标签库。
2. 在web.xml文件中对URI为“/mytaglib”的标签的配置如下:

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

由此可知URI为“/mytaglib”的标签库的TLD文件为WEB-INF/mytaglib.tld。
3. 在WEB-INF/mytaglib.tld文件中对名为hello的标签的定义如下:

  <tag>
    <name>hello</name>
    <tag-class>helloapp.HelloTag</tag-class>
    <body-content>empty</body-content>
    <description>Just Says Hello</description>
  </tag>

由此得知hello标签的处理类为helloapp.HelloTag类。因此Servlet容器运行hello.jsp时,如果遇到<mm:hello/>标签,就会加载WEB-INF/classes/helloapp目录下的HelloTag.class文件。遇到<mm:hello>标签的结束标志时,就会调用HelloTag类的doEndTag()方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值