Servlet&jsp基础:第一部分

时间:2021-12-05 08:36:20
Servlet&jsp基础:第一部分
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4288968.html

使用Servlet激活器... 5

缺省Servlet 6

Tomcat中的类装载器... 6

Servlet 8

Servlet路径映射... 8

ServletConfig接口... 9

获取ServletContext对象(getServletContext)... 9

获取Servlet的注册名(getServletName)... 9

获取Servlet初始化参数(getInitParameter)... 9

GenericServlet与HttpServlet类... 10

init方法... 10

service方法... 10

getServletInfo方法... 11

doXxx方法... 11

浏览器缓存与getLastModified方法... 13

ServletContext 16

获取web应用的初始化参数(getInitParameter)... 16

记录日志(log)... 17

访问资源文件(getResourcePaths)... 17

将虚拟路径转换成本地路径(getRealPath)... 18

Web应用程序之间的访问(getContext)... 19

其他方法(getMajorVersion、getMimeType、getServerInfo)... 19

使用Servlet激活器

配置一个Servlet时,一般要在自己项目中的web.xml配置<servlet>与<servlet-mapping>两个元素,但con/web.xml中为我们提供了一个名叫 invoker 的Servlet(5.5.30中已被注释掉,我们需要去掉),如下:

<servlet>

<servlet-name>invoker</servlet-name>

<servlet-class>

org.apache.catalina.servlets.InvokerServlet

</servlet-class>

<init-param>

<param-name>debug</param-name>

<param-value>0</param-value>

</init-param>

<load-on-startup>2</load-on-startup>

</servlet>

它可以根据URL中提供的Servlet类信息而自动激活这个Servlet,而不需要我们再在自己的应用中的web.xmlj里另外配置一<servlet>,只需要配置 <servlet-mapping>即可,配置如下:

<servlet-mapping>

<servlet-name>invoker</servlet-name>

<url-pattern>/servlet/*</url-pattern>

</servlet-mapping>

如现在有这样一个Servlet:mypak.HelloWorldServlet,则这样可以访问http://localhost:8080/myapp/servlet/mypak.HelloWorldServlet。注意,每个Web应用程序中为Servlet激活器所映射的访问路径可以各不相同,但必须以“/*”结尾(比如这里去掉前面的 /servlet 也可以)。经过这样的设置以后,即使某个Servlet程序没有在web.xml文件中进行注册,我们只需要将Servlet激活器所映射的访问路径中的通配符(*)替换为这个Servlet的完整类名,就可以通过Servlet激活器来调用这个Servlet程序。

缺省Servlet

如果某个Servlet的映射路径(<servlet-mapping>元素中的<url-pattern>元素的值)仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlete。凡是在web.xml文件中找不到匹配的<servlet-mapping>元索的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。在<tomcat的安装目录>\conf\web.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个Servlet设置为缺省Servlet。由于<tomcat的安装目录>\conf\web.xml文件的设置信息对该服务器上的所有Web应用程序都起作用,所以,服务器上的所有Web应用程序的缺省Setvlet都是org.apacbe.catalina.servlets.DefaultServlet。

<servlet>

<servlet-name>default</servlet-name>

<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>

</servlet>

当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Setvlet,而这个缺省Servlet的处理方式通常是把静态资源中的内容按字节原封不动地读出来,然后再按字节流原封不动传递给客户端,并且生成一些响应消息头字段,例如,根据静态资源的扩展名所映射的MIME类型生成Content-Type头字段,根据静态资源的大小生成Content-Length头字段。

如果将conf/web.xml 下的默认Servlet注释掉,则非Servlet与Jsp资源将不能访问:

<!--servlet-mapping>

<servlet-name>default</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping-->

注意,缺省Servlet在输出静态页面内容后,会将静态页面内容暂时在服务器上的缓存中保存5中,也就是在这5秒中内如果又来访问这个静态的页面(根据你请求的URL来分辨,如果与上次相同则认为是请求的同一资源),将会得到相同的静态页面内容,即使你立即修改了这个静态页面的内容。但这会有个问题:如果静态页面是在服务上动态生成的,虽然它是静态页面,但里面的内容却是动态的,这就会使浏览器在5秒内不会看到最新的内容。另外,当请求一个静态页面时,缺少Servlet会自动在响应头上加上 Last-Modified 头,如果静态页面不修改,缺少Servlet会回应 304 状态码,表示没有修改,浏览器可以从缓存中读取。

Tomcat中的类装载器

Servlet&jsp基础:第一部分

1、  Bootstrap类装载器负责加载Java核心名中的类<java_home>\jre\lib\rt.jar。

2、  ExtClassLoader负责加载存在<JAVA_HOME>/jre/lib/ext上当下的Java名中的类。

3、  AppClassLoader负责加载应用程序中的类,即CLASSPAH环境变量设置的目录中的类。但Tomcat的启动脚本catalina.bat 已将CLASSPATH环境变量原来的设置值完全清除,也就是说,Tomcat不会继承操作系统上原来设置好的CLASSPATH环境变量的内容,而是将CLASSPATH一半变量重新设置成了如下两个jar包:<CATALINA_HOME>/bin/bootstrap.jar、<JAVA_HOME>/lib/tools.jar。

4、  Common类装载器负责从<CATALINA_HOME>/common/classes中的.class 类文件和<CATALINA_HOME>/common/lib中的jar包加载类。Common类装载器加载的类对Tomcat服务器内核和每个Web应用程序都可见,例如,servlet.jar中包含的类既要被Tomcat服务器内核使用,又要被每个Web应用程序便用,所以,它需耍放置在CATALINA_HOME/common/lib中。

5、  Catalina类装载器负<CATALINA_HOME>/server/classes中的 .class 类文件和<CATALINA_HOME>/server/lib中的jar包加载类。Catalina类装载器加载的类只对Tomcat服务器内核可见,对每个Web应用程序完全不可见,所以,只想让Tomcat服务器内核使用而不想让Web应用程序便用的类应放置在Catalina类装载器的搜索目录中。对于运行Tomcat内核的线程,它的上下文类装载器就是Catalina类装载器。

6、  Shared类装载器负责从<CATALINA_HOME>/share/claases中的 .class 类文件和<CATALINA_HOME>/share/lib中的jar包加载类。Catalina类装载器加载的类只对所有的Web应用程序可见,对Tomcat服务器内核完全不可见。

7、  WebappX类装载器负责从当前Web应用程序的/WEB-INF/classes中的.class类文件/WEB-INF/lib中的Jar包加载类。WebappX类装载器加载的类只对当前Web应用程序可见,对其他wb应用程序不可见。对于运行每个节Web应用程序的线程,它们的上下文类装载器就是它们各自的WebappX类装载器。San公司在Servlet规范中建议WebappX类装载器不应按照标准委托模式来设计,而是应该由WebappX类装载器自己先加载某个类,只有自己加载不了时,才委托父级的类装载器进行加载,除了WebappX类装载器之外,Servlet引擎中的其他类装载器都遵循标准委托棋式。但是,许多Servlet引攀开发商认为这个建议没有什么意义,所以,他们并没有采纳Servlet规范的建议,而是仍然按照标准委托模式设计了他们的WebappX类装载器,例如,笔者通过实验发现Tomcat 4.中的WebappX类装载器采用的就是标准委托模式。

Servlet

Servlet&jsp基础:第一部分

Servlet路径映射

<servlet>

<servlet-name>test.Forward</servlet-name>

<servlet-class>test.Forward</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>test.Forward</servlet-name>

<url-pattern>/demo/hello.html</url-pattern>

</servlet-mapping>

上面Servlet的url-pattern配置的是 /demo/hello.html ,注意,demo 不是应用目录(虚拟目录或Web站点),现假设应用目录为myapp,则访问的路径为 http://localhost:8080/myapp/demo/hello.html。另外,如果在应用目录下真存在 /demo/hello.html这样一个静态页面,这里也不会去调用它,除非这里没有配这个Servlet。

Servlet映射到URL中也可以使用 * 通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”,在 * 前面不能有目录分隔符“/”,例如“*.do”表示匹配以“.do”结尾的所有URL;另一种是以“/”开头,并以“/*”结尾,例如“/*”表示匹配当前Web应用程序下的所有URL,“/action/*”表示匹配当前Web应用程序下的“/action”子路径下的所有URL。另外,在匹配时,如果与多个路径都匹配,则优先使用最精确的那个Servlet。

ServletConfig接口

Sevlet引擎将代表Servlet容器的对象和Servlet的配置参数信息(web.xml中所配置的)一并封装到一个称为ServletConfig的对象中,并在初始化Servlet实例对象时传递给该Servlet。Servlet引擎装载并创建一个Servlet的实例对象后,接着调用该实例对象的init(ServletConfig config)方法将ServletConfig对象传递给Servlet。

GenericServlet实现了ServletConfig接口。这里我们不必要这样 getServletConfig().getServletName()来获取Servlet的名称,而是直接通过 getServletName() 方法即可获取。

获取ServletContext对象(getServletContext)

getServletContext方法返回某个Web应用程序的ServletContext对象。

获取Servlet的注册名(getServletName)

getServletName方法用于返回Servlet在web.xml文件中的注册名称,如下面的的Servlet配置将返回“default”。对于没有在web.xml文件中注册的Servlet,将返回Servlet的类名。

获取Servlet初始化参数(getInitParameter)

在web.xml文件中可以为Servlet设置很多个初始化参数,getInitParameterNames()方法用于返回一个Enumeration的集合对象,该对象包含了在web.xml文件中为当前Servlet设置的所有初始化参数的名称。

<servlet>

<servlet-name>default</servlet-name>

<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>

<init-param>

<param-name>debug</param-name>

<param-value>0</param-value>

</init-param>

<init-param>

<param-name>listings</param-name>

<param-value>false</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

servletConfig.getInitParameter("debug") = "0"

GenericServlet与HttpServlet类

GenericServlet是一个实现了Servlet的基本我笑和功能的基类,其完整的名称为javax.servlet.GenericServlet。HttpServlet是GenericServlet的子类。其完整名称为javax.servlet.http.HttpServlet,它提供了处理HTTP协议的基本架构。如果一个Servlet要充分利用HTTP协议的功能,就应该继承HttpServlet。

init方法

在Servlet接口中定义了一个带参的init方法:

public void init(ServletConfig config) throws ServletException

在GenericServlet中还定义了一个无参数的init方法:

public void init() throws ServletException

GenericServlet类中带参数init与不带参数init的实现如下:

public void init() throws ServletException {}

public void init(ServletConfig config) throws ServletException {

this.config = config;

this.init();

}

所以我们在创建自己的Servlet时,一般只重写不带参数的init方法即可。如果重写带参数的init,你还得要在该方法的第一行调用一下super.init(config)语句,否则会有问题。

只有带参数的init方法才是Servlet接口定义的标准方法,也是Servler引擎才会调用的方法。

service方法

Servlet接口中定义的 service 方法如下:

public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;

GenericServlet类没有对这个方法进行实现,HttpServlet类实现了这个方法,实现如下:

public void service(ServletRequest req, ServletResponse res)

throws ServletException, IOException {

HttpServletRequest  request;

HttpServletResponse response;

try {

request = (HttpServletRequest) req;

response = (HttpServletResponse) res;

catch (ClassCastException e) {

throw new ServletException("non-HTTP request or response");

}

service(request, response);//调用了另外一重载形式的service方法

}

针对HttpServlet的实现,为了简化这一转换过程,HttpServlet类实现的service方法内部调用了另外一重载形式的service方法,重载的service方法的定义语法为:

protected void service(HttpServletRequest req, HttpServletResponse resp)        throws ServletException, IOException

注,Servlet引擎或容器只会去调用在Servlet接口中定义的 service方法,即service(ServletRequest req, ServletResponse res)方法,还不是在HttpServlet中重载的service(HttpServletRequest req, HttpServletResponse resp)方法。所以我们在创建自己的Servlet类时,我们可以去重写service(HttpServletRequest req, HttpServletResponse resp)方法,Servlet引擎会通过调用Servlet接口上中的那个service方法来调用我们重写过的service方法。

getServletInfo方法

返回Servlet的描述信息,GenericServlet实现返回的为空字符串,如有必要,可以重写这个方法,以便返回Servlet的作者、版本等信息

doXxx方法

不管客户端以哪种请求主方式访问Servlet,Servlet引擎都将调用Servlet接口中定义的那个service方法,它是所有请求方式的总入口;然后再调用HttpServlet中重载的那个service方法;最后由重载的那个service方法分派到不同的doXxx方法。HttpServlet重载的service方法实现如下:

protected void service(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {

String method = req.getMethod();

if (method.equals(METHOD_GET)) {

long lastModified = getLastModified(req);//默认为-1,即不支持Last-Modified头

if (lastModified == -1) {//不支持Last-Modified头

// servlet doesn't support if-modified-since, no reason

// to go through further expensive logic

doGet(req, resp);

else {//支持Last-Modified头

long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);

if (ifModifiedSince < (lastModified / 1000 * 1000)) {//如果已更新

// If the servlet mod time is later, call doGet()

// Round down to the nearest second for a proper compare

// A ifModifiedSince of -1 will always be less

//在响应头中设置文档最后更新时间头Last-Modified头

maybeSetLastModified(resp, lastModified);

doGet(req, resp);

else {

//如果没有更改,则返回304状态码,表示GET请求的资源可用并且没有修改,

//浏览器会从缓存中读取

resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);

}

}

else if (method.equals(METHOD_HEAD)) {

//doHead的目的就是看服务器是否已更新文档

long lastModified = getLastModified(req);

maybeSetLastModified(resp, lastModified);

doHead(req, resp);

else if (method.equals(METHOD_POST)) {

doPost(req, resp);

else if (method.equals(METHOD_PUT)) {

doPut(req, resp);

else if (method.equals(METHOD_DELETE)) {

doDelete(req, resp);

else if (method.equals(METHOD_OPTIONS)) {

doOptions(req,resp);

else if (method.equals(METHOD_TRACE)) {

doTrace(req,resp);

else {

String errMsg = lStrings.getString("http.method_not_implemented");

Object[] errArgs = new Object[1];

errArgs[0] = method;

errMsg = MessageFormat.format(errMsg, errArgs);

resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);

}

}

在一般情况下,Servlet只需处理GET和POST两种请求方式,也就是在继承HttpServlet时只需重写doGet和doPost这两个方法即可。

如果自己编写的Servlet程序要对客户端的所有请求方式进行相同的处理,可以重写HttpServlet的service方法。这时Servlet引擎将不会再调用父类HttpServlet中的service方法(除非在重写的service方法中使用super.service()进行显示的调用),所以,doXxx方法也将不再被调用。

另外,虽然HttpServlet中的doXxx方法都有默认实现,但doGet、doPost、doPut、doDelete方法的实现都是违例实现,即它们在没有重写的情况下,就表示没有实现,再直接调用时会返回给浏览器相应错误状态码与信息,以下是doGet、doPost、doPut、doDelete各自默认实现所抛出的错误信息:

http.method_get_not_supported=HTTP method GET is not supported by this URL

http.method_post_not_supported=HTTP method POST is not supported by this URL

http.method_put_not_supported=HTTP method PUT is not supported by this URL

http.method_delete_not_supported=Http method DELETE is not supported by this URL

当然,在重写doGet或doPost时,如果这两个方法实现都一样,我们也只需要实现其中一个,再实现另一个时去直接调用它即可。

浏览器缓存与getLastModified方法

Servlet&jsp基础:第一部分

getLastModified()方法返回Servlet当前输出的响应内容的修改时间,单位为毫秒数。它由HttpServlet类的service方法调用,HttpServlet类的service方法可以根据这个返回值决定是否在响应消息中自动生成Last-Modified头。默认返回-1,表示不生成,则浏览器将页面缓存在本地时不会有

注意,HttpServet中的doGet支持Last-Modified头缓存功能,但doPost不支持。如果我们要实现Last-Modified头与If-Modified-Since头的功能,对于doGet,我们只需要实现HttpServlet中的getLastModified方法;但如果是doPost,则还要自己让doPost支持缓存功能。

public class CacheServlet  extends HttpServlet{

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {

PrintWriter out = resp.getWriter();

out.println(new Date());

}

//     @Override

//     protected long getLastModified(HttpServletRequest req) {

//            //返回当前毫秒数,默认返回为-1

//            return System.currentTimeMillis();

//     }

}

http://localhost:8080/myapp/cacheservlet

Servlet&jsp基础:第一部分

去掉getLastModified的注释再访问

Servlet&jsp基础:第一部分

如果将getLastModified方法返回值改成很小的毫秒数,如1,则刷新页面时,页中的时间内容不会再变化。

如果getLastModified方法返回的不是-1,则响应头会有Last-Modified字段:

linux-7qez /home/fpcsmp> telnet 192.168.1.94 8080

Trying 192.168.1.94...

Connected to 192.168.1.94.

Escape character is '^]'.

GET /myapp/cacheservlet HTTP/1.1

Host:

HTTP/1.1 200 OK

Server: Apache-Coyote/1.1

Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT

Content-Length: 30

Date: Mon, 12 Jul 2010 08:34:52 GMT

Mon Jul 12 16:34:52 CST 2010

如果请求头的 if-modified-since 头字段值比服务器新或等时,返回304状态码:

linux-7qez /home/fpcsmp> telnet 192.168.1.94 8080

Trying 192.168.1.94...

Connected to 192.168.1.94.

Escape character is '^]'.

GET /myapp/cacheservlet HTTP/1.1

if-modified-since: Thu, 01 Jan 1970 00:00:00 GMT

Host:

HTTP/1.1 304 Not Modified

Server: Apache-Coyote/1.1

Date: Mon, 12 Jul 2010 08:38:12 GMT

ServletContext

Servlet引擎为每个Web应用程序都创建一个对应的ServletContext对象,ServletContext对像被包含在ServletConfig对象中。调用ServletConfig.getServletcontext法可以返回ServletContext对象的引用。在Servlet容器初始化Servlet对象时,ServletContext对象随着ServletConfig对象提供给Servlet。与Servlet API中的其他接口一样,ServletContext接口的实现类也是由Servlet引擎提供的。

获取web应用的初始化参数(getInitParameter)

如果要在server.xml文件中为某个web应用程序设置初始化参数,需要在该web应用所对应的<Context>元素增加<Parameter>子元素,更改conf\Catalina\localhost\myapp.xml 文件,添加 <Parameter>:

<Context path= "/myapp" docBase="Z:\eclipse_workspace\servlet_jsp_exercise\myapp" debug="0" reloadable="true">

<Parameter name="companyName1" value="XX" override="false"/>

<Parameter name="companyName2" value="YY" override="true"/>

</Context>

其中voerride属性用于指定在web应用程序的web.xml文件中设置的同名初始化参数是否覆盖这里的设置,当该属性值为false时表示不允许覆盖,默认值为true,即允许覆盖。

如果要在web应用程序中web.xml文件设置初始化参数,则需要在根元素的<Web-app>中增加<context-param>子元素,如下:

<context-param>

<param-name>companyName1</param-name>

<param-value>XXX</param-value>

</context-param>

<context-param>

<param-name>companyName2</param-name>

<param-value>YYY</param-value>

</context-param>

public class ContextParmServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

ServletContext servletContext = this.getServletContext();

System.out.println(servletContext.getInitParameterNames().nextElement());

//companyName1 = XX

System.out.println("companyName1 = " + servletContext.getInitParameter("companyName1"));

//companyName2 = YYY

System.out.println("companyName2 = " + servletContext.getInitParameter("companyName2"));

}

}

记录日志(log

ServletContext类中定义了以下两个记录日志方法:

log(java.lang.String msg)

log(java.lang.String message, java.lang.Throwable throwable)

GenericServlet类中也定义了两个log方法,不过它们是通过调用ServletContext相应的方法来实现的:

public void log(String msg) {

getServletContext().log(getServletName() + ": "+ msg);

}

public void log(String message, Throwable t) {

getServletContext().log(getServletName() + ": " + message, t);

}

log方法记录的日志文件名称和存储路径因Web服务器不同而不同。Tomcat中的日志文件的存储路径和名称是在servlet.xml文件中进行设置的。每个Web应用程序都可以设置其单独的日志文件。在server.xml文件中,使用<logger>元素来设置日志文件的相关信息,如下所示:

<Logger className="org.apache.catalina.logger.FileLogger" directory="logs" prefix="catalina_log." suffix=".txt" timestamp="true"/>

directory如果是相对路径,则是相对于 $CATALINA_HOME 环境变量所设置的目录,directory的默认值为相对目录“logs”。

以上是针对 Tomcat 4.X的配置,Tomcat 5.x不一样了。

<logger>元素可以嵌套在<Engine> <Host> <Context>元素之中,如果里层元素中没有单独的设置<logger>元素,它将继承外层元素中的日志设置。

访问资源文件(getResourcePaths

ServletContext接口中还定义了一些用于访问Web应用程序的内部资源文件的方法,比如WEB-INF目录中不能被外界访问的文件。

getResourcePaths(java.lang.String path):返回一个Set集合,包含某个资源目录中所有子目录和文件的路径名称,每个路径名名称相对于web应用程序的根目录(web应用目录或虚拟目录)的形式表示,如果是目录,则还以“/”结尾。如有某个应用目录下有以下这些资源:

/welcome.html

/catalog/index.html

/catalog/products.html

/catalog/offers/books.html

/catalog/offers/music.html

/customer/login.jsp

/WEB-INF/web.xml

/WEB-INF/classes/com.acme.OrderServlet.class,

getResourcePaths("/") returns {"/welcome.html", "/catalog/", "/customer/", "/WEB-INF/"}

getResourcePaths("/catalog/") returns {"/catalog/index.html", "/catalog/products.html", "/catalog/offers/"}

注,参数path一定要以“/”开头,且是相对于应用目录。

java.net.URL getResource(java.lang.String path):返回指定的某个资源的URL对象,path参数也必须以“/”开头,并且也是相对于应用根目录的。

java.io.InputStream getResourceAsStream(java.lang.String path):返回连接到某个资源上的InputStream对象,实际上是打开了getResource方法返回的URL对象上的输入流,它的参数传递规则与getResource方法完全一样。

一个Web应用程序在本地文件系统中的安装位里是可以变化的,不同的公司或个人可能将它安装在不同的位置上,所以ServIet程序不应使用绝对路径的形式来访问Web应用程序中的某个文件。

在某个java类中使用的相对路径是相对于当前的工作目录(比如在eclipse里运行一个Java类时,目录为项目所在的根目录;如果Tomcat是通过startup.bat启动的,那就是相对于startup.bat所在的目录而言的)而言的,这个工作目录通常是执行java命令的目录,而不是当前正在执行的java类所在的目录,这一特性导致在Java程序中很难直接使用相对目录。

针对在Java程序中使用FileInputStream类和相对路径访问资源文件时会出现的问题,JDK中的ClassLoader类专门提供了getResource等方法去装载资源文件,它们使用与查找Java类文件同样的方式去查找资源文件,即在类装载器所搜索的目录中查找。为了简化程序的编写,Class类中也定义了几个访问资源文件的方法,这些方法内部调用了ClassLoader类中的同名方法去完成相应的功能。为了防止外部使用浏览器访问到资源文件,Web应用程序中的资源文件通常应放置在WEB-INF目录或其子目录中。由于Web应用程序的类装载器会搜索WEB-INF/classes目录,所以,ClassLoader.getResourceAsStream方法也可以访问该目录中的资源文件,但是,ClassLoader.getResourceAsStream方法不能访问Web应用程序内的其他目录(比如应用目录或虚拟目录下的文件,或者是WEB-INF中的资源,而不是WEB-INF/classes或WEB-INF/lib下的资源)中的资源。ServletContext类中的访问资源的方法是通过Servlet容器来获得资源文件的,它使得Servlet程序可以访问Web应用程序内部的任意位置的文件。

将虚拟路径转换成本地路径(getRealPath

ServletContext接口的getRealPath方法,用于返回某个虚拟路径所映射的本地文件系统路径。不管传递的路径参数是否以“/”开头,它们都应该是相对于应用目录的,因为在转换后它们前面都会带上应用目录。具体的细微差别请看:

// Z:\eclipse_workspace\servlet_jsp_exercise\myapp

System.out.println(servletContext.getRealPath(""));

// Z:\eclipse_workspace\servlet_jsp_exercise\myapp\

System.out.println(servletContext.getRealPath("/"));

// Z:\eclipse_workspace\servlet_jsp_exercise\myapp\WEB-INF

System.out.println(servletContext.getRealPath("WEB-INF"));

// Z:\eclipse_workspace\servlet_jsp_exercise\myapp\WEB-INF

System.out.println(servletContext.getRealPath("/WEB-INF"));

Web应用程序之间的访问(getContext)

ServletContext.getRealPath只能获取当前应用下的资源路径,因为传给它的路径永远是相对于当前应用目录的。如果要获取其他Web应用的资源,怎么办?那就得先要获取到其他Web应用的ServletContext对象。

ServletContext接口中定义了一个getContext方法来获得某个URL所对应的ServletContext对象,传递给geContext方法的路径字符串必须以“/”作为起始字符,这个“/”代表的是整个Web服务器的根目录,而不是某个Web应用程序的根目录,这说明在一个Web应用程序中可以获得代表其他Web应用程序的ServletContext对象,如果要让某个Web应用程序内部能够使用ServletContext.getContext方法返回代表其他Web应用程序的ServletContext对象,必须将conf/server.xml文件中与该Web应用程序对应的<Context>元素的crossContext属性设置为true,只有为true时,它所对应的Web应用程序内部才能使用ServletContext.getContext方法返回代表其他Web应用程序的ServletContext对象。例如,如果想/myapp这个Web应用程序能够访问其他的Web应用程序,应将对应的<Context>元素的起始标签修改成如下形式:

<Context path= "/myapp" docBase="Z:\eclipse_workspace\servlet_jsp_exercise\myapp" crossContext="true" debug="0" reloadable="true">

//获取同一主机中Web应用目录为 / 的Web应用

ServletContext sc = servletContext.getContext("/");

// Z:\tomcat-5.5.30\webapps\ROOT

System.out.println(sc.getRealPath(""));

//获取同一主机中Web应用目录为 /myapp2 的Web应用

sc = servletContext.getContext("/myapp2");

// Z:\eclipse_workspace\myapp2\myapp2

System.out.println(sc.getRealPath(""));

crossContext的默认值为false。

其他方法(getMajorVersion、getMimeType、getServerInfo)

//获取Servlet容器所支持的Servlet API的主次版本号

System.out.println(servletContext.getMajorVersion() + "."

+ servletContext.getMinorVersion());//2.4

//获取某个文件的MIME类型,这个类型是在conf/web.xml中配置的

System.out.println(servletContext.getMimeType("xx.html"));//text/html

//获取容器的名称和版本信息

System.out.println(servletContext.getServerInfo());//Apache Tomcat/5.5.30